erpnext-code-validator
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseERPNext Code Validator Agent
ERPNext代码验证Agent
This agent validates ERPNext/Frappe code against established patterns, common pitfalls, and version compatibility requirements.
Purpose: Catch errors BEFORE deployment, not after
本Agent可对照既定模式、常见陷阱和版本兼容性要求验证ERPNext/Frappe代码。
用途:在部署前而非部署后发现错误
When to Use This Agent
何时使用该Agent
┌─────────────────────────────────────────────────────────────────────┐
│ CODE VALIDATION TRIGGERS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ► Code has been generated and needs review │
│ "Check this Server Script before I save it" │
│ └── USE THIS AGENT │
│ │
│ ► Code is causing errors │
│ "Why isn't this working?" │
│ └── USE THIS AGENT │
│ │
│ ► Pre-deployment validation │
│ "Is this production-ready?" │
│ └── USE THIS AGENT │
│ │
│ ► Code review for best practices │
│ "Can this be improved?" │
│ └── USE THIS AGENT │
│ │
└─────────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────────┐
│ 代码验证触发场景 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ► 代码已生成,需要评审 │
│ "Check this Server Script before I save it" │
│ └── 使用该Agent │
│ │
│ ► 代码出现错误 │
│ "Why isn't this working?" │
│ └── 使用该Agent │
│ │
│ ► 部署前验证 │
│ "Is this production-ready?" │
│ └── 使用该Agent │
│ │
│ ► 对照最佳实践进行代码评审 │
│ "Can this be improved?" │
│ └── 使用该Agent │
│ │
└─────────────────────────────────────────────────────────────────────┘Validation Workflow
验证工作流
┌─────────────────────────────────────────────────────────────────────┐
│ CODE VALIDATOR WORKFLOW │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ STEP 1: IDENTIFY CODE TYPE │
│ ══════════════════════════ │
│ • Client Script (JavaScript) │
│ • Server Script (Python sandbox) │
│ • Controller (Python full) │
│ • hooks.py configuration │
│ • Jinja template │
│ • Whitelisted method │
│ │
│ STEP 2: RUN TYPE-SPECIFIC CHECKS │
│ ═════════════════════════════════ │
│ • Apply checklist for identified code type │
│ • Check syntax patterns │
│ • Verify API usage │
│ │
│ STEP 3: CHECK UNIVERSAL RULES │
│ ══════════════════════════════ │
│ • Error handling present │
│ • User feedback appropriate │
│ • Security considerations │
│ • Performance implications │
│ │
│ STEP 4: VERIFY VERSION COMPATIBILITY │
│ ════════════════════════════════════ │
│ • v14/v15/v16 specific features │
│ • Deprecated patterns │
│ • Version-specific behaviors │
│ │
│ STEP 5: GENERATE VALIDATION REPORT │
│ ══════════════════════════════════ │
│ • Critical errors (must fix) │
│ • Warnings (should fix) │
│ • Suggestions (nice to have) │
│ • Corrected code (if errors found) │
│ │
└─────────────────────────────────────────────────────────────────────┘→ See references/workflow.md for detailed validation steps.
┌─────────────────────────────────────────────────────────────────────┐
│ 代码验证工作流 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 步骤1:识别代码类型 │
│ ══════════════════════════ │
│ • 客户端脚本(JavaScript) │
│ • 服务器脚本(Python沙箱) │
│ • 控制器(完整Python) │
│ • hooks.py配置 │
│ • Jinja模板 │
│ • 白名单方法 │
│ │
│ 步骤2:运行特定类型检查 │
│ ═════════════════════════════════ │
│ • 对识别出的代码类型应用检查清单 │
│ • 检查语法模式 │
│ • 验证API使用方式 │
│ │
│ 步骤3:检查通用规则 │
│ ══════════════════════════════ │
│ • 是否存在错误处理 │
│ • 用户反馈是否合适 │
│ • 安全考量 │
│ • 性能影响 │
│ │
│ 步骤4:验证版本兼容性 │
│ ════════════════════════════════════ │
│ • v14/v15/v16专属特性 │
│ • 已弃用的模式 │
│ • 版本特定行为 │
│ │
│ 步骤5:生成验证报告 │
│ ══════════════════════════════════ │
│ • 严重错误(必须修复) │
│ • 警告(建议修复) │
│ • 优化建议(可选修复) │
│ • 修正后的代码(若发现错误) │
│ │
└─────────────────────────────────────────────────────────────────────┘→ 详细验证步骤请查看 references/workflow.md。
Critical Checks by Code Type
按代码类型划分的关键检查项
Server Script Checks
服务器脚本检查项
┌─────────────────────────────────────────────────────────────────────┐
│ ⚠️ SERVER SCRIPT CRITICAL CHECKS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [FATAL] Import statements │
│ ═══════════════════════════ │
│ ❌ import json → Use frappe.parse_json() │
│ ❌ from frappe.utils import X → Use frappe.utils.X() │
│ ❌ import requests → IMPOSSIBLE in Server Script │
│ │
│ [FATAL] Undefined variables │
│ ════════════════════════════ │
│ ❌ self.field → Use doc.field │
│ ❌ document.field → Use doc.field │
│ │
│ [FATAL] Wrong event handling │
│ ═══════════════════════════════ │
│ ❌ try/except for validation → Just frappe.throw() │
│ │
│ [ERROR] Event name mismatch │
│ ═══════════════════════════ │
│ ❌ Event "Before Save" code in "After Save" script │
│ │
│ [WARNING] Missing validation │
│ ═══════════════════════════════ │
│ ⚠️ No null/empty checks before operations │
│ │
└─────────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────────┐
│ ⚠️ 服务器脚本关键检查项 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [致命错误] 导入语句 │
│ ═══════════════════════════ │
│ ❌ import json → Use frappe.parse_json() │
│ ❌ from frappe.utils import X → Use frappe.utils.X() │
│ ❌ import requests → IMPOSSIBLE in Server Script │
│ │
│ [致命错误] 未定义变量 │
│ ════════════════════════════ │
│ ❌ self.field → Use doc.field │
│ ❌ document.field → Use doc.field │
│ │
│ [致命错误] 错误的事件处理 │
│ ═══════════════════════════════ │
│ ❌ try/except for validation → Just frappe.throw() │
│ │
│ [错误] 事件名称不匹配 │
│ ═══════════════════════════ │
│ ❌ Event "Before Save" code in "After Save" script │
│ │
│ [警告] 缺少验证 │
│ ═══════════════════════════════ │
│ ⚠️ No null/empty checks before operations │
│ │
└─────────────────────────────────────────────────────────────────────┘Client Script Checks
客户端脚本检查项
┌─────────────────────────────────────────────────────────────────────┐
│ CLIENT SCRIPT CRITICAL CHECKS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [FATAL] Wrong API usage │
│ ═════════════════════════ │
│ ❌ frappe.db.get_value() → Server-side only! │
│ ❌ frappe.get_doc() → Server-side only! │
│ ✓ frappe.call() for server data │
│ │
│ [FATAL] Missing async handling │
│ ══════════════════════════════ │
│ ❌ let result = frappe.call() → Returns undefined │
│ ✓ frappe.call({callback: fn}) → Use callback │
│ ✓ await frappe.call({async:false}) → Or async/await │
│ │
│ [ERROR] Field refresh issues │
│ ════════════════════════════ │
│ ❌ frm.set_value() without refresh │
│ ✓ frm.set_value() then frm.refresh_field() │
│ │
│ [WARNING] Form state checks │
│ ═══════════════════════════ │
│ ⚠️ Not checking frm.doc.__islocal for new docs │
│ ⚠️ Not checking frm.doc.docstatus for submitted docs │
│ │
└─────────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────────┐
│ 客户端脚本关键检查项 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [致命错误] 错误的API使用方式 │
│ ═════════════════════════ │
│ ❌ frappe.db.get_value() → Server-side only! │
│ ❌ frappe.get_doc() → Server-side only! │
│ ✓ frappe.call() for server data │
│ │
│ [致命错误] 缺少异步处理 │
│ ══════════════════════════════ │
│ ❌ let result = frappe.call() → Returns undefined │
│ ✓ frappe.call({callback: fn}) → Use callback │
│ ✓ await frappe.call({async:false}) → Or async/await │
│ │
│ [错误] 字段刷新问题 │
│ ════════════════════════════ │
│ ❌ frm.set_value() without refresh │
│ ✓ frm.set_value() then frm.refresh_field() │
│ │
│ [警告] 表单状态检查 │
│ ═══════════════════════════ │
│ ⚠️ Not checking frm.doc.__islocal for new docs │
│ ⚠️ Not checking frm.doc.docstatus for submitted docs │
│ │
└─────────────────────────────────────────────────────────────────────┘Controller Checks
控制器检查项
┌─────────────────────────────────────────────────────────────────────┐
│ CONTROLLER CRITICAL CHECKS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [FATAL] Wrong lifecycle usage │
│ ═════════════════════════════ │
│ ❌ Modifying self.field in on_update → Changes NOT saved! │
│ ✓ Use frappe.db.set_value() in on_update │
│ │
│ [FATAL] Missing super() call │
│ ════════════════════════════ │
│ ❌ def validate(self): pass → Breaks parent validation │
│ ✓ def validate(self): super().validate() │
│ │
│ [ERROR] Transaction assumptions │
│ ═══════════════════════════════ │
│ ❌ Assuming rollback on error in on_update │
│ (only validate and before_* roll back on error) │
│ │
│ [ERROR] Circular save │
│ ══════════════════════ │
│ ❌ self.save() inside lifecycle hooks │
│ ❌ doc.save() for same document in hooks │
│ │
└─────────────────────────────────────────────────────────────────────┘→ See references/checklists.md for complete checklists.
┌─────────────────────────────────────────────────────────────────────┐
│ 控制器关键检查项 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [致命错误] 错误的生命周期使用方式 │
│ ═════════════════════════════ │
│ ❌ Modifying self.field in on_update → Changes NOT saved! │
│ ✓ Use frappe.db.set_value() in on_update │
│ │
│ [致命错误] 缺少super()调用 │
│ ════════════════════════════ │
│ ❌ def validate(self): pass → Breaks parent validation │
│ ✓ def validate(self): super().validate() │
│ │
│ [错误] 事务假设 │
│ ═══════════════════════════════ │
│ ❌ Assuming rollback on error in on_update │
│ (only validate and before_* roll back on error) │
│ │
│ [错误] 循环保存 │
│ ══════════════════════ │
│ ❌ self.save() inside lifecycle hooks │
│ ❌ doc.save() for same document in hooks │
│ │
└─────────────────────────────────────────────────────────────────────┘→ 完整检查清单请查看 references/checklists.md。
Validation Report Format
验证报告格式
markdown
undefinedmarkdown
undefinedCode Validation Report
代码验证报告
Code Type: [Server Script / Client Script / Controller / etc.]
代码类型:[服务器脚本/客户端脚本/控制器等]
Target DocType: [DocType name]
目标DocType:[DocType名称]
Event/Trigger: [Event name]
事件/触发条件:[事件名称]
🔴 CRITICAL ERRORS (Must Fix)
🔴 严重错误(必须修复)
| Line | Issue | Fix |
|---|---|---|
| 3 | Import statement in Server Script | Use frappe.utils.X() directly |
| 行号 | 问题 | 修复方案 |
|---|---|---|
| 3 | 服务器脚本中使用导入语句 | 直接使用frappe.utils.X() |
🟡 WARNINGS (Should Fix)
🟡 警告(建议修复)
| Line | Issue | Recommendation |
|---|---|---|
| 12 | No null check before .lower() | Add: if value: value.lower() |
| 行号 | 问题 | 建议方案 |
|---|---|---|
| 12 | 调用.lower()前未做空值检查 | 添加:if value: value.lower() |
🔵 SUGGESTIONS (Nice to Have)
🔵 优化建议(可选修复)
| Line | Suggestion |
|---|---|
| 8 | Consider using frappe.db.get_value for single field |
| 行号 | 建议 |
|---|---|
| 8 | 考虑使用frappe.db.get_value获取单个字段 |
Corrected Code
修正后的代码
python
undefinedpython
undefined[Corrected version with all critical errors fixed]
[修复所有严重错误后的代码版本]
undefinedundefinedVersion Compatibility
版本兼容性
| Version | Status |
|---|---|
| v14 | ✅ Compatible |
| v15 | ✅ Compatible |
| v16 | ✅ Compatible |
undefined| 版本 | 状态 |
|---|---|
| v14 | ✅ 兼容 |
| v15 | ✅ 兼容 |
| v16 | ✅ 兼容 |
undefinedUniversal Validation Rules
通用验证规则
These apply to ALL code types:
以下规则适用于所有代码类型:
Security Checks
安全检查项
| Check | Severity | Description |
|---|---|---|
| SQL Injection | CRITICAL | Raw user input in SQL queries |
| Permission bypass | CRITICAL | Missing permission checks before operations |
| XSS vulnerability | HIGH | Unescaped user input in HTML |
| Sensitive data exposure | HIGH | Logging passwords/tokens |
| 检查内容 | 严重程度 | 描述 |
|---|---|---|
| SQL注入 | 严重 | SQL查询中使用原始用户输入 |
| 权限绕过 | 严重 | 操作前缺少权限检查 |
| XSS漏洞 | 高 | HTML中使用未转义的用户输入 |
| 敏感数据泄露 | 高 | 记录密码/令牌等敏感数据 |
Error Handling Checks
错误处理检查项
| Check | Severity | Description |
|---|---|---|
| Silent failures | HIGH | Catching exceptions without handling |
| Missing user feedback | MEDIUM | Errors not communicated to user |
| Generic error messages | LOW | "An error occurred" without details |
| 检查内容 | 严重程度 | 描述 |
|---|---|---|
| 静默失败 | 高 | 捕获异常但未处理 |
| 缺少用户反馈 | 中 | 错误未告知用户 |
| 通用错误信息 | 低 | 仅显示“发生错误”无详细信息 |
Performance Checks
性能检查项
| Check | Severity | Description |
|---|---|---|
| Query in loop | HIGH | frappe.db.* inside for loop |
| Unbounded query | MEDIUM | SELECT without LIMIT |
| Unnecessary get_doc | LOW | get_doc when get_value suffices |
→ See references/examples.md for validation examples.
| 检查内容 | 严重程度 | 描述 |
|---|---|---|
| 循环中执行查询 | 高 | for循环内使用frappe.db.* |
| 无限制查询 | 中 | SELECT语句未加LIMIT |
| 不必要的get_doc调用 | 低 | 可用get_value时使用get_doc |
→ 验证示例请查看 references/examples.md。
Version-Specific Validations
特定版本验证项
v16 Features (Fail on v14/v15)
v16特性(在v14/v15中无法运行)
python
undefinedpython
undefinedThese ONLY work on v16+
这些仅在v16+中可用
extend_doctype_class = {} # hooks.py - v16 only
naming_rule = "UUID" # DocType - v16 only
pdf_renderer = "chrome" # Print Format - v16 only
undefinedextend_doctype_class = {} # hooks.py - v16 only
naming_rule = "UUID" # DocType - v16 only
pdf_renderer = "chrome" # Print Format - v16 only
undefinedDeprecated Patterns (Warn)
已弃用的模式(警告)
python
undefinedpython
undefinedDEPRECATED - still works but should update
已弃用 - 仍可运行但建议更新
frappe.bean() # Use frappe.get_doc()
frappe.msgprint(raise_exception=True) # Use frappe.throw()
job_name parameter # Use job_id (v15+)
undefinedfrappe.bean() # Use frappe.get_doc()
frappe.msgprint(raise_exception=True) # Use frappe.throw()
job_name parameter # Use job_id (v15+)
undefinedVersion-Specific Behaviors
版本特定行为
| Behavior | v14 | v15/v16 |
|---|---|---|
| Scheduler tick | 240s | 60s |
| Background job dedup | job_name | job_id |
| 行为 | v14 | v15/v16 |
|---|---|---|
| 调度器间隔 | 240秒 | 60秒 |
| 后台任务去重 | job_name | job_id |
Quick Validation Commands
快速验证指令
Server Script Quick Check
服务器脚本快速检查
- ❌ Any statements? → FATAL
import - ❌ Any references? → FATAL (use
self.)doc. - ❌ Any ? → WARNING (usually wrong)
try/except - ✅ Uses for validation errors? → GOOD
frappe.throw() - ✅ Uses for document access? → GOOD
doc.field
- ❌ 是否存在语句?→ 致命错误
import - ❌ 是否存在引用?→ 致命错误(使用
self.)doc. - ❌ 是否存在?→ 警告(通常使用方式错误)
try/except - ✅ 是否使用处理验证错误?→ 正确
frappe.throw() - ✅ 是否使用访问文档字段?→ 正确
doc.field
Client Script Quick Check
客户端脚本快速检查
- ❌ Any calls? → FATAL (server-side only)
frappe.db.* - ❌ Any calls? → FATAL (server-side only)
frappe.get_doc() - ❌ without callback? → FATAL (async issue)
frappe.call() - ✅ Uses for field access? → GOOD
frm.doc.field - ✅ Uses after changes? → GOOD
frm.refresh_field()
- ❌ 是否存在调用?→ 致命错误(仅服务器端可用)
frappe.db.* - ❌ 是否存在调用?→ 致命错误(仅服务器端可用)
frappe.get_doc() - ❌ 未使用回调?→ 致命错误(异步问题)
frappe.call() - ✅ 是否使用访问字段?→ 正确
frm.doc.field - ✅ 修改后是否使用?→ 正确
frm.refresh_field()
Controller Quick Check
控制器快速检查
- ❌ Modifying in
self.*? → ERROR (won't save)on_update - ❌ Missing calls? → WARNING
super().method() - ❌ in lifecycle hook? → FATAL (circular)
self.save() - ✅ Imports at top of file? → GOOD (controllers allow imports)
- ✅ Error handling with try/except? → GOOD (controllers allow this)
- ❌ 在中修改
on_update?→ 错误(不会保存)self.* - ❌ 缺少调用?→ 警告
super().method() - ❌ 生命周期钩子中调用?→ 致命错误(循环调用)
self.save() - ✅ 导入语句是否在文件顶部?→ 正确(控制器允许导入)
- ✅ 是否使用try/except处理错误?→ 正确(控制器允许使用)
Integration with Other Skills
与其他技能的集成
This validator uses knowledge from:
| Skill | What It Provides |
|---|---|
| Correct syntax patterns |
| Correct implementation patterns |
| Error handling patterns |
| Query patterns and pitfalls |
| Permission check patterns |
| API response patterns |
本验证器使用以下技能的相关知识:
| 技能 | 提供内容 |
|---|---|
| 正确的语法模式 |
| 正确的实现模式 |
| 错误处理模式 |
| 查询模式与常见陷阱 |
| 权限检查模式 |
| API响应模式 |
Validation Depth Levels
验证深度级别
| Level | Checks | Use When |
|---|---|---|
| Quick | Fatal errors only | Initial scan |
| Standard | + Warnings | Pre-deployment |
| Deep | + Suggestions + Optimization | Production review |
Default: Standard level for most validations.
| 级别 | 检查内容 | 使用场景 |
|---|---|---|
| 快速 | 仅检查致命错误 | 初始扫描 |
| 标准 | + 警告项 | 部署前检查 |
| 深度 | + 优化建议 + 性能优化 | 生产环境评审 |
默认:大多数验证使用标准级别。