ERPNext Server Scripts Syntax
Server Scripts are Python scripts that run within Frappe's secure sandbox environment. They are managed via Setup → Server Script in the ERPNext UI.
CRITICAL: Sandbox Limitations
┌─────────────────────────────────────────────────────────────────────┐ │ ⚠️ NO IMPORTS ALLOWED │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ The sandbox blocks ALL import statements: │ │ import json → ImportError: import not found │ │ from datetime import date → ImportError │ │ │ │ SOLUTION: Use Frappe's pre-loaded namespace: │ │ frappe.utils.nowdate() not: from frappe.utils import nowdate │ │ frappe.parse_json(data) not: import json; json.loads(data) │ │ │ └─────────────────────────────────────────────────────────────────────┘
Server Script Types
Type Usage Trigger
Document Event React to document lifecycle Save, Submit, Cancel, etc.
API Custom REST endpoint HTTP request to /api/method/{name}
Scheduler Event Scheduled tasks Cron schedule
Permission Query Dynamic list filtering Document list view
Event Name Mapping (Document Event)
IMPORTANT: The UI event names differ from the internal hook names:
UI Name (Server Script) Internal Hook When
Before Insert before_insert
Before new doc to DB
After Insert after_insert
After new doc saved
Before Validate before_validate
Before validation
Before Save validate
Before save (new or update)
After Save on_update
After successful save
Before Submit before_submit
Before submit
After Submit on_submit
After submit
Before Cancel before_cancel
Before cancel
After Cancel on_cancel
After cancel
Before Delete on_trash
Before delete
After Delete after_delete
After delete
Quick Reference: Available API
Always available in sandbox
Document object (in Document Event scripts)
doc # Current document doc.name # Document name doc.doctype # DocType name doc.fieldname # Field value doc.get("fieldname") # Safe field access doc.items # Child table (list)
Frappe namespace
frappe.db # Database operations frappe.get_doc() # Fetch document frappe.get_all() # Multiple documents frappe.throw() # Validation error frappe.msgprint() # User message frappe.log_error() # Error logging frappe.utils.* # Utility functions frappe.session.user # Current user frappe.form_dict # Request parameters (API) frappe.response # Response object (API)
Decision Tree: Which Script Type?
What do you want to achieve? │ ├─► React to document save/submit/cancel? │ └─► Document Event script │ ├─► Create REST API endpoint? │ └─► API script │ ├─► Run task on schedule? │ └─► Scheduler Event script │ └─► Filter document list view per user/role? └─► Permission Query script
Basic Syntax per Type
Document Event
Configuration:
Reference DocType: Sales Invoice
DocType Event: Before Save (= validate)
if doc.grand_total < 0: frappe.throw("Total cannot be negative")
if doc.grand_total > 10000: doc.requires_approval = 1
API
Configuration:
API Method: get_customer_info
Allow Guest: No
Endpoint: /api/method/get_customer_info
customer = frappe.form_dict.get("customer") if not customer: frappe.throw("Customer parameter required")
data = frappe.get_all( "Sales Order", filters={"customer": customer, "docstatus": 1}, fields=["name", "grand_total"], limit=10 ) frappe.response["message"] = data
Scheduler Event
Configuration:
Event Frequency: Cron
Cron Format: 0 9 * * * (daily at 9:00)
overdue = frappe.get_all( "Sales Invoice", filters={"status": "Unpaid", "due_date": ["<", frappe.utils.today()]}, fields=["name", "customer"] )
for inv in overdue: frappe.log_error(f"Overdue: {inv.name}", "Invoice Reminder")
frappe.db.commit()
Permission Query
Configuration:
Reference DocType: Sales Invoice
Output: conditions string for WHERE clause
user_roles = frappe.get_roles(user)
if "System Manager" in user_roles:
conditions = "" # Full access
elif "Sales User" in user_roles:
conditions = f"tabSales Invoice.owner = {frappe.db.escape(user)}"
else:
conditions = "1=0" # No access
References
-
references/events.md - Complete event mapping and execution order
-
references/methods.md - All available frappe.* methods in sandbox
-
references/examples.md - 10+ working examples per script type
-
references/anti-patterns.md - Sandbox limitations and common mistakes
Version Information
-
Frappe v14+: Server Scripts fully supported
-
Activation required: bench --site [site] set-config server_script_enabled true
-
Frappe v15: No significant syntax changes for Server Scripts