Documentation Index Fetch the complete documentation index at: https://mintlify.com/frappe/erpnext/llms.txt
Use this file to discover all available pages before exploring further.
ERPNext provides comprehensive automation tools to streamline business operations, reduce manual work, and ensure consistency across processes.
Workflow Automation
Define multi-step approval processes with custom rules and conditions.
Creating Workflows
Define Workflow
Navigate to Setup > Workflow > Workflow workflow = frappe.get_doc({
"doctype" : "Workflow" ,
"workflow_name" : "Sales Order Approval" ,
"document_type" : "Sales Order" ,
"is_active" : 1 ,
"workflow_state_field" : "workflow_state"
})
Add States
Define the stages your document goes through:
Draft → Pending Approval → Approved → Rejected
Each state can have different allowed roles
Set colors for visual indication
Configure Transitions
Define allowed transitions between states: transition = {
"state" : "Draft" ,
"action" : "Submit for Approval" ,
"next_state" : "Pending Approval" ,
"allowed" : "Sales User" ,
"condition" : "doc.grand_total > 10000"
}
Set Conditions
Add business rules: # Condition examples
"doc.grand_total > 100000" # Amount threshold
"doc.customer_group == 'VIP'" # Customer category
"doc.territory in ['North', 'South']" # Geographic rules
Workflow Actions
Approval Route documents through approval hierarchy based on amount, department, or custom criteria.
Rejection Send back to previous state with comments and reasons.
Conditional Routing Automatic routing based on field values or calculations.
Parallel Approval Multiple approvers can review simultaneously.
Scheduled Events
Automate recurring tasks using the scheduler:
Hourly Tasks
Daily Tasks
Cron Expressions
# hooks.py - Register scheduled events
scheduler_events = {
"hourly" : [
"erpnext.projects.doctype.project.project.hourly_reminder"
],
"hourly_long" : [
"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries"
],
"hourly_maintenance" : [
"erpnext.utilities.bulk_transaction.retry" ,
"erpnext.projects.doctype.project.project.collect_project_status"
]
}
Common Scheduled Tasks
Automatically create Purchase Requests when inventory falls below reorder level: def reorder_item ():
items = frappe.db.sql( """
SELECT item_code, warehouse, reorder_level, reorder_qty
FROM `tabItem Reorder`
WHERE enabled = 1
""" , as_dict = 1 )
for item in items:
current_qty = get_stock_qty(item.item_code, item.warehouse)
if current_qty < item.reorder_level:
create_material_request(item)
Send automated reminders for overdue invoices: def send_payment_reminders ():
overdue_invoices = frappe.db.sql( """
SELECT name, customer, due_date, outstanding_amount
FROM `tabSales Invoice`
WHERE docstatus = 1
AND outstanding_amount > 0
AND due_date < CURDATE()
""" , as_dict = 1 )
for invoice in overdue_invoices:
send_reminder_email(invoice)
Archive old records and clean up temporary data: def cleanup_old_logs ():
# Delete old activity logs
frappe.db.sql( """
DELETE FROM `tabActivity Log`
WHERE creation < DATE_SUB(NOW(), INTERVAL 90 DAY)
""" )
# Archive old documents
old_docs = frappe.get_all(
"Sales Order" ,
filters = { "creation" : [ "<" , "2020-01-01" ]},
pluck = "name"
)
for doc in old_docs:
archive_document( "Sales Order" , doc)
Email Automation
Email Alerts
Trigger emails based on document events:
# Setup > Email > Email Alert
email_alert = frappe.get_doc({
"doctype" : "Email Alert" ,
"subject" : "New Sales Order: {{ doc.name }} " ,
"document_type" : "Sales Order" ,
"event" : "Submit" ,
"send_to_all_assignees" : 1 ,
"recipients" : [
{ "email_by_document_field" : "sales_manager_email" }
],
"condition" : "doc.grand_total > 50000" ,
"message" : """
<p>A new high-value order has been placed:</p>
<ul>
<li>Customer: {{ doc.customer }} </li>
<li>Amount: {{ doc.grand_total }} </li>
<li>Delivery Date: {{ doc.delivery_date }} </li>
</ul>
"""
})
email_alert.insert()
Trigger Events:
New document created
Document saved
Document submitted
Document cancelled
Value changed (specific field)
Days before/after date field
Custom conditions
Email Campaigns
Automated marketing campaigns:
Campaign Setup
Unsubscribe Handling
# CRM > Email Campaign
campaign = frappe.get_doc({
"doctype" : "Email Campaign" ,
"name" : "Lead Nurture Series" ,
"campaign_name" : "Product Launch" ,
"email_campaign_for" : "Lead" ,
"sender" : "sales@company.com" ,
"status" : "Active"
})
# Add email schedules
campaign.append( "campaign_schedules" , {
"send_after_days" : 0 ,
"email_template" : "Welcome Email"
})
campaign.append( "campaign_schedules" , {
"send_after_days" : 3 ,
"email_template" : "Feature Introduction"
})
campaign.append( "campaign_schedules" , {
"send_after_days" : 7 ,
"email_template" : "Special Offer"
})
campaign.insert()
Auto-Repeat (Subscriptions)
Automate recurring transactions:
# Create auto-repeat for monthly invoices
auto_repeat = frappe.get_doc({
"doctype" : "Auto Repeat" ,
"reference_doctype" : "Sales Invoice" ,
"reference_document" : "SINV-00001" ,
"frequency" : "Monthly" ,
"start_date" : "2024-01-01" ,
"end_date" : "2024-12-31" ,
"repeat_on_day" : 1 , # 1st of each month
"notify_by_email" : 1 ,
"recipients" : "customer@example.com" ,
"submit_on_creation" : 1
})
auto_repeat.insert()
Use Cases:
Monthly subscriptions
Recurring rentals
Annual maintenance contracts
Periodic purchase orders
Regular salary payments
Document Events & Hooks
Trigger custom code on document lifecycle events:
Standard Events
Custom Validation
Field Updates
# hooks.py
doc_events = {
"Sales Invoice" : {
"before_insert" : "your_app.custom.validate_customer" ,
"after_insert" : "your_app.custom.notify_sales_team" ,
"before_save" : "your_app.custom.calculate_commission" ,
"on_submit" : "your_app.custom.update_inventory" ,
"on_cancel" : "your_app.custom.reverse_inventory" ,
"on_trash" : "your_app.custom.cleanup_related_docs"
}
}
Assignment Automation
Auto-assign documents to users:
# Setup > Automation > Assignment Rule
assignment_rule = frappe.get_doc({
"doctype" : "Assignment Rule" ,
"name" : "Auto Assign Leads" ,
"document_type" : "Lead" ,
"assign_condition" : "doc.status == 'Open'" ,
"rule" : "Round Robin" , # or "Load Balancing", "Based on Field"
"users" : [
{ "user" : "sales1@company.com" },
{ "user" : "sales2@company.com" },
{ "user" : "sales3@company.com" }
]
})
assignment_rule.insert()
Assignment Strategies:
Round Robin : Distribute evenly in rotation
Load Balancing : Assign to user with fewest open documents
Based on Field : Assign based on territory, department, etc.
Priority Based : Assign high-priority items to specific users
Notification Automation
System Notifications In-app notifications for document assignments, mentions, and updates.
SMS Alerts Send SMS for critical events like payment confirmations.
Push Notifications Mobile app notifications for mobile users.
Slack/Teams Integration with team collaboration tools.
Bank Reconciliation Automation
Automatic matching of bank transactions:
# accounts/doctype/bank_transaction/auto_match_party.py
def auto_match_party ():
# Get unreconciled transactions
transactions = frappe.get_all(
"Bank Transaction" ,
filters = { "status" : "Unreconciled" },
fields = [ "name" , "description" , "withdrawal" , "deposit" ]
)
for transaction in transactions:
# Try to match with invoices
matches = find_matching_invoices(transaction)
if len (matches) == 1 :
# Auto-reconcile if single match
reconcile_transaction(transaction, matches[ 0 ])
API Webhooks
Trigger external systems when documents change:
# Setup > Integrations > Webhook
webhook = frappe.get_doc({
"doctype" : "Webhook" ,
"webhook_doctype" : "Sales Order" ,
"webhook_docevent" : "on_submit" ,
"request_url" : "https://api.external-system.com/order-created" ,
"request_method" : "POST" ,
"request_structure" : "Form URL-Encoded" ,
"webhook_headers" : [
{ "key" : "Authorization" , "value" : "Bearer TOKEN" },
{ "key" : "Content-Type" , "value" : "application/json" }
],
"webhook_data" : [
{ "fieldname" : "name" , "key" : "order_id" },
{ "fieldname" : "customer" , "key" : "customer_name" },
{ "fieldname" : "grand_total" , "key" : "total_amount" }
]
})
webhook.insert()
Server Scripts
Write custom automation without deploying apps:
// Setup > Automation > Server Script
// Script Type: DocType Event
// DocType: Sales Order
// Event: Before Save
if ( doc . customer ) {
let customer = frappe . get_doc ( "Customer" , doc . customer );
// Apply customer-specific discount
if ( customer . discount_percentage ) {
doc . additional_discount_percentage = customer . discount_percentage ;
}
// Set payment terms
if ( customer . payment_terms ) {
doc . payment_terms_template = customer . payment_terms ;
}
}
Best Practices
Automation Guidelines
Test automations in staging before production
Use error handling and logging
Avoid infinite loops in document events
Set appropriate permissions for automated actions
Monitor automated jobs for failures
Use queues for long-running tasks
Document all automation rules
Performance
Use background jobs for bulk operations
Implement rate limiting
Cache frequently accessed data
Optimize database queries
Reliability
Add retry logic with exponential backoff
Log all automation activities
Send failure notifications
Use dead letter queues
Next Steps
Reporting Automate report generation and distribution
Integrations Connect with external systems