Skip to content
erpnext-syntax-customapp logo

ERPNext Custom App Syntax Skill

erpnext-syntax-customapp

Deterministic syntax for building Frappe custom apps including app structure, pyproject.toml, modules, patches and fixtures

SKILL.md

Full skill instructions

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: references/​structure.md for complete directory structure.


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 = "[email protected]" }
]
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: references/​pyproject-toml.md for all configuration options.


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: references/​modules.md for module organization.


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?

SituationSection
Migrate data from old field[pre_model_sync]
Populate new fields[post_model_sync]
Data cleanup[post_model_sync]

See: references/​patches.md for complete patch documentation.


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

DocTypeUsage
Custom FieldCustom fields on existing DocTypes
Property SetterModify field properties
RoleCustom roles
WorkflowWorkflow definitions

See: references/​fixtures.md for fixture configuration.


Minimal hooks.py

app_name = "my_custom_app"
app_title = "My Custom App"
app_publisher = "Your Company"
app_description = "Description"
app_email = "[email protected]"
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

Aspectv14v15
Build configsetup.pypyproject.toml
Dependenciesrequirements.txtIn pyproject.toml
Build backendsetuptoolsflit_core
Python minimum>=3.10>=3.10
INI patches✅✅

Critical Rules

✅ ALWAYS

  1. Define __version__ in __init__.py
  2. Add dynamic = ["version"] in pyproject.toml
  3. Register modules in modules.txt
  4. Include __init__.py in EVERY directory
  5. Put Frappe dependencies in [tool.bench.frappe-dependencies]
  6. Add error handling in patches
  7. Use batch processing for large datasets

❌ NEVER

  1. Put Frappe/​ERPNext in project dependencies (not on PyPI)
  2. Create patches without error handling
  3. Include user/​transactional data in fixtures
  4. Hardcode site-specific values
  5. Process large datasets without batching

Fixtures vs Patches

WhatFixturesPatches
Custom Fields✅❌
Property Setters✅❌
Roles/​Workflows✅❌
Data transformation❌✅
Data cleanup❌✅
One-time migration❌✅

Reference Files

FileContents
references/​structure.mdComplete directory structure
references/​pyproject-toml.mdBuild configuration options
references/​modules.mdModule organization
references/​patches.mdMigration scripts
references/​fixtures.mdData export/​import
references/​examples.mdComplete app examples
references/​anti-patterns.mdMistakes to avoid

See Also

  • erpnext-syntax-hooks - For hooks.py configuration
  • erpnext-syntax-controllers - For DocType controllers
  • erpnext-impl-customapp - For implementation patterns

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View