SuperAPI AI 工具集

erpnext-syntax-customapp

编程与开发

查看项目

更新于 10-16 02:06

技能介绍

用途

本技能用于在 Frappe/ERPNext 中从零构建自定义应用(custom app),提供 v14/v15/v16 版本的完整语法参考。当需要创建新的 Frappe/ERPNext 自定义应用、配置 pyproject.toml 或 setup.py、组织应用内的模块、编写数据库迁移补丁、配置 fixtures 数据导出/导入,或管理应用依赖时,可使用本技能。

本技能不适用于以下场景:DocType 控制器(应使用 erpnext-syntax-controllers)、客户端脚本(应使用 erpnext-syntax-clientscripts)、服务器脚本(应使用 erpnext-syntax-serverscripts)、hooks 配置(应使用 erpnext-syntax-hooks)。

主要能力

应用结构

v15 以 pyproject.toml 为主要构建配置。典型目录结构如下:

apps/my_custom_app/
├── pyproject.toml                     # 构建配置
├── README.md
├── my_custom_app/                     # 主包
│   ├── __init__.py                    # 必须包含 __version__!
│   ├── hooks.py                       # Frappe 集成
│   ├── modules.txt                    # 模块注册
│   ├── patches.txt                    # 迁移脚本
│   ├── patches/                       # 补丁文件
│   ├── my_custom_app/                 # 默认模块
│   │   └── doctype/
│   ├── public/                        # 客户端资源
│   └── templates/                     # Jinja 模板
└── .git/

关键文件

__init__.py 为必需文件,必须定义 __version__,否则 flit 构建会失败:

# my_custom_app/__init__.py
__version__ = "0.0.1"

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"

模块组织

modules.txt 每行一个模块,名称中的空格在目录中对应下划线,每个 DocType 必须归属于某个模块。每个模块目录必须包含 __init__.py。

My Custom App
Integrations
Settings
Reports

补丁(迁移脚本)

patches.txt 使用 INI 分节:

[pre_model_sync]
# 模式同步前 - 旧字段仍可用
myapp.patches.v1_0.backup_old_data

[post_model_sync]
# 模式同步后 - 新字段可用
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

pre_model_sync 与 post_model_sync 的选择:从旧字段迁移数据用 pre_model_sync;填充新字段、数据清理用 post_model_sync。

Fixtures

在 hooks.py 中配置:

fixtures = [
    # 所有记录
    "Category",
    
    # 带过滤条件
    {
        "dt": "Custom Field",
        "filters": [["module", "=", "My Custom App"]]
    },
    
    # 多个过滤条件
    {
        "dt": "Property Setter",
        "filters": [
            ["module", "=", "My Custom App"],
            ["doc_type", "in", ["Sales Invoice", "Sales Order"]]
        ]
    }
]

导出命令:

bench --site mysite export-fixtures --app my_custom_app

常见 fixture DocType 包括 Custom Field(现有 DocType 上的自定义字段)、Property Setter(修改字段属性)、Role(自定义角色)、Workflow(工作流定义)。

最小 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"]  # 或 ["frappe", "erpnext"]

fixtures = [
    {"dt": "Custom Field", "filters": [["module", "=", "My Custom App"]]}
]

使用方式

创建并安装应用:

# 创建新应用
bench new-app my_custom_app

# 在站点上安装
bench --site mysite install-app my_custom_app

# 迁移(补丁 + fixtures)
bench --site mysite migrate

# 构建资源
bench build --app my_custom_app

版本差异

方面 v14 v15
构建配置 setup.py pyproject.toml
依赖 requirements.txt 在 pyproject.toml 中
构建后端 setuptools flit_core
Python 最低版本 >=3.10 >=3.10
INI 补丁 ✅ ✅

关键规则

应始终:在 __init__.py 中定义 __version__;在 pyproject.toml 中添加 dynamic = ["version"];在 modules.txt 中注册模块;每个目录都包含 __init__.py;将 Frappe 依赖放在 [tool.bench.frappe-dependencies];在补丁中添加错误处理;对大数据集使用批处理。

不应:将 Frappe/ERPNext 放入项目依赖(它们不在 PyPI 上);创建无错误处理的补丁;在 fixtures 中包含用户/事务数据;硬编码站点特定值;不批处理地处理大数据集。

Fixtures 与 Patches 的区分

内容 Fixtures Patches
自定义字段 ✅ ❌
属性设置器 ✅ ❌
角色/工作流 ✅ ❌
数据转换 ❌ ✅
数据清理 ❌ ✅
一次性迁移 ❌ ✅

适用场景与限制

适用场景:创建新的 Frappe/ERPNext 自定义应用、配置构建文件、组织模块、编写迁移补丁、配置 fixtures、管理应用依赖。

限制:本技能不覆盖 DocType 控制器、客户端脚本、服务器脚本和 hooks 配置,这些需使用对应的独立技能。参考文件包括 references/structure.md(完整目录结构)、references/pyproject-toml.md(构建配置选项)、references/modules.md(模块组织)、references/patches.md(迁移脚本)、references/fixtures.md(数据导出/导入)、references/examples.md(完整应用示例)、references/anti-patterns.md(应避免的错误)。相关技能:erpnext-syntax-hooks(hooks.py 配置)、erpnext-syntax-controllers(DocType 控制器)、erpnext-impl-customapp(实现模式)。