frappe-agent-architect
Use when designing multi-app Frappe architectures, deciding whether to split functionality into separate apps, or implementing cross-app communication…
Use when configuring scheduler events and background jobs in Frappe/ERPNext v14/v15/v16. Covers scheduler_events in hooks.py, frappe.enqueue() for async jobs, queue configuration, job deduplication, error handling, and monitoring. Keywords: scheduler, background job, cron, RQ
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-syntax-scheduler --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/frappe-syntax-schedulerContext preview
The summary Claude sees to decide when to auto-load this skill.
Use when configuring scheduler events and background jobs in Frappe/ERPNext v14/v15/v16. Covers scheduler_events in hooks.py, frappe.enqueue() for async jobs, queue configuration, job deduplication, error handling, and monitoring. Keywords: scheduler, background job, cron, RQ
name: frappe-syntax-scheduler description: > Use when configuring scheduler events and background jobs in Frappe/ERPNext v14/v15/v16. Covers scheduler_events in hooks.py, frappe.enqueue() for async jobs, queue configuration, job deduplication, error handling, and monitoring. Keywords: scheduler, background job, cron, RQ worker, job queue, async task, frappe.enqueue, scheduled task, cron syntax, how often does it run, background job example, enqueue example. license: MIT compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16." metadata: author: OpenAEC-Foundation version: "2.0"
Deterministic syntax reference for Frappe scheduler events and background job processing via Redis Queue (RQ).
Need periodic execution? ├─ Fixed interval (hourly/daily/weekly/monthly) → scheduler_events in hooks.py ├─ Custom cron schedule → scheduler_events.cron in hooks.py ├─ User-configurable interval → Scheduled Job Type DocType └─ No, triggered by user/event ├─ Run method on a specific document → frappe.enqueue_doc() ├─ Run standalone function async → frappe.enqueue() └─ Run from controller on self → self.queue_action()
# hooks.py — ALWAYS run bench migrate after changes
scheduler_events = {
# Standard events (default queue)
"all": ["myapp.tasks.every_tick"], # Every tick [v14: 240s, v15+: 60s]
"hourly": ["myapp.tasks.hourly_task"],
"daily": ["myapp.tasks.daily_task"],
"weekly": ["myapp.tasks.weekly_task"],
"monthly": ["myapp.tasks.monthly_task"],
# Long queue events (for heavy processing)
"hourly_long": ["myapp.tasks.hourly_heavy"],
"daily_long": ["myapp.tasks.daily_heavy"],
"weekly_long": ["myapp.tasks.weekly_heavy"],
"monthly_long": ["myapp.tasks.monthly_heavy"],
# Cron events (croniter-compatible syntax)
"cron": {
"*/15 * * * *": ["myapp.tasks.every_15_min"],
"0 9 * * 1-5": ["myapp.tasks.weekday_9am"],
"0 0 1 * *": ["myapp.tasks.first_of_month"],
}
}**CRITICAL**: ALWAYS run `bench migrate` after ANY change to scheduler_events. Without it, changes are NOT applied.
| Event | Frequency | Queue | Use Case | |-------|-----------|-------|----------| | `all` | Every tick [v14: 4min, v15+: 60s] | default | Frequent polling | | `hourly` | Once per hour | default | Sync, cleanup | | `daily` | Once per day | default | Reports, summaries | | `weekly` | Once per week | default | Archival | | `monthly` | Once per month | default | Billing, statements | | `hourly_long` | Once per hour | **long** | Heavy sync | | `daily_long` | Once per day | **long** | Large exports | | `weekly_long` | Once per week | **long** | Data warehousing | | `monthly_long` | Once per month | **long** | Annual reports | | `cron` | Custom schedule | configurable | Any custom timing |
┌───────────── minute (0-59) │ ┌───────────── hour (0-23) │ │ ┌───────────── day of month (1-31) │ │ │ ┌───────────── month (1-12) │ │ │ │ ┌───────────── day of week (0-6, Sunday=0) │ │ │ │ │ * * * * *
| Symbol | Meaning | Example | |--------|---------|---------| | `*` | Any value | `* * * * *` = every minute | | `,` | List | `1,15 * * * *` = minute 1 and 15 | | `-` | Range | `0 9-17 * * *` = hours 9 through 17 | | `/` | Interval | `*/10 * * * *` = every 10 minutes |
Common patterns:
frappe.enqueue(
method, # REQUIRED: function or "dotted.module.path"
queue="default", # "short", "default", "long", or custom
timeout=None, # Override queue timeout (seconds)
is_async=True, # False = run synchronously (skip worker)
now=False, # True = run via frappe.call() directly
job_id=None, # [v15+] Unique ID for deduplication
enqueue_after_commit=False, # Wait for DB commit before enqueue
at_front=False, # Place at front of queue
on_success=None, # Success callback
on_failure=None, # Failure callback
**kwargs # Arguments passed to method
)| Queue | Default Timeout | Use When | |-------|-----------------|----------| | `short` | 300s (5 min) | Task < 30 seconds | | `default` | 300s (5 min) | Task 30s - 5 min | | `long` | 1500s (25 min) | Task 5 - 25 min | | `long` + custom timeout | user-defined | Task > 25 min |
# Short queue — quick status update
frappe.enqueue("myapp.tasks.update_status", queue="short", doc=doc.name)
# Long queue — heavy report generation
frappe.enqueue("myapp.tasks.generate_report", queue="long", timeout=3600)Enqueue a controller method on a specific document.
frappe.enqueue_doc(
"Sales Invoice", # DocType
"SINV-00001", # Document name
"send_notification", # Controller method name
queue="long",
timeout=600,
recipient="user@example.com" # kwargs passed to method
)The controller method MUST be decorated with `@frappe.whitelist()`:
class SalesInvoice(Document):
@frappe.whitelist()
def send_notification(self, recipient):
# self is the loaded document
passAlternative from within a controller:
class SalesOrder(Document):
def on_submit(self):
self.queue_action("send_emails", emails=email_list)
def send_emails(self, emails):
for email in emails:
send_mail(email)from frappe.utils.background_jobs import is_job_enqueued
job_id = f"import::{doc.name}"
if n60 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations
Repo: Impertio-Studio/Frappe_Claude_Skill_Package
Use when designing multi-app Frappe architectures, deciding whether to split functionality into separate apps, or implementing cross-app communication…
Use when debugging Frappe errors, using bench console for live inspection, analyzing tracebacks, or reading Frappe log files. Prevents wasted debugging time…
Use when receiving vague or unclear ERPNext/Frappe development requests that need interpretation. Transforms requirements like 'make invoice auto-calculate' or…
Use when migrating a Frappe app between major versions, detecting breaking API changes, or resolving post-migration errors. Prevents failed migrations from…
Use when reviewing or validating Frappe/ERPNext code against best practices and common pitfalls. Checks generated code before deployment, validates against all…
Use when building ERPNext/Frappe API integrations (v14/v15/v16) including REST API, RPC API, authentication, webhooks, and rate limiting. Covers external API…