You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Use when building Frappe custom apps from scratch. Covers app structure, pyproject.toml configuration, module creation, patches, and fixtures for v14/v15/v16. Prevents common mistakes with app scaffolding and module organization. Keywords: custom app, bench new-app, pyproject.toml, patches, fixtures, modules, app structure, app boilerplate, bench new-app example, module setup, patch example.
license
MIT
compatibility
Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16.
metadata
author
version
OpenAEC-Foundation
2.0
Frappe Custom App Syntax
Deterministic syntax reference for building Frappe custom apps — scaffolding, configuration, modules, patches, and fixtures.
Decision Tree
What do you need?
├─ Brand new app from scratch → bench new-app
├─ Extend existing ERPNext behavior → bench new-app + required_apps = ["frappe", "erpnext"]
├─ Install existing app from Git → bench get-app <url>
└─ Add functionality to an installed app
├─ New data model → Add module to modules.txt + create DocType
├─ New fields on existing DocType → Fixtures (Custom Field)
├─ Modify field properties → Fixtures (Property Setter)
└─ Data migration → Patch in patches.txt
New app vs extend existing?
├─ Independent functionality → New app
├─ Tightly coupled to one app → New app with required_apps dependency
└─ Small customization (fields, properties) → Extend via fixtures in existing custom app
Creating an App
# Create new app (interactive prompts for title, description, publisher, etc.)
bench new-app my_custom_app
# Install on site
bench --site mysite install-app my_custom_app
# Get existing app from Git
bench get-app https://github.com/org/my_custom_app
# Build frontend assets
bench build --app my_custom_app
# Run migrations (patches + fixtures + schema sync)
bench --site mysite migrate
[pre_model_sync]# Runs BEFORE schema sync — old fields still available
myapp.patches.v1_0.backup_old_data
[post_model_sync]# Runs AFTER schema sync — new fields available
myapp.patches.v1_0.populate_new_fields
myapp.patches.v1_0.cleanup_data