Agent skill

erpnext-code-validator

Intelligent agent for validating ERPNext/Frappe code against best practices and common pitfalls. Use when reviewing generated code, checking for errors before deployment, or validating code quality. Triggers: review this code, check my script, validate before deployment, is this correct, find bugs, check for errors, will this work.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/erpnext-code-validator

SKILL.md

ERPNext Code Validator Agent

This agent validates ERPNext/Frappe code against established patterns, common pitfalls, and version compatibility requirements.

Purpose: Catch errors BEFORE deployment, not after

When to Use This 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                                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

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.

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                          │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

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               │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

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.

Validation Report Format

markdown
## Code Validation Report

### Code Type: [Server Script / Client Script / Controller / etc.]
### Target DocType: [DocType name]
### Event/Trigger: [Event name]

---

### 🔴 CRITICAL ERRORS (Must Fix)

| Line | Issue | Fix |
|------|-------|-----|
| 3 | Import statement in Server Script | Use frappe.utils.X() directly |

### 🟡 WARNINGS (Should Fix)

| Line | Issue | Recommendation |
|------|-------|----------------|
| 12 | No null check before .lower() | Add: if value: value.lower() |

### 🔵 SUGGESTIONS (Nice to Have)

| Line | Suggestion |
|------|------------|
| 8 | Consider using frappe.db.get_value for single field |

---

### Corrected Code

```python
# [Corrected version with all critical errors fixed]

Version Compatibility

Version Status
v14 ✅ Compatible
v15 ✅ Compatible
v16 ✅ Compatible

## Universal 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 |

### 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](references/examples.md) for validation examples.

## Version-Specific Validations

### v16 Features (Fail on v14/v15)

```python
# These ONLY work on v16+
extend_doctype_class = {}  # hooks.py - v16 only
naming_rule = "UUID"       # DocType - v16 only
pdf_renderer = "chrome"    # Print Format - v16 only

Deprecated Patterns (Warn)

python
# DEPRECATED - 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+)

Version-Specific Behaviors

Behavior v14 v15/v16
Scheduler tick 240s 60s
Background job dedup job_name job_id

Quick Validation Commands

Server Script Quick Check

  1. ❌ Any import statements? → FATAL
  2. ❌ Any self. references? → FATAL (use doc.)
  3. ❌ Any try/except? → WARNING (usually wrong)
  4. ✅ Uses frappe.throw() for validation errors? → GOOD
  5. ✅ Uses doc.field for document access? → GOOD

Client Script Quick Check

  1. ❌ Any frappe.db.* calls? → FATAL (server-side only)
  2. ❌ Any frappe.get_doc() calls? → FATAL (server-side only)
  3. frappe.call() without callback? → FATAL (async issue)
  4. ✅ Uses frm.doc.field for field access? → GOOD
  5. ✅ Uses frm.refresh_field() after changes? → GOOD

Controller Quick Check

  1. ❌ Modifying self.* in on_update? → ERROR (won't save)
  2. ❌ Missing super().method() calls? → WARNING
  3. self.save() in lifecycle hook? → FATAL (circular)
  4. ✅ Imports at top of file? → GOOD (controllers allow imports)
  5. ✅ Error handling with try/except? → GOOD (controllers allow this)

Integration with Other Skills

This validator uses knowledge from:

Skill What It Provides
erpnext-syntax-* Correct syntax patterns
erpnext-impl-* Correct implementation patterns
erpnext-errors-* Error handling patterns
erpnext-database Query patterns and pitfalls
erpnext-permissions Permission check patterns
erpnext-api-patterns API response patterns

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.

Didn't find tool you were looking for?

Be as detailed as possible for better results