Skip to content
AI & Agents
Skill

/frappe-syntax-clientscripts

Use when writing client-side JavaScript for ERPNext/Frappe form events, field manipulation, server calls, or child table handling in v14/v15/v16. Covers exact syntax for frappe.ui.form.on, frm methods, frappe.call, and browser-side validation. Keywords: client script, form

From plugin
frappe-claude-skill-package
17861 skills
Install
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-syntax-clientscripts --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-syntax-clientscripts

Context preview

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

Use when writing client-side JavaScript for ERPNext/Frappe form events, field manipulation, server calls, or child table handling in v14/v15/v16. Covers exact syntax for frappe.ui.form.on, frm methods, frappe.call, and browser-side validation. Keywords: client script, form

SKILL.md

frappe-syntax-clientscripts.SKILL.md
name: frappe-syntax-clientscripts
description: >
  Use when writing client-side JavaScript for ERPNext/Frappe form events,
  field manipulation, server calls, or child table handling in v14/v15/v16.
  Covers exact syntax for frappe.ui.form.on, frm methods, frappe.call,
  and browser-side validation. Keywords: client script, form event, frm,
  frappe.call, frappe.ui.form.on, JavaScript, UI interaction, field validation,
  form event syntax, how to write client script, frm example, frappe.call example.
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
  author: OpenAEC-Foundation
  version: "2.0"

Frappe Client Scripts Syntax

Client Scripts run in the browser and control all UI interactions in Frappe/ERPNext. Create them via **Setup > Client Script** or in custom apps under `public/js/`.

**CRITICAL**: Client Script validations ONLY apply in the browser form view. API calls and System Console bypass them. ALWAYS pair with Server Scripts for security-critical validation.

Quick Reference

| Action | Code | |--------|------| | Set value | `frm.set_value('field', value)` | | Get value | `frm.doc.fieldname` | | Hide field | `frm.toggle_display('field', false)` | | Make mandatory | `frm.toggle_reqd('field', true)` | | Make read-only | `frm.toggle_enable('field', false)` | | Set field property | `frm.set_df_property('field', 'options', [...])` | | Filter Link field | `frm.set_query('field', () => ({filters: {}}))` | | Call server | `frappe.call({method: 'path.to.fn', args: {}})` | | Call doc method | `frm.call('method_name', {args})` | | Prevent save | `frappe.throw(__('Error message'))` | | Add button | `frm.add_custom_button(__('Label'), callback, group)` | | Add child row | `frm.add_child('table', {values}); frm.refresh_field('table')` | | Show alert | `frappe.show_alert({message: __('Done'), indicator: 'green'})` | | Translate string | `__('Text')` or `__('Hello {0}', [name])` |

Event Decision Tree

What do you need to do?
│
├─ One-time setup (queries, formatters)?
│  └─ ALWAYS use setup — runs once per form instance
│
├─ Show/hide fields, add buttons, update UI?
│  └─ ALWAYS use refresh — fires after every load/reload
│
├─ Validate data before save?
│  └─ ALWAYS use validate — use frappe.throw() to block save
│
├─ Modify data right before server save?
│  └─ Use before_save — last chance to change values
│
├─ Run logic after successful save?
│  └─ Use after_save — document is persisted
│
├─ React to a field value change?
│  └─ Use the fieldname as the event name
│
├─ Intercept workflow state change?
│  └─ Use before_workflow_action / after_workflow_action
│
└─ Manipulate DOM after full render?
   └─ Use onload_post_render — NEVER use jQuery selectors directly

> See [references/events.md](references/events.md) for complete event list and execution order.

Form Event Registration

// Parent form events
frappe.ui.form.on('Sales Order', {
    setup(frm) { },           // Once per form instance
    refresh(frm) { },         // After every load/reload
    validate(frm) { },        // Before save — throw to block
    fieldname(frm) { }        // On field value change
});

// Child table events — ALWAYS register on the CHILD doctype
frappe.ui.form.on('Sales Order Item', {
    qty(frm, cdt, cdn) {
        let row = frappe.get_doc(cdt, cdn);
        frappe.model.set_value(cdt, cdn, 'amount', row.qty * row.rate);
    },
    items_add(frm, cdt, cdn) { },     // Row added
    items_remove(frm) { },            // Row removed (no cdt/cdn)
    items_move(frm) { }               // Row reordered
});

Value Manipulation

// ALWAYS use frm.set_value() — NEVER assign frm.doc.field directly
frm.set_value('status', 'Approved');                        // Single
frm.set_value({status: 'Approved', priority: 'High'});      // Multiple

// Read values (read-only — NEVER write via frm.doc)
let val = frm.doc.fieldname;
let items = frm.doc.items;  // Child table array

Field Properties

// Show/hide (accepts single field or array)
frm.toggle_display(['priority', 'due_date'], frm.doc.status === 'Open');

// Mandatory toggle
frm.toggle_reqd('due_date', true);

// Read-only toggle
frm.toggle_enable('amount', false);  // false = read-only

// Arbitrary property change
frm.set_df_property('status', 'options', ['New', 'Open', 'Closed']);
frm.set_df_property('amount', 'read_only', 1);
frm.set_df_property('notes', 'label', 'Internal Notes');

// Intro message at form top
frm.set_intro('This document is pending review', 'orange');

Link Field Filters

// ALWAYS set queries in setup event — NEVER in refresh
frappe.ui.form.on('Sales Order', {
    setup(frm) {
        // Simple filter
        frm.set_query('customer', () => ({
            filters: { disabled: 0 }
        }));

        // Child table filter
        frm.set_query('item_code', 'items', (doc, cdt, cdn) => {
            let row = locals[cdt][cdn];
            return { filters: { is_sales_item: 1 } };
        });

        // Server-side query for complex logic
        frm.set_query('customer', () => ({
            query: 'myapp.queries.get_filtered_customers',
            filters: { region: frm.doc.region }
        }));
    }
});

Server Communication

// frappe.call — whitelisted Python method
let r = await frappe.call({
    method: 'myapp.api.process_data',
    args: { customer: frm.doc.customer },
    freeze: true,
    freeze_message: __('Processing...')
});
if (r.message) { /* use r.message */ }

// frm.call — document controller method
let result = await frm.call('calculate_taxes', { include_shipping: true });

// frappe.db shortcuts
let val = await frappe.db.get_value('Customer', name, 'credit_limit');
let list = await frappe.db.get_list('Sales Order', {
    filters: { customer: frm.doc.customer },
    fields: ['name', 'grand_total'],
    order_by: 'creation desc',
    limit: 10
});

##

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