Claude-skill-registry erpnext-syntax-customapp
Deterministic syntax for building Frappe custom apps including app structure, pyproject.toml, modules, patches and fixtures
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/erpnext-syntax-customapp" ~/.claude/skills/majiayu000-claude-skill-registry-erpnext-syntax-customapp && rm -rf "$T"
manifest:
skills/data/erpnext-syntax-customapp/SKILL.mdsource content
ERPNext Custom App Syntax Skill
Complete syntax for building Frappe custom apps in v14/v15, including build configuration, module organization, patches and fixtures.
When to Use This Skill
USE this skill when you:
- Create a new Frappe/ERPNext custom app
- Configure pyproject.toml or setup.py
- Organize modules within an app
- Write database migration patches
- Configure fixtures for data export/import
- Manage app dependencies
DO NOT USE for:
- DocType controllers (use erpnext-syntax-controllers)
- Client Scripts (use erpnext-syntax-clientscripts)
- Server Scripts (use erpnext-syntax-serverscripts)
- Hooks configuration (use erpnext-syntax-hooks)
App Structure Overview
v15 (pyproject.toml - Primary)
apps/my_custom_app/ ├── pyproject.toml # Build configuration ├── README.md ├── my_custom_app/ # Main package │ ├── __init__.py # MUST contain __version__! │ ├── hooks.py # Frappe integration │ ├── modules.txt # Module registration │ ├── patches.txt # Migration scripts │ ├── patches/ # Patch files │ ├── my_custom_app/ # Default module │ │ └── doctype/ │ ├── public/ # Client assets │ └── templates/ # Jinja templates └── .git/
See:
for complete directory structure.references/structure.md
Critical Files
init.py (REQUIRED)
# my_custom_app/__init__.py __version__ = "0.0.1"
CRITICAL: Without
__version__ the flit build fails!
pyproject.toml (v15)
[build-system] requires = ["flit_core >=3.4,<4"] build-backend = "flit_core.buildapi" [project] name = "my_custom_app" authors = [ { name = "Your Company", email = "dev@example.com" } ] description = "Description of your app" requires-python = ">=3.10" readme = "README.md" dynamic = ["version"] dependencies = [] [tool.bench.frappe-dependencies] frappe = ">=15.0.0,<16.0.0" erpnext = ">=15.0.0,<16.0.0"
See:
for all configuration options.references/pyproject-toml.md
Modules
modules.txt
My Custom App Integrations Settings Reports
Rules:
- One module per line
- Spaces in name → underscores in directory
- Every DocType MUST belong to a module
Module Directory
my_custom_app/ ├── my_custom_app/ # "My Custom App" module │ ├── __init__.py # REQUIRED │ └── doctype/ ├── integrations/ # "Integrations" module │ ├── __init__.py # REQUIRED │ └── doctype/ └── settings/ # "Settings" module ├── __init__.py # REQUIRED └── doctype/
See:
for module organization.references/modules.md
Patches (Migration Scripts)
patches.txt with INI Sections
[pre_model_sync] # Before schema sync - old fields still available myapp.patches.v1_0.backup_old_data [post_model_sync] # After schema sync - new fields available myapp.patches.v1_0.populate_new_fields myapp.patches.v1_0.cleanup_data
Patch Implementation
# myapp/patches/v1_0/populate_new_fields.py import frappe def execute(): """Populate new fields with default values.""" batch_size = 1000 offset = 0 while True: records = frappe.get_all( "MyDocType", filters={"new_field": ["is", "not set"]}, fields=["name"], limit_page_length=batch_size, limit_start=offset ) if not records: break for record in records: frappe.db.set_value( "MyDocType", record.name, "new_field", "default_value", update_modified=False ) frappe.db.commit() offset += batch_size
When Pre vs Post Model Sync?
| Situation | Section |
|---|---|
| Migrate data from old field | |
| Populate new fields | |
| Data cleanup | |
See:
for complete patch documentation.references/patches.md
Fixtures
hooks.py Configuration
fixtures = [ # All records "Category", # With filter { "dt": "Custom Field", "filters": [["module", "=", "My Custom App"]] }, # Multiple filters { "dt": "Property Setter", "filters": [ ["module", "=", "My Custom App"], ["doc_type", "in", ["Sales Invoice", "Sales Order"]] ] } ]
Exporting
bench --site mysite export-fixtures --app my_custom_app
Common Fixture DocTypes
| DocType | Usage |
|---|---|
| Custom fields on existing DocTypes |
| Modify field properties |
| Custom roles |
| Workflow definitions |
See:
for fixture configuration.references/fixtures.md
Minimal hooks.py
app_name = "my_custom_app" app_title = "My Custom App" app_publisher = "Your Company" app_description = "Description" app_email = "dev@example.com" app_license = "MIT" required_apps = ["frappe"] # Or ["frappe", "erpnext"] fixtures = [ {"dt": "Custom Field", "filters": [["module", "=", "My Custom App"]]} ]
Creating and Installing App
# Create new app bench new-app my_custom_app # Install on site bench --site mysite install-app my_custom_app # Migrate (patches + fixtures) bench --site mysite migrate # Build assets bench build --app my_custom_app
Version Differences
| Aspect | v14 | v15 |
|---|---|---|
| Build config | setup.py | pyproject.toml |
| Dependencies | requirements.txt | In pyproject.toml |
| Build backend | setuptools | flit_core |
| Python minimum | >=3.10 | >=3.10 |
| INI patches | ✅ | ✅ |
Critical Rules
✅ ALWAYS
- Define
in__version____init__.py - Add
in pyproject.tomldynamic = ["version"] - Register modules in
modules.txt - Include
in EVERY directory__init__.py - Put Frappe dependencies in
[tool.bench.frappe-dependencies] - Add error handling in patches
- Use batch processing for large datasets
❌ NEVER
- Put Frappe/ERPNext in project dependencies (not on PyPI)
- Create patches without error handling
- Include user/transactional data in fixtures
- Hardcode site-specific values
- Process large datasets without batching
Fixtures vs Patches
| What | Fixtures | Patches |
|---|---|---|
| Custom Fields | ✅ | ❌ |
| Property Setters | ✅ | ❌ |
| Roles/Workflows | ✅ | ❌ |
| Data transformation | ❌ | ✅ |
| Data cleanup | ❌ | ✅ |
| One-time migration | ❌ | ✅ |
Reference Files
| File | Contents |
|---|---|
| Complete directory structure |
| Build configuration options |
| Module organization |
| Migration scripts |
| Data export/import |
| Complete app examples |
| Mistakes to avoid |
See Also
- For hooks.py configurationerpnext-syntax-hooks
- For DocType controllerserpnext-syntax-controllers
- For implementation patternserpnext-impl-customapp