Get the FREE Ultimate OpenClaw Setup Guide →

erpnext-impl-scheduler

npx machina-cli add skill OpenAEC-Foundation/ERPNext_Anthropic_Claude_Development_Skill_Package/erpnext-impl-scheduler --openclaw
Files (1)
SKILL.md
7.2 KB

ERPNext 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

AspectScheduler Eventfrappe.enqueue
Triggered byTime/intervalCode execution
Defined inhooks.pyPython code
ArgumentsNone (must be parameterless)Any serializable data
Use caseDaily cleanup, hourly syncUser-triggered long task
Restart behaviorRuns on scheduleLost 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?

QueueTimeoutUse For
short5 minQuick operations (<1 min)
default5 minStandard tasks (1-3 min)
long30 minHeavy 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

Aspectv14v15v16
Tick interval4 min60 sec60 sec
Job dedupjob_namejob_idjob_id
Cron support

V14 deduplication uses different parameter:

# v14
enqueue(..., job_name="unique_id")
# v15+
enqueue(..., job_id="unique_id")

Reference Files

FileContents
workflows.mdStep-by-step implementation patterns
decision-tree.mdDetailed decision flowcharts
examples.mdComplete working examples
anti-patterns.mdCommon mistakes to avoid

See Also

  • erpnext-syntax-scheduler - Exact syntax reference
  • erpnext-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

  1. Step 1: Create a parameterless Python function (e.g., daily_cleanup) in your app's tasks.py
  2. Step 2: Wire the function into hooks.py under scheduler_events with the desired cadence (cron/hourly/daily)
  3. 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

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers