Token导航 LogoToken导航TokenDH.com
开发只读github未标认证来源可访问许可证需确认审计通过

frappe-syntax-controllersFrappe 语法控制器

Agent Skill

frappe-syntax-controllers 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

564

周安装

24

GitHub Stars

87

下载量

198
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:frappe-syntax-controllers(Frappe 语法控制器)
来源仓库:https://github.com/openaec-foundation/erpnext_anthropic_claude_development_skill_package
仓库路径:skills/frappe-syntax-controllers
安装命令:
npx skills add https://github.com/openaec-foundation/erpnext_anthropic_claude_development_skill_package --skill frappe-syntax-controllers
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/openaec-foundation/erpnext_anthropic_claude_development_skill_package --skill frappe-syntax-controllers

简介

frappe-syntax-controllers 用于处理 GitHub 仓库、Issue、Pull Request 等协作信息,适合整理代码变更与项目状态。

  • 适用于围绕仓库状态、代码协作事项进行信息梳理的场景。
  • 通过 npx skills add 命令从指定 GitHub 仓库安装并使用。
  • 安装前需确认权限范围和维护状态,注意可能触发联网或命令执行操作。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

Frappe Syntax: Document Controllers

Document Controllers are Python classes that define all server-side logic for a DocType. EVERY DocType has exactly one controller file. The controller class extends frappe.model.document.Document.

Quick Reference

import frappe
from frappe import _
from frappe.model.document import Document

class SalesOrder(Document):
    def autoname(self):
        """Custom naming logic. Sets self.name."""
        self.name = f"SO-{self.customer_code}-{frappe.utils.now_datetime().year}"

    def validate(self):
        """MAIN validation — runs on EVERY save (insert and update).
        Changes to self ARE saved to database."""
        if not self.items:
            frappe.throw(_("Items are required"))
        self.total = sum(item.amount for item in self.items)

    def on_update(self):
        """After save — changes to self are NOT saved.
        Use frappe.db.set_value() for post-save field changes."""
        self.notify_linked_docs()

    def on_submit(self):
        """After submit (docstatus 0 -> 1). Create ledger entries here."""
        self.create_gl_entries()

    def on_cancel(self):
        """After cancel (docstatus 1 -> 2). Reverse ledger entries here."""
        self.reverse_gl_entries()

    @frappe.whitelist()
    def recalculate(self):
        """Exposed to client JS via frm.call('recalculate')."""
        self.total = sum(item.amount for item in self.items)
        return {"total": self.total}

File Location and Naming

DocType NameClass NameFile Path
Sales OrderSalesOrderselling/doctype/sales_order/sales_order.py
My Custom DocMyCustomDocmodule/doctype/my_custom_doc/my_custom_doc.py

Rule: DocType name -> PascalCase class -> snake_case filename. ALWAYS match exactly.


Lifecycle Hook Execution Order

INSERT (new document)

before_insert -> before_naming -> autoname -> before_validate -> validate
-> before_save -> [db_insert] -> after_insert -> on_update -> on_change

SAVE (existing document)

before_validate -> validate -> before_save -> [db_update]
-> on_update -> on_change

SUBMIT (docstatus 0 -> 1)

before_validate -> validate -> before_submit -> [db_update]
-> on_submit -> on_update -> on_change

CANCEL (docstatus 1 -> 2)

before_cancel -> [db_update] -> on_cancel -> on_change

UPDATE AFTER SUBMIT

before_update_after_submit -> [db_update]
-> on_update_after_submit -> on_change

DELETE

on_trash -> [db_delete] -> after_delete

DISCARD [v15+]

before_discard -> [db_set docstatus=2] -> on_discard

Complete hook reference with parameters: See lifecycle-methods.md


Hook Selection Decision Tree

What do you need to do?
|
+-- Validate data or calculate fields?
|   +-- validate (changes to self ARE saved)
|
+-- Action AFTER save (emails, sync, linked docs)?
|   +-- on_update (changes to self are NOT saved)
|
+-- Only for NEW documents?
|   +-- after_insert (runs once on first save only)
|
+-- Custom document name?
|   +-- autoname (set self.name)
|
+-- Before/after SUBMIT?
|   +-- Validate before submit? -> before_submit
|   +-- Create entries after submit? -> on_submit
|
+-- Before/after CANCEL?
|   +-- Check linked docs? -> before_cancel
|   +-- Reverse entries? -> on_cancel
|
+-- Cleanup before delete?
|   +-- on_trash
|
+-- React to ANY value change (including db_set)?
|   +-- on_change (MUST be idempotent)

Critical Rules

1. Changes after on_update are NOT saved

# WRONG - change is lost after on_update
def on_update(self):
    self.status = "Completed"  # NOT saved to database

# CORRECT - use db_set or frappe.db.set_value
def on_update(self):
    self.db_set("status", "Completed")

2. NEVER call frappe.db.commit() in controllers

# WRONG - breaks Frappe transaction management
def validate(self):
    frappe.db.commit()  # Can cause partial updates on error

# CORRECT - Frappe commits automatically at end of request
def validate(self):
    self.update_related()  # No commit needed

3. ALWAYS call super() when overriding

# WRONG - parent validation is skipped entirely
def validate(self):
    self.custom_check()

# CORRECT - parent logic preserved
def validate(self):
    super().validate()
    self.custom_check()

4. Use flags for recursion prevention

def on_update(self):
    if self.flags.get("from_linked_doc"):
        return
    linked = frappe.get_doc("Linked Doc", self.linked_doc)
    linked.flags.from_linked_doc = True
    linked.save()

5. NEVER put validation logic in on_update

# WRONG - document is already saved when this throws
def on_update(self):
    if self.total < 0:
        frappe.throw("Invalid total")  # Too late!

# CORRECT - validate BEFORE save
def validate(self):
    if self.total < 0:
        frappe.throw("Invalid total")  # Blocks save

Document Naming (autoname)

MethodExampleResultVersion
field:fieldnamefield:customer_nameABC CompanyAll
naming_series:naming_series:SO-2024-00001All
ExpressionPRE-.#####PRE-00001All
Old-style formatINV-{YYYY}-{####}INV-2024-0001Deprecated v16
hash / randomhasha1b2c3d4e5All
PromptPromptUser enters nameAll
autoincrementautoincrement1, 2, 3All
UUIDUUID550e8400-e29b-...v16+
Custom methodautoname() in controllerAny patternAll

Custom autoname Method

from frappe.model.naming import getseries

class Project(Document):
    def autoname(self):
        prefix = f"P-{self.customer[:3].upper()}-"
        self.name = getseries(prefix, 3)
        # Result: P-ACM-001, P-ACM-002, etc.

UUID Naming [v16+]

Set autoname = "UUID" in DocType definition. Frappe generates UUID v4.

When to use UUID:              When to use traditional naming:
- Cross-system sync            - User-facing references (SO-00001)
- Bulk record creation         - Sequential numbering required
- Global uniqueness needed     - Auditing requires readable names

Controller Extension Mechanisms

1. override_doctype_class (full replacement) [All versions]

# hooks.py
override_doctype_class = {
    "Sales Order": "custom_app.overrides.CustomSalesOrder"
}

# custom_app/overrides.py
from erpnext.selling.doctype.sales_order.sales_order import SalesOrder

class CustomSalesOrder(SalesOrder):
    def validate(self):
        super().validate()  # ALWAYS call super()
        self.custom_validation()

WARNING: Only ONE app can override a DocType class. Multiple overrides conflict.

2. extend_doctype_class (mixin, non-destructive) [v16+]

# hooks.py
extend_doctype_class = {
    "Address": ["custom_app.extensions.address.GeocodingMixin"],
    "Contact": [
        "custom_app.extensions.common.ValidationMixin",
        "custom_app.extensions.contact.PhoneMixin"
    ]
}

# custom_app/extensions/address.py
from frappe.model.document import Document

class GeocodingMixin(Document):
    @property
    def full_address(self):
        return f"{self.address_line1}, {self.city}, {self.country}"

    def validate(self):
        super().validate()
        self.geocode_address()

ALWAYS prefer extend_doctype_class over override_doctype_class in v16+. Multiple apps can safely extend the same DocType.

3. doc_events (hook individual events) [All versions]

# hooks.py
doc_events = {
    "Sales Order": {
        "validate": "custom_app.events.validate_sales_order",
        "on_submit": "custom_app.events.on_submit_sales_order"
    },
    "*": {  # ALL DocTypes
        "after_insert": "custom_app.events.log_creation"
    }
}

# custom_app/events.py
def validate_sales_order(doc, method=None):
    if doc.total > 100000:
        doc.requires_approval = 1

When to Use Which

Need full class replacement?     -> override_doctype_class [all versions]
Need to add methods/properties?  -> extend_doctype_class [v16+]
Need to hook one or two events?  -> doc_events [all versions]
Need to extend in v14/v15?       -> override_doctype_class or doc_events

Whitelisted Methods

Expose controller methods to client-side JavaScript with @frappe.whitelist():

class SalesOrder(Document):
    @frappe.whitelist()
    def send_email(self, recipient):
        """Callable from JS: frm.call('send_email', {recipient: '...'})"""
        frappe.sendmail(recipients=[recipient], message="Order confirmed")
        return {"status": "sent"}
// Client-side call
frm.call('send_email', { recipient: 'customer@example.com' })
    .then(r => frappe.msgprint(r.message.status));

Rules:

  • ALWAYS add @frappe.whitelist() decorator — without it, the method is NOT callable from client
  • The method MUST be defined on the controller class (not standalone)
  • Permission checks happen automatically (user must have read access to the document)

Submittable Documents

Documents with is_submittable = 1 follow the docstatus lifecycle:

docstatusStateEditableTransitions
0DraftYes-> 1 (Submit)
1SubmittedOnly "Allow on Submit" fields-> 2 (Cancel)
2CancelledNoNone (amend creates new Draft)

ALWAYS implement both on_submit and on_cancel as a pair. ALWAYS reverse in on_cancel what on_submit created.


Inheritance Patterns

# Standard controller
from frappe.model.document import Document
class MyDoc(Document): pass

# Tree DocType (hierarchical)
from frappe.utils.nestedset import NestedSet
class Department(NestedSet):
    nsm_parent_field = "parent_department"

# Virtual DocType (no database table)
class ExternalData(Document):
    def load_from_db(self): ...
    def db_insert(self, *args, **kwargs): ...
    def db_update(self, *args, **kwargs): ...
    @staticmethod
    def get_list(args): ...
    @staticmethod
    def get_count(args): ...

Type Annotations [v15+]

class Person(Document):
    if TYPE_CHECKING:
        from frappe.types import DF
        first_name: DF.Data
        last_name: DF.Data
        birth_date: DF.Date
        company: DF.Link

Enable auto-generation in hooks.py: export_python_type_annotations = True


Version Differences

Featurev14v15v16
Type annotationsNoAuto-generatedYes
before_discard / on_discardNoYesYes
flags.notify_updateNoYesYes
extend_doctype_classNoNoYes
UUID autonameNoNoYes
Old-style format namingYesYesDeprecated

Reference Files

FileContents
lifecycle-methods.mdAll hooks with execution order diagrams
document-api-complete.mdComplete Document API: all methods by category (CRUD, fields, DB, permissions, flags, child tables, naming)
methods.mdDocument class method signatures
events.mdAll document events in order
examples.mdComplete working controller examples
anti-patterns.mdCommon mistakes and corrections
flags.mdFlags system (doc.flags, frappe.flags)
hooks.mdController interaction with hooks.py
patterns.mdCommon controller patterns
syntax.mdController class syntax reference

Related Skills

  • frappe-syntax-serverscripts -- Server Scripts (sandbox alternative)
  • frappe-syntax-hooks -- hooks.py configuration
  • frappe-impl-controllers -- Implementation workflows
  • frappe-core-permissions -- Permission system

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

36.45%
按下载量换算72

Claude

28%
按下载量换算55

Cursor

17.11%
按下载量换算34

Gemini CLI

8.55%
按下载量换算17

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

只读

该 Skill 主要提供规则、说明或参考内容,本身偏只读;真正读写文件、联网或执行命令仍取决于宿主 Agent 的任务。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills