NYC
skills/smithery/ai/erpnext-syntax-serverscripts

erpnext-syntax-serverscripts

SKILL.md

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

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
Weekly Installs
1
Repository
smithery/ai
First Seen
Feb 5, 2026
Installed on
replit1
amp1
opencode1
kimi-cli1
codex1
github-copilot1