erpnext-syntax-serverscripts
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseERPNext Server Scripts Syntax
ERPNext Server Scripts 语法参考
Server Scripts are Python scripts that run within Frappe's secure sandbox environment. They are managed via Setup → Server Script in the ERPNext UI.
Server Scripts 是运行在 Frappe 安全沙箱环境中的 Python 脚本。可通过 ERPNext 界面中的 设置 → 服务器脚本 进行管理。
CRITICAL: Sandbox Limitations
重要提示:沙箱限制
┌─────────────────────────────────────────────────────────────────────┐
│ ⚠️ NO IMPORTS ALLOWED │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ The sandbox blocks ALL import statements: │
│ import json → ImportError: __import__ not found │
│ from datetime import date → ImportError │
│ │
│ SOLUTION: Use Frappe's pre-loaded namespace: │
│ frappe.utils.nowdate() not: from frappe.utils import nowdate │
│ frappe.parse_json(data) not: import json; json.loads(data) │
│ │
└─────────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────────┐
│ ⚠️ NO IMPORTS ALLOWED │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 沙箱会阻止所有导入语句: │
│ import json → ImportError: __import__ not found │
│ from datetime import date → ImportError │
│ │
│ 解决方案:使用 Frappe 预加载的命名空间: │
│ frappe.utils.nowdate() 而非: from frappe.utils import nowdate │
│ frappe.parse_json(data) 而非: import json; json.loads(data) │
│ │
└─────────────────────────────────────────────────────────────────────┘Server Script Types
服务器脚本类型
| Type | Usage | Trigger |
|---|---|---|
| Document Event | React to document lifecycle | Save, Submit, Cancel, etc. |
| API | Custom REST endpoint | HTTP request to |
| Scheduler Event | Scheduled tasks | Cron schedule |
| Permission Query | Dynamic list filtering | Document list view |
| 类型 | 用途 | 触发方式 |
|---|---|---|
| 文档事件 | 响应文档生命周期 | 保存、提交、取消等 |
| API | 自定义 REST 端点 | HTTP 请求到 |
| 调度器事件 | 定时任务 | Cron 调度规则 |
| 权限查询 | 动态列表过滤 | 文档列表视图 |
Event Name Mapping (Document Event)
事件名称映射(文档事件)
IMPORTANT: The UI event names differ from the internal hook names:
| UI Name (Server Script) | Internal Hook | When |
|---|---|---|
| Before Insert | | Before new doc to DB |
| After Insert | | After new doc saved |
| Before Validate | | Before validation |
| Before Save | | Before save (new or update) |
| After Save | | After successful save |
| Before Submit | | Before submit |
| After Submit | | After submit |
| Before Cancel | | Before cancel |
| After Cancel | | After cancel |
| Before Delete | | Before delete |
| After Delete | | After delete |
重要提示:UI 中的事件名称与内部钩子名称不同:
| UI 名称(服务器脚本) | 内部钩子 | 触发时机 |
|---|---|---|
| 插入前 | | 新文档存入数据库前 |
| 插入后 | | 新文档保存后 |
| 验证前 | | 验证操作前 |
| 保存前 | | 保存前(新建或更新) |
| 保存后 | | 保存成功后 |
| 提交前 | | 提交操作前 |
| 提交后 | | 提交操作后 |
| 取消前 | | 取消操作前 |
| 取消后 | | 取消操作后 |
| 删除前 | | 删除操作前 |
| 删除后 | | 删除操作后 |
Quick Reference: Available API
快速参考:可用 API
Always available in sandbox
沙箱中始终可用
python
undefinedpython
undefinedDocument object (in Document Event scripts)
文档对象(文档事件脚本中)
doc # Current document
doc.name # Document name
doc.doctype # DocType name
doc.fieldname # Field value
doc.get("fieldname") # Safe field access
doc.items # Child table (list)
doc # 当前文档
doc.name # 文档名称
doc.doctype # 文档类型名称
doc.fieldname # 字段值
doc.get("fieldname") # 安全字段访问
doc.items # 子表(列表)
Frappe namespace
Frappe 命名空间
frappe.db # Database operations
frappe.get_doc() # Fetch document
frappe.get_all() # Multiple documents
frappe.throw() # Validation error
frappe.msgprint() # User message
frappe.log_error() # Error logging
frappe.utils.* # Utility functions
frappe.session.user # Current user
frappe.form_dict # Request parameters (API)
frappe.response # Response object (API)
undefinedfrappe.db # 数据库操作
frappe.get_doc() # 获取文档
frappe.get_all() # 获取多个文档
frappe.throw() # 抛出验证错误
frappe.msgprint() # 用户提示消息
frappe.log_error() # 错误日志
frappe.utils.* # 工具函数
frappe.session.user # 当前用户
frappe.form_dict # 请求参数(API 脚本)
frappe.response # 响应对象(API 脚本)
undefinedDecision Tree: Which Script Type?
决策树:选择哪种脚本类型?
What do you want to achieve?
│
├─► React to document save/submit/cancel?
│ └─► Document Event script
│
├─► Create REST API endpoint?
│ └─► API script
│
├─► Run task on schedule?
│ └─► Scheduler Event script
│
└─► Filter document list view per user/role?
└─► Permission Query script你想要实现什么功能?
│
├─► 响应文档的保存/提交/取消操作?
│ └─► 文档事件脚本
│
├─► 创建 REST API 端点?
│ └─► API 脚本
│
├─► 按计划运行任务?
│ └─► 调度器事件脚本
│
└─► 按用户/角色过滤文档列表视图?
└─► 权限查询脚本Basic Syntax per Type
各类型基本语法
Document Event
文档事件
python
undefinedpython
undefinedConfiguration:
配置:
Reference DocType: Sales Invoice
参考文档类型:销售发票
DocType Event: Before Save (= validate)
文档类型事件:保存前(= validate)
if doc.grand_total < 0:
frappe.throw("Total cannot be negative")
if doc.grand_total > 10000:
doc.requires_approval = 1
undefinedif doc.grand_total < 0:
frappe.throw("总金额不能为负数")
if doc.grand_total > 10000:
doc.requires_approval = 1
undefinedAPI
API
python
undefinedpython
undefinedConfiguration:
配置:
API Method: get_customer_info
API 方法:get_customer_info
Allow Guest: No
允许访客访问:否
Endpoint: /api/method/get_customer_info
端点:/api/method/get_customer_info
customer = frappe.form_dict.get("customer")
if not customer:
frappe.throw("Customer parameter required")
data = frappe.get_all(
"Sales Order",
filters={"customer": customer, "docstatus": 1},
fields=["name", "grand_total"],
limit=10
)
frappe.response["message"] = data
undefinedcustomer = frappe.form_dict.get("customer")
if not customer:
frappe.throw("缺少客户参数")
data = frappe.get_all(
"Sales Order",
filters={"customer": customer, "docstatus": 1},
fields=["name", "grand_total"],
limit=10
)
frappe.response["message"] = data
undefinedScheduler Event
调度器事件
python
undefinedpython
undefinedConfiguration:
配置:
Event Frequency: Cron
事件频率:Cron
Cron Format: 0 9 * * * (daily at 9:00)
Cron 格式:0 9 * * *(每天 9:00 运行)
overdue = frappe.get_all(
"Sales Invoice",
filters={"status": "Unpaid", "due_date": ["<", frappe.utils.today()]},
fields=["name", "customer"]
)
for inv in overdue:
frappe.log_error(f"Overdue: {inv.name}", "Invoice Reminder")
frappe.db.commit()
undefinedoverdue = frappe.get_all(
"Sales Invoice",
filters={"status": "Unpaid", "due_date": ["<", frappe.utils.today()]},
fields=["name", "customer"]
)
for inv in overdue:
frappe.log_error(f"逾期发票:{inv.name}", "发票提醒")
frappe.db.commit()
undefinedPermission Query
权限查询
python
undefinedpython
undefinedConfiguration:
配置:
Reference DocType: Sales Invoice
参考文档类型:销售发票
Output: conditions string for WHERE clause
输出:WHERE 子句的条件字符串
user_roles = frappe.get_roles(user)
if "System Manager" in user_roles:
conditions = "" # Full access
elif "Sales User" in user_roles:
conditions = f".owner = {frappe.db.escape(user)}"
else:
conditions = "1=0" # No access
tabSales Invoiceundefineduser_roles = frappe.get_roles(user)
if "System Manager" in user_roles:
conditions = "" # 完全访问权限
elif "Sales User" in user_roles:
conditions = f".owner = {frappe.db.escape(user)}"
else:
conditions = "1=0" # 无访问权限
tabSales InvoiceundefinedReferences
参考资料
- references/events.md - Complete event mapping and execution order
- references/methods.md - All available frappe.* methods in sandbox
- references/examples.md - 10+ working examples per script type
- references/anti-patterns.md - Sandbox limitations and common mistakes
- references/events.md - 完整的事件映射和执行顺序
- references/methods.md - 沙箱中所有可用的 frappe.* 方法
- references/examples.md - 每种脚本类型的 10+ 个可用示例
- references/anti-patterns.md - 沙箱限制和常见错误
Version Information
版本信息
- Frappe v14+: Server Scripts fully supported
- Activation required:
bench --site [site] set-config server_script_enabled true - Frappe v15: No significant syntax changes for Server Scripts
- Frappe v14+:完全支持 Server Scripts
- 需要激活:
bench --site [site] set-config server_script_enabled true - Frappe v15:Server Scripts 无重大语法变更