Loading...
Loading...
Deterministic syntax for building Frappe custom apps including app structure, pyproject.toml, modules, patches and fixtures
npx skill4agent add openaec-foundation/erpnext_anthropic_claude_development_skill_package erpnext-syntax-customappComplete syntax for building Frappe custom apps in v14/v15, including build configuration, module organization, patches and fixtures.
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
# my_custom_app/__init__.py
__version__ = "0.0.1"__version__[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
My Custom App
Integrations
Settings
Reportsmy_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
[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# 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| Situation | Section |
|---|---|
| Migrate data from old field | |
| Populate new fields | |
| Data cleanup | |
See:for complete patch documentation.references/patches.md
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"]]
]
}
]bench --site mysite export-fixtures --app my_custom_app| DocType | Usage |
|---|---|
| Custom fields on existing DocTypes |
| Modify field properties |
| Custom roles |
| Workflow definitions |
See:for fixture configuration.references/fixtures.md
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"]]}
]# 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| 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 | ✅ | ✅ |
__version____init__.pydynamic = ["version"]modules.txt__init__.py[tool.bench.frappe-dependencies]| What | Fixtures | Patches |
|---|---|---|
| Custom Fields | ✅ | ❌ |
| Property Setters | ✅ | ❌ |
| Roles/Workflows | ✅ | ❌ |
| Data transformation | ❌ | ✅ |
| Data cleanup | ❌ | ✅ |
| One-time migration | ❌ | ✅ |
| File | Contents |
|---|---|
| Complete directory structure |
| Build configuration options |
| Module organization |
| Migration scripts |
| Data export/import |
| Complete app examples |
| Mistakes to avoid |
erpnext-syntax-hookserpnext-syntax-controllerserpnext-impl-customapp