erpnext-impl-scheduler
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseERPNext Scheduler - Implementation
ERPNext调度器 - 实现指南
This skill helps you implement scheduled tasks and background jobs. For exact syntax, see .
erpnext-syntax-schedulerVersion: v14/v15/v16 compatible
本指南将帮助你实现定时任务与后台作业。如需精确语法参考,请查看。
erpnext-syntax-scheduler版本兼容性:支持v14/v15/v16版本
Main Decision: What Are You Trying to Do?
核心决策:你要实现什么功能?
┌─────────────────────────────────────────────────────────────────────┐
│ SCHEDULER DECISION │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Run at fixed intervals or times? │
│ ├── YES → Scheduler Event (hooks.py) │
│ │ See: references/workflows.md §1-2 │
│ │ │
│ └── NO → Run in response to user action? │
│ ├── YES → frappe.enqueue() │
│ │ See: references/workflows.md §3-4 │
│ │ │
│ └── NO → Probably neither - reconsider requirements │
│ │
└─────────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────────┐
│ 调度器决策树 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 是否需要按固定间隔或时间运行? │
│ ├── 是 → 调度器事件(hooks.py) │
│ │ 参考:references/workflows.md §1-2 │
│ │ │
│ └── 否 → 是否需响应用户操作触发? │
│ ├── 是 → frappe.enqueue() │
│ │ 参考:references/workflows.md §3-4 │
│ │ │
│ └── 否 → 不属于上述场景 - 请重新审视需求 │
│ │
└─────────────────────────────────────────────────────────────────────┘Scheduler Event vs frappe.enqueue
调度器事件 vs frappe.enqueue
| Aspect | Scheduler Event | frappe.enqueue |
|---|---|---|
| Triggered by | Time/interval | Code execution |
| Defined in | hooks.py | Python code |
| Arguments | None (must be parameterless) | Any serializable data |
| Use case | Daily cleanup, hourly sync | User-triggered long task |
| Restart behavior | Runs on schedule | Lost if worker restarts |
| 维度 | 调度器事件 | frappe.enqueue |
|---|---|---|
| 触发方式 | 时间/间隔 | 代码执行 |
| 定义位置 | hooks.py | Python代码 |
| 参数支持 | 无(必须无参数) | 支持任何可序列化数据 |
| 适用场景 | 每日清理、每小时同步 | 用户触发的长耗时任务 |
| 重启行为 | 按计划自动运行 | 若Worker重启则任务丢失 |
Which Scheduler Event Type?
选择哪种调度器事件类型?
┌─────────────────────────────────────────────────────────────────────┐
│ SCHEDULER EVENT TYPE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Simple recurring interval? │
│ ├── Every minute → scheduler_events.cron["* * * * *"] │
│ ├── Hourly → scheduler_events.hourly │
│ ├── Daily → scheduler_events.daily │
│ ├── Weekly → scheduler_events.weekly │
│ └── Monthly → scheduler_events.monthly │
│ │
│ Complex schedule (e.g., "weekdays at 9am")? │
│ └── scheduler_events.cron["0 9 * * 1-5"] │
│ │
│ Run after every request? │
│ └── scheduler_events.all (use sparingly!) │
│ │
└─────────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────────┐
│ 调度器事件类型选择 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 是否为简单重复间隔? │
│ ├── 每分钟 → scheduler_events.cron["* * * * *"] │
│ ├── 每小时 → scheduler_events.hourly │
│ ├── 每天 → scheduler_events.daily │
│ ├── 每周 → scheduler_events.weekly │
│ └── 每月 → scheduler_events.monthly │
│ │
│ 是否为复杂调度(如"工作日上午9点")? │
│ └── scheduler_events.cron["0 9 * * 1-5"] │
│ │
│ 是否需在每次请求后运行? │
│ └── scheduler_events.all(请谨慎使用!) │
│ │
└─────────────────────────────────────────────────────────────────────┘Which Queue?
选择哪种队列?
| Queue | Timeout | Use For |
|---|---|---|
| 5 min | Quick operations (<1 min) |
| 5 min | Standard tasks (1-3 min) |
| 30 min | Heavy processing (>3 min) |
Rule: Always specify queue explicitly. Default is .
short| 队列 | 超时时间 | 适用场景 |
|---|---|---|
| 5分钟 | 快速操作(耗时<1分钟) |
| 5分钟 | 标准任务(耗时1-3分钟) |
| 30分钟 | 重型处理(耗时>3分钟) |
规则:务必显式指定队列。默认队列为。
shortQuick Start: Basic Scheduled Task
快速入门:基础定时任务
python
undefinedpython
undefinedmyapp/tasks.py
myapp/tasks.py
import frappe
def daily_cleanup():
"""Daily cleanup task - no parameters allowed"""
frappe.db.delete("Error Log", {"creation": ("<", frappe.utils.add_days(None, -30))})
frappe.db.commit()
```pythonimport frappe
def daily_cleanup():
"""每日清理任务 - 不允许传入参数"""
frappe.db.delete("Error Log", {"creation": ("<", frappe.utils.add_days(None, -30))})
frappe.db.commit()
```pythonhooks.py
hooks.py
scheduler_events = {
"daily": [
"myapp.tasks.daily_cleanup"
]
}
After editing hooks.py: `bench migrate`scheduler_events = {
"daily": [
"myapp.tasks.daily_cleanup"
]
}
编辑hooks.py后执行:`bench migrate`Quick Start: Background Job
快速入门:后台作业
python
undefinedpython
undefinedmyapp/api.py
myapp/api.py
import frappe
from frappe import enqueue
@frappe.whitelist()
def process_documents(doctype, filters):
enqueue(
"myapp.tasks.process_batch",
queue="long",
timeout=1800,
job_id=f"process_{doctype}_{frappe.session.user}", # v15+ dedup
doctype=doctype,
filters=filters
)
return {"status": "queued"}
undefinedimport frappe
from frappe import enqueue
@frappe.whitelist()
def process_documents(doctype, filters):
enqueue(
"myapp.tasks.process_batch",
queue="long",
timeout=1800,
job_id=f"process_{doctype}_{frappe.session.user}", # v15+ 版本支持去重
doctype=doctype,
filters=filters
)
return {"status": "queued"}
undefinedCritical Rules
重要规则
1. Scheduler tasks receive NO arguments
1. 调度器任务不接收任何参数
python
undefinedpython
undefined❌ WRONG
❌ 错误示例
def my_task(doctype): # Arguments not supported
pass
def my_task(doctype): # 不支持传入参数
pass
✅ CORRECT
✅ 正确示例
def my_task(): # Parameterless
doctype = "Sales Invoice" # Hardcode or read from settings
undefineddef my_task(): # 无参数
doctype = "Sales Invoice" # 硬编码或从配置读取
undefined2. ALWAYS migrate after hooks.py changes
2. 修改hooks.py后必须执行migrate
bash
bench migrate # Required to register new scheduler eventsbash
bench migrate # 必须执行此命令以注册新的调度器事件3. Jobs run as Administrator
3. 作业以Administrator权限运行
Scheduler and enqueued jobs run with Administrator permissions. Always commit explicitly.
调度器和入队作业均以Administrator权限运行,请务必显式提交事务。
4. Commit after batches, not per record
4. 批量处理后提交,而非逐条记录提交
python
undefinedpython
undefined❌ WRONG - Slow
❌ 错误示例 - 效率低下
for doc in docs:
doc.save()
frappe.db.commit() # Commit per record
for doc in docs:
doc.save()
frappe.db.commit() # 逐条记录提交
✅ CORRECT - Fast
✅ 正确示例 - 高效
for doc in docs:
doc.save()
frappe.db.commit() # Single commit after batch
undefinedfor doc in docs:
doc.save()
frappe.db.commit() # 批量处理后单次提交
undefined5. Use job_id for deduplication (v15+)
5. 使用job_id实现作业去重(v15及以上版本)
python
enqueue(..., job_id="unique_identifier") # Prevents duplicate jobspython
enqueue(..., job_id="unique_identifier") # 防止重复作业Version Differences
版本差异
| Aspect | v14 | v15 | v16 |
|---|---|---|---|
| Tick interval | 4 min | 60 sec | 60 sec |
| Job dedup | | | |
| Cron support | ✅ | ✅ | ✅ |
V14 deduplication uses different parameter:
python
undefined| 维度 | v14 | v15 | v16 |
|---|---|---|---|
| 调度间隔 | 4分钟 | 60秒 | 60秒 |
| 作业去重参数 | | | |
| Cron支持 | ✅ | ✅ | ✅ |
V14版本的作业去重使用不同参数:
python
undefinedv14
v14版本
enqueue(..., job_name="unique_id")
enqueue(..., job_name="unique_id")
v15+
v15及以上版本
enqueue(..., job_id="unique_id")
---enqueue(..., job_id="unique_id")
---Reference Files
参考文件
| File | Contents |
|---|---|
| workflows.md | Step-by-step implementation patterns |
| decision-tree.md | Detailed decision flowcharts |
| examples.md | Complete working examples |
| anti-patterns.md | Common mistakes to avoid |
| 文件 | 内容 |
|---|---|
| workflows.md | 分步实现模式 |
| decision-tree.md | 详细决策流程图 |
| examples.md | 完整可运行示例 |
| anti-patterns.md | 需避免的常见错误 |
See Also
相关参考
- - Exact syntax reference
erpnext-syntax-scheduler - - Error handling patterns
erpnext-errors-serverscripts
- - 精确语法参考
erpnext-syntax-scheduler - - 错误处理模式
erpnext-errors-serverscripts