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

erpnext-errors-controllerserpnext 错误控制器

Agent Skill

erpnext-errors-controllers 用于记录任务执行中的错误、用户纠正、经验和能力缺口,适合在 Codex、Claude、Cursor、Gemini CLI 中希望让 Agent 持续沉淀问题、修正和最佳实践时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

1,063

周安装

43

GitHub Stars

87

下载量

334
CodexClaudeCursorGemini CLI

安装说明

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

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

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

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

命令行安装

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

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

简介

提供 DocType 控制器的错误处理模式,支持 try/except 和自定义异常机制。

  • 适合在 Codex、Claude、Cursor、Gemini CLI 中实现多文档事务和复杂业务校验。
  • 可参考原始文档了解 frappe.throw() 与 raise 语句的适用区别及版本兼容性。
  • 安装前应核实目标 ERPNext 版本,避免引入不兼容的异常类型或钩子逻辑。
  • erpnext-errors-controllers 属于开发类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

ERPNext Controllers - Error Handling

This skill covers error handling patterns for Document Controllers. For syntax, see erpnext-syntax-controllers. For implementation workflows, see erpnext-impl-controllers.

Version: v14/v15/v16 compatible


Controllers vs Server Scripts: Error Handling

┌─────────────────────────────────────────────────────────────────────┐
│ CONTROLLERS HAVE FULL PYTHON POWER                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│ ✅ try/except blocks - Full exception handling                      │
│ ✅ raise statements - Custom exceptions                             │
│ ✅ Multiple except clauses - Handle specific errors                 │
│ ✅ finally blocks - Cleanup operations                              │
│ ✅ frappe.throw() - Stop with user message                          │
│ ✅ frappe.log_error() - Silent error logging                        │
│                                                                     │
│ ⚠️ Transaction behavior varies by hook:                            │
│    • validate: throw rolls back entire save                         │
│    • on_update: document already saved!                             │
│    • on_submit: partial rollback possible                           │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Main Decision: Error Handling by Hook

┌─────────────────────────────────────────────────────────────────────────┐
│ WHICH LIFECYCLE HOOK ARE YOU IN?                                        │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│ ► validate / before_save                                                │
│   └─► frappe.throw() → Rolls back, document NOT saved                   │
│   └─► try/except → Catch and re-throw or handle gracefully              │
│                                                                         │
│ ► on_update / after_insert                                              │
│   └─► Document already saved! frappe.throw() shows error but saved      │
│   └─► Use try/except + log_error for non-critical operations            │
│   └─► Critical failures: frappe.throw() (shows error, doc is saved)     │
│                                                                         │
│ ► before_submit                                                         │
│   └─► frappe.throw() → Prevents submit, stays draft                     │
│   └─► Last chance for validation before docstatus=1                     │
│                                                                         │
│ ► on_submit                                                             │
│   └─► Document is submitted! throw shows error but docstatus=1          │
│   └─► Critical: throw causes partial state (submitted but failed)       │
│   └─► Better: validate everything in before_submit                      │
│                                                                         │
│ ► on_cancel                                                             │
│   └─► Reverse operations - use try/except for each reversal             │
│   └─► Log errors but try to continue cleanup                            │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Error Methods Reference

Quick Reference

MethodStops Execution?Rolls Back?User Sees?Use For
frappe.throw()✅ YESDepends on hookDialogValidation errors
raise Exception✅ YESDepends on hookError pageInternal errors
frappe.msgprint()❌ NO❌ NODialogWarnings
frappe.log_error()❌ NO❌ NOError LogDebug/audit

Transaction Rollback by Hook

Hookfrappe.throw() Effect
validate✅ Full rollback - document NOT saved
before_save✅ Full rollback - document NOT saved
on_update⚠️ Document IS saved, error shown
after_insert⚠️ Document IS saved, error shown
before_submit✅ Full rollback - stays Draft
on_submit⚠️ docstatus=1, error shown
before_cancel✅ Full rollback - stays Submitted
on_cancel⚠️ docstatus=2, error shown

Error Handling Patterns

Pattern 1: Validation with Error Collection

def validate(self):
    """Collect all errors before throwing."""
    errors = []

    # Required fields
    if not self.customer:
        errors.append(_("Customer is required"))

    if not self.items:
        errors.append(_("At least one item is required"))

    # Business rules
    if self.discount_percent > 50:
        errors.append(_("Discount cannot exceed 50%"))

    # Child table validation
    for idx, item in enumerate(self.items, 1):
        if not item.item_code:
            errors.append(_("Row {0}: Item Code is required").format(idx))
        if (item.qty or 0) <= 0:
            errors.append(_("Row {0}: Quantity must be positive").format(idx))

    # Throw all errors at once
    if errors:
        frappe.throw("<br>".join(errors), title=_("Validation Error"))

Pattern 2: External API Call with Fallback

def validate(self):
    """Call external API with error handling."""
    if self.requires_credit_check:
        try:
            result = self.check_credit_external()
            self.credit_score = result.get("score", 0)
        except requests.Timeout:
            # Timeout - use cached value
            frappe.msgprint(
                _("Credit check timed out. Using cached value."),
                indicator="orange"
            )
            self.credit_score = self.get_cached_credit_score()
        except requests.RequestException as e:
            # API error - log and continue with warning
            frappe.log_error(
                f"Credit check failed: {str(e)}",
                "External API Error"
            )
            frappe.msgprint(
                _("Credit check unavailable. Please verify manually."),
                indicator="orange"
            )
            self.credit_check_pending = 1
        except Exception as e:
            # Unexpected error - log and re-raise
            frappe.log_error(frappe.get_traceback(), "Credit Check Error")
            frappe.throw(_("Credit check failed. Please try again."))

Pattern 3: Safe Post-Save Operations

def on_update(self):
    """Handle post-save operations safely."""
    # Critical operation - throw on failure
    self.update_linked_documents()

    # Non-critical operations - log errors, don't throw
    try:
        self.send_notification()
    except Exception:
        frappe.log_error(
            frappe.get_traceback(),
            f"Notification failed for {self.name}"
        )

    try:
        self.sync_to_external_system()
    except Exception:
        frappe.log_error(
            frappe.get_traceback(),
            f"External sync failed for {self.name}"
        )
        # Queue for retry
        frappe.enqueue(
            "myapp.tasks.retry_sync",
            doctype=self.doctype,
            name=self.name,
            queue="short"
        )

Pattern 4: Submittable Document Error Handling

def before_submit(self):
    """All validations that must pass before submit."""
    # Validate everything here - last chance to abort cleanly
    if not self.items:
        frappe.throw(_("Cannot submit without items"))

    if self.grand_total <= 0:
        frappe.throw(_("Total must be greater than zero"))

    # Check stock availability
    for item in self.items:
        available = get_stock_balance(item.item_code, item.warehouse)
        if available < item.qty:
            frappe.throw(
                _("Row {0}: Insufficient stock for {1}. Available: {2}").format(
                    item.idx, item.item_code, available
                )
            )

def on_submit(self):
    """Post-submit actions - document is already submitted!"""
    # These operations should not fail if before_submit passed
    try:
        self.create_stock_ledger_entries()
    except Exception as e:
        # CRITICAL: Document is submitted but entries failed!
        frappe.log_error(frappe.get_traceback(), "Stock Ledger Error")
        frappe.throw(
            _("Stock entries failed. Please cancel and retry. Error: {0}").format(str(e))
        )

    try:
        self.create_gl_entries()
    except Exception as e:
        # Rollback stock entries if GL fails
        self.reverse_stock_ledger_entries()
        frappe.log_error(frappe.get_traceback(), "GL Entry Error")
        frappe.throw(_("Accounting entries failed. Stock entries reversed."))

Pattern 5: Cancel with Cleanup

def before_cancel(self):
    """Validate cancel is allowed."""
    # Check for linked documents
    linked_invoices = frappe.get_all(
        "Sales Invoice Item",
        filters={"sales_order": self.name, "docstatus": 1},
        pluck="parent"
    )

    if linked_invoices:
        frappe.throw(
            _("Cannot cancel. Linked invoices exist: {0}").format(
                ", ".join(linked_invoices)
            )
        )

def on_cancel(self):
    """Reverse operations - try to complete all cleanup."""
    errors = []

    # Reverse stock
    try:
        self.reverse_stock_ledger_entries()
    except Exception as e:
        errors.append(f"Stock reversal: {str(e)}")
        frappe.log_error(frappe.get_traceback(), "Stock Reversal Error")

    # Reverse GL
    try:
        self.reverse_gl_entries()
    except Exception as e:
        errors.append(f"GL reversal: {str(e)}")
        frappe.log_error(frappe.get_traceback(), "GL Reversal Error")

    # Update linked docs
    try:
        self.update_linked_on_cancel()
    except Exception as e:
        errors.append(f"Linked docs: {str(e)}")
        frappe.log_error(frappe.get_traceback(), "Linked Doc Update Error")

    # Report any errors but don't prevent cancel
    if errors:
        frappe.msgprint(
            _("Cancel completed with errors:<br>{0}").format("<br>".join(errors)),
            title=_("Warning"),
            indicator="orange"
        )

Pattern 6: Database Operation Error Handling

def validate(self):
    """Handle database errors gracefully."""
    try:
        # Check for duplicates
        existing = frappe.db.exists(
            "Customer Contract",
            {"customer": self.customer, "status": "Active", "name": ["!=", self.name]}
        )
        if existing:
            frappe.throw(_("Active contract already exists for this customer"))

    except frappe.db.InternalError as e:
        # Database error - log and show user-friendly message
        frappe.log_error(frappe.get_traceback(), "Database Error")
        frappe.throw(_("Database error. Please try again or contact support."))
See: references/patterns.md for more error handling patterns.

Transaction Management

Understanding Transactions

# Frappe wraps each request in a transaction
# - On success: auto-commit
# - On exception: auto-rollback

def validate(self):
    # All these changes are in ONE transaction
    self.calculate_totals()
    frappe.db.set_value("Counter", "main", "count", 100)

    if error_condition:
        frappe.throw("Error")  # EVERYTHING rolls back

def on_update(self):
    # Document save is already committed!
    # New changes here are in a NEW transaction
    frappe.db.set_value("Other", "doc", "field", "value")

    if error_condition:
        frappe.throw("Error")  # Only on_update changes roll back
        # The document itself is already saved!

Manual Savepoints (Advanced)

def on_submit(self):
    """Use savepoints for partial rollback."""
    # Create savepoint before risky operation
    frappe.db.savepoint("before_stock")

    try:
        self.create_stock_entries()
    except Exception:
        # Rollback only stock entries
        frappe.db.rollback(save_point="before_stock")
        frappe.log_error(frappe.get_traceback(), "Stock Entry Error")
        frappe.throw(_("Stock entries failed"))

    frappe.db.savepoint("before_gl")

    try:
        self.create_gl_entries()
    except Exception:
        frappe.db.rollback(save_point="before_gl")
        frappe.log_error(frappe.get_traceback(), "GL Entry Error")
        frappe.throw(_("GL entries failed"))

Critical Rules

✅ ALWAYS

  1. Collect multiple validation errors - Better UX than one at a time
  2. Use try/except around external calls - APIs, file I/O, network
  3. Log unexpected errors - frappe.log_error(frappe.get_traceback())
  4. Call super() in overridden methods - Preserve parent behavior
  5. Validate in before_submit - Last clean abort point for submittables
  6. Use _() for error messages - Enable translation

❌ NEVER

  1. Don't call frappe.db.commit() - Framework handles transactions
  2. Don't swallow errors silently - Always log unexpected exceptions
  3. Don't assume on_update can rollback doc - It's already saved
  4. Don't put critical logic in on_submit - Validate in before_submit
  5. Don't ignore return values - Check for None/empty results

Quick Reference: Exception Handling

# Catch specific exceptions first, general last
try:
    result = risky_operation()
except frappe.ValidationError:
    # Re-raise validation errors
    raise
except frappe.DoesNotExistError:
    # Handle missing document
    frappe.throw(_("Referenced document not found"))
except requests.Timeout:
    # Handle timeout specifically
    frappe.msgprint(_("Operation timed out"), indicator="orange")
except Exception as e:
    # Log and handle unexpected errors
    frappe.log_error(frappe.get_traceback(), "Unexpected Error")
    frappe.throw(_("An error occurred: {0}").format(str(e)))

Reference Files

FileContents
references/patterns.mdComplete error handling patterns
references/examples.mdFull working examples
references/anti-patterns.mdCommon mistakes to avoid

See Also

  • erpnext-syntax-controllers - Controller syntax
  • erpnext-impl-controllers - Implementation workflows
  • erpnext-errors-serverscripts - Server Script error handling (sandbox)
  • erpnext-errors-hooks - Hook error handling

适合场景

01

用户想查找某类 Agent Skill 时

02

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

03

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

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

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

能力 4

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

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

平台分布

Codex

33.61%
按下载量换算112

Claude

31.17%
按下载量换算104

Cursor

18%
按下载量换算60

Gemini CLI

8.77%
按下载量换算29

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

external-service

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

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

来源信息

继续浏览同类 Skills