server-scripts
Frappe server-side Python patterns for controllers, document events, whitelisted APIs, background jobs, and database operations. Use when writing controller logic, creating APIs, handling document events, or processing data on the server.
What this skill does
# Frappe Server Scripts Reference
Complete reference for server-side Python development in Frappe Framework.
## When to Use This Skill
- Writing document controllers
- Creating whitelisted API endpoints
- Handling document lifecycle events
- Background job processing
- Database operations and queries
- Permission checks and validation
- Email and notification handling
## Controller Location
```
my_app/
└── my_module/
└── doctype/
└── my_doctype/
└── my_doctype.py # Python controller
```
## Document Controller
### Complete Controller Template
```python
# my_doctype.py
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import nowdate, nowtime, flt, cint, getdate, add_days
class MyDocType(Document):
# ===== NAMING =====
def autoname(self):
"""Custom naming logic"""
self.name = f"{self.prefix}-{frappe.generate_hash()[:8].upper()}"
def before_naming(self):
"""Called before autoname"""
pass
# ===== VALIDATION =====
def before_validate(self):
"""Called before validate"""
self.set_defaults()
def validate(self):
"""Main validation - called on insert and update"""
self.validate_dates()
self.validate_amounts()
self.calculate_totals()
self.set_status()
def before_save(self):
"""Called after validate, before database write"""
self.update_modified_info()
# ===== INSERT =====
def before_insert(self):
"""Called before new document is inserted"""
self.set_initial_values()
def after_insert(self):
"""Called after new document is inserted"""
self.create_related_documents()
self.send_notification()
# ===== UPDATE =====
def on_update(self):
"""Called after document is saved (insert or update)"""
self.update_related_documents()
self.clear_cache()
def after_save(self):
"""Called after on_update, always runs"""
pass
def on_change(self):
"""Called when document changes in database"""
pass
# ===== SUBMISSION =====
def before_submit(self):
"""Called before document is submitted"""
self.validate_for_submit()
def on_submit(self):
"""Called after document is submitted"""
self.create_gl_entries()
self.update_stock()
def on_update_after_submit(self):
"""Called when submitted doc is updated (limited fields)"""
pass
# ===== CANCELLATION =====
def before_cancel(self):
"""Called before document is cancelled"""
self.validate_cancellation()
def on_cancel(self):
"""Called after document is cancelled"""
self.reverse_gl_entries()
self.reverse_stock()
# ===== DELETION =====
def before_delete(self):
"""Called before document is deleted"""
self.check_dependencies()
def after_delete(self):
"""Called after document is deleted"""
self.cleanup_related()
def on_trash(self):
"""Called when document is trashed"""
pass
def after_restore(self):
"""Called after document is restored from trash"""
pass
# ===== CUSTOM METHODS =====
def set_defaults(self):
"""Set default values"""
if not self.posting_date:
self.posting_date = nowdate()
if not self.company:
self.company = frappe.defaults.get_user_default("Company")
def validate_dates(self):
"""Validate date fields"""
if self.end_date and getdate(self.start_date) > getdate(self.end_date):
frappe.throw(_("End Date cannot be before Start Date"))
if getdate(self.posting_date) > getdate(nowdate()):
frappe.throw(_("Posting Date cannot be in the future"))
def validate_amounts(self):
"""Validate amount fields"""
for item in self.items:
if flt(item.qty) <= 0:
frappe.throw(_("Row {0}: Quantity must be greater than 0").format(item.idx))
if flt(item.rate) < 0:
frappe.throw(_("Row {0}: Rate cannot be negative").format(item.idx))
def calculate_totals(self):
"""Calculate document totals"""
self.total = 0
for item in self.items:
item.amount = flt(item.qty) * flt(item.rate)
self.total += item.amount
self.tax_amount = flt(self.total) * flt(self.tax_rate) / 100
self.grand_total = flt(self.total) + flt(self.tax_amount)
def set_status(self):
"""Set document status based on state"""
if self.docstatus == 0:
self.status = "Draft"
elif self.docstatus == 1:
if self.is_completed():
self.status = "Completed"
else:
self.status = "Submitted"
elif self.docstatus == 2:
self.status = "Cancelled"
def is_completed(self):
"""Check if document is completed"""
return all(item.delivered_qty >= item.qty for item in self.items)
```
## Whitelisted APIs
### Basic API
```python
@frappe.whitelist()
def get_customer_details(customer):
"""Get customer details
Args:
customer (str): Customer ID
Returns:
dict: Customer details with outstanding amount
"""
if not customer:
frappe.throw(_("Customer is required"))
doc = frappe.get_doc("Customer", customer)
return {
"customer_name": doc.customer_name,
"customer_type": doc.customer_type,
"territory": doc.territory,
"credit_limit": flt(doc.credit_limit),
"outstanding": get_customer_outstanding(customer)
}
@frappe.whitelist()
def create_invoice(customer, items):
"""Create sales invoice from data
Args:
customer (str): Customer ID
items (str): JSON string of items
Returns:
str: Invoice name
"""
items = frappe.parse_json(items)
doc = frappe.get_doc({
"doctype": "Sales Invoice",
"customer": customer,
"items": [{
"item_code": item.get("item_code"),
"qty": flt(item.get("qty")),
"rate": flt(item.get("rate"))
} for item in items]
})
doc.insert()
doc.submit()
return doc.name
```
### Guest API
```python
@frappe.whitelist(allow_guest=True)
def get_public_data():
"""Public API - no login required"""
return {
"status": "ok",
"message": "This is public data"
}
```
### Method-Restricted API
```python
@frappe.whitelist(methods=["POST"])
def create_record(data):
"""Only accepts POST requests"""
data = frappe.parse_json(data)
doc = frappe.get_doc(data)
doc.insert()
return {"name": doc.name}
@frappe.whitelist(methods=["GET", "POST"])
def flexible_endpoint(**kwargs):
"""Accepts GET and POST"""
return kwargs
```
### Permission-Checked API
```python
@frappe.whitelist()
def sensitive_operation(doctype, name):
"""API with permission check"""
# Check permission
if not frappe.has_permission(doctype, "write", name):
frappe.throw(_("Not permitted"), frappe.PermissionError)
# Proceed with operation
doc = frappe.get_doc(doctype, name)
# ... do something
return {"status": "success"}
```
## Database Operations
### Reading Data
```python
# Get single document
doc = frappe.get_doc("Customer", "CUST-001")
# Get with filters
doc = frappe.get_doc("Customer", {"customer_name": "John Corp"})
# Get single value
name = frappe.db.get_value("Customer", "CUST-001", "customer_name")
# Get multiple values
values = frappe.db.get_value("Customer", "CUST-001",
["customer_name", "territory"], as_dict=True)
# Get list
customers = frappe.db.get_all("Customer",
filters={"status": "Active"},
fields=["name", "customer_name", "territory"],
order_by="customer_name asc",
limit=10
)
# Complex filters
invoicRelated in Writing & Docs
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.