When ERPNext client scripts are enough for form logic
A team that needs a form to react to user input, such as filtering a linked field by company or showing a button only when a document is submitted, can usually solve it with ERPNext client scripts. A client script is JavaScript that runs in the browser when a user opens or edits a form. It is the right tool for behaviour that exists only to help a person fill in a document: hiding or showing fields, setting default values in the open form, narrowing the options in a link field, confirming a value before save, and adding custom buttons. Client scripts are written in the Desk interface and attached to a DocType, so they need no custom app and no change to ERPNext's own code. Confirm the exact event names and helper functions for your version in the Frappe Framework documentation before you write the first script, because the API differs between major releases.
The boundary is the important decision. A client script only runs when a person is using the form in a browser. Data created through the REST API, the Data Import tool, a bench console, or another integration bypasses it entirely. So any rule that must hold for every record, such as a credit limit check, a mandatory tax field for a given territory, or a status transition that affects stock or accounting, belongs in a server-side mechanism. In ERPNext that is usually a server script, a controller override in a custom app, or a validation the data model enforces. Treat a client script as a usability layer on top of server-side validation, never as the only control. If you find yourself writing a client script to block a bad value, ask what happens when the same record arrives by import, and move that check to the server.
Before you commit to client scripts, list each piece of form logic and mark whether it is a convenience or a control. Convenience logic is safe in a client script and easy to change later. Control logic needs a server-side check plus, optionally, a client-side hint for faster feedback. Ask your implementation partner or internal ERPNext administrator to confirm which events and settings apply on your version, and test each rule with an import or API call as well as with a browser session. Scripts that depend on the Desk environment also need to be stored so they survive an upgrade or move between sites, which is covered in the version control section of this guide.
Which form changes fit a client script and which need a server-side or app solution
Filtering link fields and showing or hiding fields based on other values are the most common reasons to write a client script. These changes only affect what a user sees and can select in the form, so the browser is the right place for them. A client script can narrow the options a link field offers, for example limiting a Customer list to a company, and can toggle a section when a checkbox changes. Nothing is enforced here, so the filter does not stop data entered through the API, the Data Import tool or another script.
- Good fit: hiding or showing fields, setting read-only state, and narrowing link queries for the person using the form.
- Not enough alone: if a value must be valid for every record, add a server-side validation so that imports and API writes are checked too.
- Data Import tool: imported rows do not run form-level client logic, so confirm how the import path handles the same fields.
- Version differences: confirm the exact filter and visibility hooks available in your ERPNext version and whether they behave the same on mobile views.
- Testing: check the form on each role that will see it, because permissions can hide fields that the script tries to set.
Before you rely on a filter, list every path that can create or change the field, not only the desk form.
Custom buttons that open a dialog, copy values, or call a helper usually fit a client script when the action is a convenience for the user. A button that calls a server method through the framework is a common pattern, and the work runs on the server even though the trigger is in the browser. The distinction matters: a button that only fills in fields on the screen is a client concern, while a button that changes stock, posts a ledger entry, or sends a document needs server-side logic that checks permissions and state.
- Client-only actions: opening a dialog, filling fields, or navigating to another document are safe to keep in the browser.
- Server-triggered actions: any action that writes data should call a server method or script so that the rules run regardless of how the user reached the form.
- Permission checks: a hidden button is not a permission control. Confirm that the server method checks the user's rights before acting.
- Duplicate clicks: confirm how your version handles repeated clicks on long-running actions.
- Scripts disabled: on sites where server scripts are disabled, plan for the server method to live in a custom app.
For each button, write down who may press it and what the server does if the request arrives without the button.
A client script can compute a value and show it while the user edits the form, such as a running total or a preview of tax before save. This is useful for feedback, but the displayed number is not the stored number. ERPNext's own calculation logic runs when the document is saved, and any figure a client script shows must match it. If your team copies totals into fields by hand in the browser, the saved value can differ when the document is created by another path.
- Preview only: showing an estimate in a read-only field or dialog is a reasonable client use.
- Stored totals: values that feed accounting, tax, or stock should be computed by the standard document logic or a server-side rule.
- Rounding and currency: confirm the rounding and precision settings your version applies, and test with the currencies you use.
- Child table changes: confirm that recalculation fires when rows are added, edited, or deleted, not only on the parent field.
- Reconciliation: compare the browser preview with the saved document for a sample of transactions before go-live.
If a user could act on a displayed number and the saved number would differ, move the calculation to the server.
Some rules must hold no matter how a record is written: a required approval before submission, a field that may not be changed after a status, or a restriction on which accounts a cost center can use. These belong in server-side logic, either a server script on document events or a custom app with validation hooks. A client script cannot protect these rules because it runs only in a browser session and can be skipped through the API, the Data Import tool, background jobs, or a second client.
- Server validation: place mandatory business rules in server-side validation that runs on save and submit.
- Server scripts: on version 15 and later, server scripts are disabled by default and must be enabled with the
server_script_enabledsetting. Frappe Cloud public shared benches do not allow them, so confirm your hosting model first. - Custom app: when rules are complex, need tests, or must survive upgrades, move them into a custom app.
- Write paths to test: desk form, API, Data Import, web forms, and scheduled jobs each need a test case.
- Audit needs: if a regulator or auditor expects evidence of a control, record how the rule is enforced server-side and who owns the code.
Confirm with your own environment and version which write paths exist, because a rule that covers only the desk form is not a control for the whole system.
Client script configuration options and when each fits
This table compares the main choices when configuring an ERPNext client script: the DocType it attaches to, the form events it hooks, its scope, and whether it is enabled. Read each row as a decision, then check the trade-off column before deciding where the logic should live. The main takeaway is that a client script shapes the browser experience but does not enforce rules on its own.
| Configuration choice | When it fits | What it trades away |
|---|---|---|
| DocType the script attaches to | Standard forms where users enter data, such as Sales Invoice or Item, and where the change is purely about layout or guidance | Changes apply only where the form loads; a custom DocType needs its own script, so keep a list of every attached DocType |
| Refresh event handler | Adding custom buttons, hiding sections or setting read-only state based on the document's current status | Runs each time the form renders, so heavy calls here slow every open; keep logic light and check status fields first |
| Field change handler | Filtering linked fields, auto-filling a value when a user selects a customer or item, or recalculating a total in the form | Fires only when a user edits the field in the browser, so values set by import, API or another script will not trigger it |
| Form-level scope | Behaviour that depends on several fields or the document as a whole, such as a warning when a combination of fields is invalid | More code to review and test per form; changes to the form layout can break logic that reads field names |
| Field-level scope | A single field needs a filter or default that is easy to name and reuse across the same form | Several field-level handlers on one form can interact in ways that are hard to trace; document their order |
| Enabled state | Turning a script on for testing or a rollout, or keeping it in the database while you decide whether to move it into an app | A disabled script is easy to forget; record who disabled it and why, and confirm it is off on each site |
| Browser-only enforcement | User guidance, helpful defaults and convenience buttons that do not change what is allowed to be saved | Users with API access, data import or a different client can bypass the script, so validation must also exist server side |
| Logic that must hold for every write | Not a good fit for a client script; move it to a server script, a custom app or the standard validation path | Server-side logic takes more effort to build and test, and server scripts depend on the enabled setting described in the Frappe documentation |
How to build and check a client script in ERPNext
This sequence takes a client script from a confirmed target form to a tested, version-controlled change that behaves the same for every role that uses the form. Each step ends with a check so problems surface on a test site rather than in production.
Confirm the target DocType and version
Identify the exact DocType, such as Sales Invoice or Purchase Order, and the ERPNext major version running on the test site. Client scripts attach to a DocType, and form behaviour can differ between major versions, so record both before writing anything. Ask the administrator to confirm which version is in production and whether the site is self-hosted or on Frappe Cloud. Done when you can state the DocType, the version and the site name in one line.
Identify the form events the logic needs
List the moments the logic must react to, such as the form loading, a field value changing, a row being added to a child table, or a button being needed. Map each requirement to a form event and write down which fields it reads and which it sets. Keep the list short, because every extra handler adds behaviour that must be tested later. Done when each rule in the requirement has one named event and one named field.
Write and save the script on a test site
Create the Client Script record on a test site, select the DocType, choose the script type (Form or List view), and write the handlers. Save the record and reload the form, since changes may not appear until the page reloads. Use the same field names the form shows in its Customize Form view, not labels. Done when the form loads without a visible error and the new behaviour appears on a sample record.
Verify filters and buttons for each role
Log in as each role that uses the form and repeat the same actions. For a field filter, confirm that a restricted role sees only the records it is allowed to see in the linked field, and that an unrestricted role still sees the full list. For a custom button, confirm it appears only for the intended roles and that clicking it produces the expected change. Done when each role's result matches the requirement and you have noted any role that behaves differently.
Check the browser console for errors
Open the browser developer tools on each form path you tested and read the console for JavaScript errors and failed requests. Repeat the check in at least two browsers that your users run, and note any browser-specific differences in layout or button behaviour. Errors that do not block the screen can still stop a later handler from running, so do not skip this check. Done when the console shows no errors on the tested paths, or each remaining error has been traced and explained.
Confirm behaviour survives a reload and an upgrade test
Create, save, submit and amend a test record, then reload the page and confirm the script still runs at each stage. Where the site allows it, repeat the test on a copy of the site after a minor update, since a change in the framework or in the form can break handlers that worked before. Done when the behaviour holds across the document lifecycle and the copy-after-update test produces no new errors.
Move the script into version control or a custom app
Export the Client Script record so the code lives in version control rather than only in the database, either through the customization export workflow or by placing it in a custom app. Record who owns the change, the version it was tested on and the roles it was verified for, and deploy it to production through the same path as other changes. Done when the script can be restored from the repository to a fresh test site and passes the role checks from the earlier steps.
Common client script patterns: field filters and custom buttons
The two patterns teams request most from client scripts are restricting the options of a linked field based on another field, and adding a custom button that either calls a server method or opens a dialog. Both depend on the DocType's field names, its link targets and the role permissions of the person using the form. A field filter needs the controlling field to exist on the same DocType, and the filtered link field must point to a DocType the user can read. A custom button needs a clear condition for when it appears, such as a document status or a docstatus value, so that it is not offered on documents where the action makes no sense. For each button, confirm which server method it calls, whether that method checks permissions itself, and which roles can reach it.
A filter set in the browser only narrows what the form displays. It does not stop a user with API access, a data import or another script from saving a value outside that list. Any rule that matters for accounting, stock or approvals belongs in a permission rule, a validation on the server, or a user or role permission, and the client script should only make the correct path easier to follow. The same applies to buttons: hiding a button from one role does not block that role from calling the underlying method. Test each pattern with a user who holds the minimum roles, and confirm that the server rejects the action when it is called directly.
Before building either pattern, list the linked fields and buttons the business needs, then check each one against the role matrix in the ERPNext roles and permissions setup. Ask the team that owns the process to confirm which values are valid in each case, because a filter that is too narrow can block legitimate invoices and orders. Keep a short test script for each form that covers the filtered and unfiltered cases, and rerun it after every ERPNext minor release, since form behaviour can change between versions.
How to test client scripts across browsers and roles
Client scripts run in the browser, so their behaviour depends on the browser, the user's role, the form state and the ERPNext version underneath them. The checks below cover the risks that most often make a script work for the developer and fail for everyone else.
Desk UI browser differences
Client scripts change Desk forms through the frappe.ui and form APIs, and rendering can differ between current Chrome, Edge, Firefox and Safari. Test each script in every browser your users run, on the same form, with the same data. Check that dropdown filters open, custom buttons appear in the Actions or Create menus where expected, and dialogs can be dismissed with the keyboard. Record the browser version alongside each result so a later failure can be traced.
Cache and stale script after edits
After you change a client script, a browser may keep serving the old version, and the Desk may show behaviour from before the edit. Hard-refresh the page, clear site cache with the bench clear-cache command on your test site, and reload in a private window before you judge a fix. Confirm the saved script is the one running by adding a temporary, clearly named log line. Remove it before promotion so production logs stay clean.
Role-specific visibility
A script that hides a field or button for one role can leave another role with an empty or broken form. Log in as each role that touches the DocType, including users with restricted user permissions and those limited to a single company. Confirm that fields set read-only or hidden by the script match what the role permissions allow, and that the server still rejects changes the browser would block. Do not treat a hidden button as access control.
Narrow screens and mobile layouts
Desk forms reflow on tablets and phones, and custom buttons or dialogs added by a script can move, overlap or fall off screen. Test the forms your field staff use on a phone-sized viewport in browser developer tools and on a real device if one is available. Check that custom dialogs remain scrollable, that the primary action stays reachable, and that filters on link fields still open when the keyboard is visible.
Console errors and event order
Scripts that fail silently often leave a half-applied form state. Open the browser console on each test run and look for uncaught exceptions, failed network calls and warnings from the form lifecycle, such as code that reads a field before refresh has completed. Confirm that handlers for refresh, validate and field change run in the order your logic assumes, and that a failed server call does not leave buttons or fields in a stuck state.
Regression after minor releases
ERPNext ships frequent minor releases on both supported major versions, and a change in a standard form's JavaScript can break a script that depended on its internals. Keep a written test list for each script, run it on a staging copy of production after every minor upgrade, and compare the results with the previous run. Pin the ERPNext version you tested against in your change record, and check the release notes for changes to the forms your scripts touch.
Questions teams ask before building client scripts
How much effort does a simple client script take compared with a custom app?
A simple client script is usually a small, contained change: a few lines of JavaScript attached to one DocType, written and tested in the browser. A custom app is a larger undertaking because it adds a repository, a release process, deployment steps and ongoing maintenance. What drives the gap is the number of forms affected, whether the logic depends on server data, how many roles see the form, and whether the change must ship to several sites. Ask a partner to split the quote into logic written, forms covered, test cases and handover so you can compare options on the same basis.
Can our own team build ERPNext client scripts without a partner?
Yes, many teams do, provided someone understands the ERPNext form model and can read JavaScript. Useful skills include knowing how DocTypes, fields and child tables behave, working with the browser developer tools, and reading the Frappe client-side API. Someone must also own the work after go-live, because scripts change when fields are renamed or workflows change. If your team lacks these skills, a short scoped engagement can transfer knowledge while building the first scripts, which is often cheaper to plan than a long dependency on an outside team.
How long does testing client scripts take, and what does it depend on?
Testing time depends on how many forms and user roles are affected, how many states a document can pass through, and how many browsers and devices your users rely on. A script that only hides a field for one role needs far fewer cases than one that filters links, sets defaults and changes buttons across submitted and cancelled documents. Plan for a test pass in each supported browser, a regression check of the forms the script touches, and a retest after each ERPNext minor release. Ask the partner to list test cases in writing before work starts.
What do we need to provide for client script work?
The work moves faster when you provide a clear description of each rule, a sample of realistic records, and a role matrix showing who creates, edits, submits and cancels each document. You should also name a business owner who signs off each behaviour and gives an opinion on edge cases, such as partially received orders or foreign currency documents. Provide a test site with a copy of data that resembles production, and confirm who can approve changes. Without these inputs, testing tends to expand and sign-off takes longer.
Do client scripts survive ERPNext upgrades?
Client scripts are stored as records in the database and are generally not overwritten by an upgrade, but they can break when the underlying form, field names or client-side API change between versions. Treat each upgrade as a test event: run the scripts on a staging copy of the site, check the forms they touch, and review release notes for changes to the affected DocTypes. Plan for version control of script source, since database-only records are harder to review and move between environments. Confirm the specific upgrade path for your ERPNext version with your partner or the release notes before you commit to a schedule.
When should client script logic move to a server script or custom app?
Move logic to the server when a rule must hold regardless of how a document is entered, such as stock checks, calculations that must be auditable, or anything that depends on data a browser should not see. Client scripts only improve the experience in the form; they do not enforce rules for data imported, created through the API or changed by other scripts. Server scripts are disabled by default from version 15 and must be enabled with server_script_enabled, and they are not available on Frappe Cloud public shared benches. Decide early which rules are guidance and which are controls, because controls belong on the server.
Do client scripts help with compliance or access control?
Client scripts can hide or lock fields for usability, but they are not an access control mechanism, because a user can still reach data through other screens, reports or the API. Access rules belong in ERPNext role and permission settings, which your auditors will examine. If you rely on scripts for a control, document the expected behaviour, test it per role, and keep evidence of each test run. DeployOpen prepares systems, controls and evidence for compliance assessments, but independent auditors and certification bodies issue the actual assessments.
How DeployOpen scopes and delivers ERPNext client script work
DeployOpen plans, implements, tests, documents and supports ERPNext client scripts as part of a self-hosted ERPNext deployment in infrastructure you control. Each engagement starts from a written scope that names the forms, fields and users the scripts affect before any browser-side code is written.
How we scope
- Inventory every form where client script logic is requested, with the DocTypes, fields and roles involved.
- Decide with you whether a client script, a custom field, a property setter or a server-side change fits each case.
- Agree browsers, ERPNext versions and test users in writing before work starts, since engagements are fixed-scope.
How we build
- Write client scripts against the form events and field filters your team has approved, keeping logic readable and documented.
- Version scripts in the same change process as other customizations so each edit can be reviewed and reverted.
- Test field filters, custom buttons and validation across the browsers your staff use before anything reaches production.
How we hand over
- Deliver a written record of each script's trigger, purpose, affected DocTypes and known limits.
- Walk administrators through how to review, enable and disable scripts, and where changes are logged.
- Document the test cases used so your team can repeat them after an ERPNext upgrade or a browser change.
How we support
- Offer ongoing support, upgrades and continuous testing as a retainer for scripts that need regular attention.
- Re-test client scripts after each ERPNext minor or major release that touches the forms they change.
- Confirm with your team which support tier, response expectations and change window apply before signing.

