ERPNext Custom App - Implementation
This skill helps you determine HOW to build and structure Frappe/ERPNext custom apps. For exact syntax, see erpnext-syntax-customapp .
Version: v14/v15/v16 compatible (differences noted)
Main Decision: What Are You Building?
┌─────────────────────────────────────────────────────────────────────────┐ │ WHAT DO YOU WANT TO CREATE? │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ► Completely new Frappe/ERPNext app? │ │ └─► See: NEW APP WORKFLOW │ │ │ │ ► Extend existing ERPNext functionality? │ │ └─► See: EXTENSION DECISION │ │ │ │ ► Migrate data between fields/DocTypes? │ │ └─► See: PATCH vs FIXTURE DECISION │ │ │ │ ► Export configuration for deployment? │ │ └─► See: FIXTURE WORKFLOW │ │ │ │ ► Update existing app to newer Frappe version? │ │ └─► See: VERSION UPGRADE WORKFLOW │ │ │ └─────────────────────────────────────────────────────────────────────────┘
Decision 1: Do You Need a Custom App?
┌─────────────────────────────────────────────────────────────────────────┐ │ DO YOU ACTUALLY NEED A CUSTOM APP? │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ What changes do you need? │ │ │ │ ► Add fields to existing DocType? │ │ └─► NO APP NEEDED: Use Custom Field + Property Setter │ │ (Can be exported as fixtures from ANY app) │ │ │ │ ► Simple automation/validation? │ │ └─► NO APP NEEDED: Server Script or Client Script │ │ (Stored in database, no deployment needed) │ │ │ │ ► Complex business logic, new DocTypes, or Python code? │ │ └─► YES, CREATE APP: You need controllers, models, and deployment │ │ │ │ ► Integration with external system? │ │ └─► USUALLY YES: APIs need whitelisted methods, scheduled sync │ │ │ │ ► Custom reports with complex queries? │ │ └─► DEPENDS: Script Report (no app) vs Query Report (app optional) │ │ │ └─────────────────────────────────────────────────────────────────────────┘
Rule: Start with the SIMPLEST solution. Server Scripts + Custom Fields solve 70% of customization needs without a custom app.
Decision 2: Extension Strategy
┌─────────────────────────────────────────────────────────────────────────┐
│ HOW TO EXTEND ERPNext? │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ► Add fields to existing DocType (e.g., Sales Invoice)? │
│ └─► Custom Field (via UI or fixtures) │
│ └─► Property Setter for behavior changes │
│ │
│ ► Modify DocType behavior/logic? │
│ ├─► v16: Use extend_doctype_class hook (PREFERRED) │
│ └─► v14/v15: Use doc_events hooks in hooks.py │
│ │
│ ► Override Jinja template? │
│ └─► Copy template to your app's templates/ folder │
│ └─► Register via jinja.override_template in hooks.py │
│ │
│ ► Add new DocType related to existing? │
│ └─► Create in your app's module │
│ └─► Link via Link field or Dynamic Link │
│ │
│ ► Add new workspace/menu items? │
│ └─► Create Workspace DocType in your app │
│ └─► Or use standard_portal_menu_items hook │
│ │
└─────────────────────────────────────────────────────────────────────────┘
See: references/decision-tree.md for detailed extension patterns.
Decision 3: Patch vs Fixture
┌─────────────────────────────────────────────────────────────────────────┐ │ SHOULD THIS BE A PATCH OR A FIXTURE? │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ Is it CONFIGURATION that should be the same everywhere? │ │ (Custom Fields, Roles, Workflows, Property Setters) │ │ └─► USE FIXTURE │ │ │ │ Is it a ONE-TIME data transformation? │ │ (Migrate old field values, cleanup bad data, populate defaults) │ │ └─► USE PATCH │ │ │ │ Does it need to run BEFORE schema changes? │ │ (Backup data from field that will be deleted) │ │ └─► USE PATCH with [pre_model_sync] │ │ │ │ Does it need to run AFTER schema changes? │ │ (Populate newly added field with calculated values) │ │ └─► USE PATCH with [post_model_sync] │ │ │ │ Is it master data / lookup tables? │ │ (Categories, Status options, Configuration records) │ │ └─► USE FIXTURE for initial, PATCH for updates │ │ │ └─────────────────────────────────────────────────────────────────────────┘
See: references/decision-tree.md for patch timing flowchart.
Decision 4: Module Organization
┌─────────────────────────────────────────────────────────────────────────┐ │ HOW MANY MODULES DO YOU NEED? │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ Small app (1-5 DocTypes, single purpose)? │ │ └─► ONE MODULE with app name │ │ Example: my_app/my_app/ (module "My App") │ │ │ │ Medium app (6-15 DocTypes, multiple areas)? │ │ └─► 2-4 MODULES by functional area │ │ Example: core/, settings/, integrations/ │ │ │ │ Large app (15+ DocTypes, complex domain)? │ │ └─► MODULES by business domain │ │ Example: inventory/, sales/, purchasing/, settings/ │ │ │ │ Multi-tenant or vertical solution? │ │ └─► Consider MULTIPLE APPS instead │ │ Base app + vertical-specific apps │ │ │ └─────────────────────────────────────────────────────────────────────────┘
Rule: Each DocType belongs to EXACTLY one module. Choose module = where would a user look for this DocType?
Quick Implementation Workflows
New App Workflow
- Create app structure → bench new-app my_app
- Configure pyproject → Edit pyproject.toml (v15+) or setup.py (v14)
- Define modules → Edit modules.txt
- Create DocTypes → bench --site mysite new-doctype MyDocType
- Write controllers → my_app/doctype/my_doctype/my_doctype.py
- Configure hooks → hooks.py for integration
- Export fixtures → bench --site mysite export-fixtures
- Test installation → bench --site testsite install-app my_app
See: references/workflows.md for detailed steps.
Patch Workflow
- Plan the migration → What data moves where?
- Choose timing → [pre_model_sync] or [post_model_sync]
- Write patch file → myapp/patches/v1_0/description.py
- Add to patches.txt → Under correct section
- Test locally → bench --site testsite migrate
- Handle errors → Add rollback logic if needed
- Test on copy of prod → ALWAYS before production
See: references/workflows.md for patch patterns.
Fixture Workflow
- Configure hooks.py → Define fixtures list with filters
- Make changes via UI → Custom Fields, Property Setters, etc.
- Export fixtures → bench --site mysite export-fixtures --app my_app
- Verify JSON files → Check my_app/fixtures/*.json
- Commit to version control
- Test import → bench --site newsite migrate
See: references/workflows.md for fixture strategies.
Version-Specific Considerations
Aspect v14 v15 v16
Build config setup.py pyproject.toml pyproject.toml
DocType extension doc_events doc_events extend_doctype_class preferred
Python minimum 3.10 3.10 3.11
INI patches ✅ ✅ ✅
Fixtures format JSON JSON JSON
v16 Breaking Changes
-
extend_doctype_class hook: Cleaner DocType extension pattern
-
Data masking: Field-level privacy configuration available
-
UUID naming: New naming rule option for DocTypes
-
Chrome PDF: wkhtmltopdf deprecated for PDF generation
Critical Implementation Rules
✅ ALWAYS
-
Start with bench new-app
-
Never create structure manually
-
Define version in init.py
-
Build will fail without it
-
Test patches on database copy - Never run untested patches on production
-
Use batch processing - Any patch touching 1000+ records needs batching
-
Filter fixtures - Never export all records of a DocType
-
Version your patches - Use v1_0, v2_0 directories for organization
❌ NEVER
-
Put frappe/erpnext in pyproject dependencies - They're not on PyPI
-
Include transactional data in fixtures - Only configuration!
-
Hardcode site-specific values - Use hooks or settings DocTypes
-
Skip frappe.db.commit() in large patches - Memory will explode
-
Delete fields without backup patch - Data loss is irreversible
-
Modify core ERPNext files - Always use hooks or override patterns
Reference Files
File Contents
references/decision-tree.md
Complete decision flowcharts
references/workflows.md
Step-by-step implementation guides
references/examples.md
Complete working examples
references/anti-patterns.md
Common mistakes to avoid
See Also
-
erpnext-syntax-customapp
-
Exact syntax reference
-
erpnext-syntax-hooks
-
Hooks configuration
-
erpnext-impl-hooks
-
Hook implementation patterns
-
erpnext-database
-
Database operations for patches