erpnext-impl-scheduler
npx machina-cli add skill OpenAEC-Foundation/ERPNext_Anthropic_Claude_Development_Skill_Package/erpnext-impl-scheduler --openclawERPNext Scheduler - Implementation
This skill helps you implement scheduled tasks and background jobs. For exact syntax, see erpnext-syntax-scheduler.
Version: v14/v15/v16 compatible
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 │
│ │
└─────────────────────────────────────────────────────────────────────┘
Scheduler Event 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 |
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!) │
│ │
└─────────────────────────────────────────────────────────────────────┘
Which Queue?
| Queue | Timeout | Use For |
|---|---|---|
short | 5 min | Quick operations (<1 min) |
default | 5 min | Standard tasks (1-3 min) |
long | 30 min | Heavy processing (>3 min) |
Rule: Always specify queue explicitly. Default is short.
Quick Start: Basic Scheduled Task
# 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()
# hooks.py
scheduler_events = {
"daily": [
"myapp.tasks.daily_cleanup"
]
}
After editing hooks.py: bench migrate
Quick Start: Background Job
# 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"}
Critical Rules
1. Scheduler tasks receive NO arguments
# ❌ WRONG
def my_task(doctype): # Arguments not supported
pass
# ✅ CORRECT
def my_task(): # Parameterless
doctype = "Sales Invoice" # Hardcode or read from settings
2. ALWAYS migrate after hooks.py changes
bench migrate # Required to register new scheduler events
3. Jobs run as Administrator
Scheduler and enqueued jobs run with Administrator permissions. Always commit explicitly.
4. Commit after batches, not per record
# ❌ WRONG - Slow
for doc in docs:
doc.save()
frappe.db.commit() # Commit per record
# ✅ CORRECT - Fast
for doc in docs:
doc.save()
frappe.db.commit() # Single commit after batch
5. Use job_id for deduplication (v15+)
enqueue(..., job_id="unique_identifier") # Prevents duplicate jobs
Version Differences
| Aspect | v14 | v15 | v16 |
|---|---|---|---|
| Tick interval | 4 min | 60 sec | 60 sec |
| Job dedup | job_name | job_id | job_id |
| Cron support | ✅ | ✅ | ✅ |
V14 deduplication uses different parameter:
# v14
enqueue(..., job_name="unique_id")
# v15+
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 |
See Also
erpnext-syntax-scheduler- Exact syntax referenceerpnext-errors-serverscripts- Error handling patterns
Source
git clone https://github.com/OpenAEC-Foundation/ERPNext_Anthropic_Claude_Development_Skill_Package/blob/main/skills/source/impl/erpnext-impl-scheduler/SKILL.mdView on GitHub Overview
This skill covers implementing ERPNext scheduled tasks and background jobs across v14/v15/v16. It focuses on hooks.py scheduler_events, frappe.enqueue, queue selection, job deduplication, and robust error handling to ensure reliable asynchronous processing.
How This Skill Works
Choose between time-based scheduler events defined in hooks.py or on-demand tasks via frappe.enqueue. Scheduler events run parameterless tasks at a defined cadence, while frappe.enqueue allows passing serializable data for background processing. Explicit queue selection (short, default, long) and deduplication help prevent overlaps, with error handling to manage retries and failures.
When to Use It
- Daily cleanup or data synchronization on a fixed schedule
- User-triggered or on-demand long-running tasks
- Complex schedules using cron expressions (e.g., weekdays at 9am)
- Background processing that benefits from explicit queue control
- Tasks requiring deduplication and robust error handling
Quick Start
- Step 1: Create a parameterless Python function (e.g., daily_cleanup) in your app's tasks.py
- Step 2: Wire the function into hooks.py under scheduler_events with the desired cadence (cron/hourly/daily)
- Step 3: For on-demand work, enqueue tasks with frappe.enqueue and explicitly set queue; test the schedule and monitor logs
Best Practices
- Always specify the queue explicitly when enqueuing tasks; avoid relying on defaults
- Use scheduler events for recurring housekeeping tasks and cron-based scheduling for flexibility
- Choose appropriate queue (short, default, long) based on task duration and impact
- Implement deduplication to prevent overlapping runs of the same job
- Add solid error handling with retries, logging, and alerting
Example Use Cases
- Daily error log cleanup and data pruning
- Nightly inventory synchronization with an external API
- Weekly performance reports generation and delivery
- Asynchronous image or file processing queued in the background
- Long-running data migration or ETL tasks scheduled during off-peak hours