Skip to content
AI & Agents
Skill

/frappe-impl-serverscripts

Use when implementing server-side features via Setup > Server Script: document validation, auto-fill, API endpoints, scheduled tasks, permission queries. Covers sandbox-safe coding, script type selection, testing, migration to controllers. Keywords: how to implement server

From plugin
frappe-claude-skill-package
17861 skills
Install
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-impl-serverscripts --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/frappe-impl-serverscripts

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when implementing server-side features via Setup > Server Script: document validation, auto-fill, API endpoints, scheduled tasks, permission queries. Covers sandbox-safe coding, script type selection, testing, migration to controllers. Keywords: how to implement server

SKILL.md

frappe-impl-serverscripts.SKILL.md
name: frappe-impl-serverscripts
description: >
  Use when implementing server-side features via Setup > Server Script:
  document validation, auto-fill, API endpoints, scheduled tasks,
  permission queries. Covers sandbox-safe coding, script type selection,
  testing, migration to controllers. Keywords: how to implement server
  script, which script type, sandbox limitation, Document Event, API
  script, Scheduler Event, Permission Query, migrate to controller,
  no-code automation, run code on save, auto-fill field, server-side validation, scheduled script.
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
  author: OpenAEC-Foundation
  version: "2.0"

Server Scripts — Implementation Workflows

Step-by-step workflows for building server-side features without a custom app. For exact syntax, see `frappe-syntax-serverscripts`.

**Version**: v14/v15/v16 | **v15+ Note**: Server Scripts disabled by default — enable with `bench set-config server_script_enabled true`

CRITICAL: Sandbox Limitations

ALL IMPORTS BLOCKED — RestrictedPython sandbox
  import json          → ImportError: __import__ not found
  from frappe.utils    → ImportError
  import requests      → ImportError

SOLUTION: Use pre-loaded namespace:
  frappe.utils.nowdate()        frappe.utils.flt()
  frappe.parse_json(data)       json.loads() (json IS available)
  frappe.as_json(obj)           json.dumps()
  frappe.make_get_request(url)  (replaces requests.get)

**Rule**: If you need `import` statements beyond `json`, ALWAYS use a Controller instead.

Workflow 1: Create a Server Script

1. Enable server scripts: `bench set-config server_script_enabled true` 2. Navigate to **Setup > Server Script** (or awesomebar: "New Server Script") 3. Select **Script Type** (see decision tree below) 4. Configure type-specific settings (DocType, event, API method, cron) 5. Write script in the editor 6. Save — script is active immediately 7. Test by triggering the configured event 8. Use "Compare Versions" button to diff changes

Workflow 2: Choose the Script Type

WHAT DO YOU NEED?
│
├── React to document save/submit/cancel?
│   └── Document Event
│       └── Select DocType + Event (Before Save, After Save, etc.)
│
├── Create a REST API endpoint?
│   └── API
│       └── Set method name + guest access setting
│       └── Endpoint: /api/method/{method_name}
│
├── Run task on schedule (daily/hourly/cron)?
│   └── Scheduler Event
│       └── Set cron pattern or frequency
│
└── Filter list views per user/role?
    └── Permission Query
        └── Select DocType — set `conditions` variable

> See [references/decision-tree.md](references/decision-tree.md) for complete decision tree.

Workflow 3: Document Event: Validation

**Goal**: Validate Sales Order before save.

**Step 1**: Choose event — "Before Save" maps to `validate` hook.

**Step 2**: Write sandbox-safe script:

# Type: Document Event | Event: Before Save | DocType: Sales Order

errors = []

if not doc.customer:
    errors.append("Customer is required")

if doc.delivery_date and doc.delivery_date < frappe.utils.today():
    errors.append("Delivery date cannot be in the past")

for item in doc.items:
    if item.qty <= 0:
        errors.append(f"Row {item.idx}: Quantity must be positive")

if errors:
    frappe.throw("<br>".join(errors), title="Validation Error")

**Rules**:

  • ALWAYS collect errors and throw once (better UX than multiple throws)
  • NEVER call `doc.save()` in Before Save — framework handles it
  • ALWAYS use `frappe.throw()` — `msgprint` does NOT stop save

Workflow 4: Document Event: Auto-Calculate

**Goal**: Auto-calculate totals and set derived fields.

# Type: Document Event | Event: Before Save | DocType: Purchase Order

doc.total_qty = sum(item.qty or 0 for item in doc.items)
doc.total_amount = sum((item.qty or 0) * (item.rate or 0) for item in doc.items)

if doc.total_amount > 50000:
    doc.requires_approval = 1
    doc.approval_status = "Pending"

if doc.supplier and not doc.supplier_name:
    doc.supplier_name = frappe.db.get_value("Supplier", doc.supplier, "supplier_name")

**Rule**: ALWAYS modify `doc` fields directly in Before Save — they are automatically persisted.

Workflow 5: Document Event: Create Related Document

**Goal**: Create a ToDo when a new Lead is inserted.

# Type: Document Event | Event: After Insert | DocType: Lead

frappe.get_doc({
    "doctype": "ToDo",
    "allocated_to": doc.lead_owner or doc.owner,
    "reference_type": "Lead",
    "reference_name": doc.name,
    "description": f"Follow up with new lead: {doc.lead_name}",
    "date": frappe.utils.add_days(frappe.utils.today(), 1),
    "priority": "High" if doc.status == "Hot" else "Medium"
}).insert(ignore_permissions=True)

**Rules**:

  • ALWAYS use After Insert or After Save for creating related docs
  • NEVER create documents in Before Save — `doc.name` may not exist yet
  • ALWAYS use `ignore_permissions=True` for system-generated documents

Workflow 6: API Endpoint

**Goal**: Create authenticated REST API returning customer data.

# Type: API | Method: get_customer_dashboard | Allow Guest: No
# Endpoint: /api/method/get_customer_dashboard

customer = frappe.form_dict.get("customer")
if not customer:
    frappe.throw("Parameter 'customer' is required")

# ALWAYS check permissions
if not frappe.has_permission("Customer", "read", customer):
    frappe.throw("Access denied", frappe.PermissionError)

orders = frappe.db.count("Sales Order", {"customer": customer, "docstatus": 1})
revenue = frappe.db.get_value("Sales Invoice",
    filters={"customer": customer, "docstatus": 1},
    fieldname="sum(grand_total)") or 0

frappe.response["message"] = {
    "customer": customer,
    "total_orders": orders,
    "total_revenue": revenue
}

**Rules**:

  • ALWAYS validate input parameters
  • ALWAYS check permissions (even with Allow Guest: No)
  • ALWAYS cap query limi
Read more
Ships withfrappe-claude-skill-package

60 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations

Get the whole plugin
Stats
178
Stars
53
Forks
Maintained
Maintenance
Python
Language
2mo ago
Last commit
8mo ago
Created
14d ago
Added

Repo: Impertio-Studio/Frappe_Claude_Skill_Package