Frappe Client Scriptjavascript

How to Validate All Rows in a Child Table in Frappe Framework?

Validate on Loop

Learn how to implement custom validation logic for child table rows in Frappe ERPNext using a Client Script. This snippet demonstrates iterating through items with $.each.

In Frappe and ERPNext, ensuring data integrity before saving a document is crucial. The `validate` trigger in a Client Script provides the perfect hook to perform complex checks, especially on child table data.

This snippet showcases two common scenarios: first, ensuring all items in a Quotation's child table have a consistent quantity for specific Item Groups, and second, verifying that all items in a Material Request are fully billed. Both examples leverage a loop to check each row individually.

javascript
1// Validate on Loop
2frappe.ui.form.on("Quotation", "validate", function(frm) {
3 if (frm.doc.items[0].item_group == "Call Center Outsourcing" || frm.doc.items[0].item_group == "IT Outsourcing") {
4 $.each(frm.doc.items || [], function(i, d) {
5 if (d.qty != frm.doc.items[0].qty) {
6 msgprint("Quantity for each Item must be same for Item group Call Center Outsourcing & IT Outsourcing");
7 validated = false;
8 }
9 });
10 }
11});
12
13frappe.ui.form.on("Material Request", "validate", function(frm) {
14 $.each(frm.doc.items || [], function(i, d) {
15 if (d.billing_status != "Fully Billed") {
16 msgprint("Sales Order Billing Status should be Fully Billed for generating Material Request");
17 validated = false;
18 }
19 });
20});

Understanding This Code

What It Does

This Client Script iterates through all rows of a child table (named 'items') upon form validation to enforce custom business logic across multiple rows.

When To Use

Use this script during the 'validate' lifecycle event of a DocType. This event fires just before the document is saved, making it the ideal place to prevent submissions with invalid data.

Prerequisites

  • Basic understanding of Frappe DocTypes and Client Scripting.
  • A DocType with a child table (e.g., Quotation with 'items', Material Request with 'items').

Key Concepts

Important ideas to understand in this code

frappe.ui.form.on()

A fundamental Frappe API for creating client-side event listeners. It hooks into form lifecycle events like 'onload', 'refresh', and 'validate' to trigger custom JavaScript code.

Learn more

The 'validate' Event

This client-side event is triggered when a user saves or submits a document. It's the last point of intervention before data is sent to the server, making it perfect for validation checks that can prevent the save action.

Learn more

frm.doc Object

The 'frm.doc' object represents the current document's data in the form. You can access any field, including child tables (which are arrays), using this object (e.g., frm.doc.items).

Learn more

Child Table Iteration

Child tables are stored as arrays within the 'frm.doc' object. The snippet uses jQuery's '$.each()' to loop through each row ('d') and its index ('i') in the 'frm.doc.items' array to perform checks.

Learn more

validated = false;

In a 'validate' event handler, setting the global 'validated' variable to 'false' stops the form submission process. This is the standard mechanism to halt a save/submit action if validation fails.

Learn more

Step-by-Step Tutorial

Follow along to understand how this code works

1

Create or Navigate to your Client Script

From the Awesome Bar, search for 'Client Script' and click 'New'. This will open a new Client Script form.

bash
// No code for this step. Navigate in the UI.
Next Step
2

Select the Target DocType

In the 'DocType' field of your new Client Script, select the DocType you want to apply this validation to, such as 'Quotation' or 'Material Request'.

bash
// Example: Set 'Applies To' to 'DocType' and 'DocType' to 'Quotation'.
Next Step
3

Hook into the 'validate' Event

Use the 'frappe.ui.form.on' method to specify that your code should run only when the 'validate' event occurs for the selected DocType.

javascript
frappe.ui.form.on("Quotation", "validate", function(frm) {
    // Your validation logic goes here
});
Next Step
4

Implement the Loop and Validation Logic

Inside the function, use '$.each(frm.doc.items, function(i, d) { ... })' to iterate through each row of your child table. 'items' is the fieldname of the child table.

javascript
$.each(frm.doc.items || [], function(i, d) {
    // 'd' represents the current row object
    if (d.qty != frm.doc.items[0].qty) { 
        // Condition failed
    }
});
Next Step
5

Display an Error and Halt Submission

If a row fails your condition, use 'msgprint()' to show an error to the user and, most importantly, set 'validated = false;' to prevent the document from being saved.

javascript
if (d.qty != frm.doc.items[0].qty) {
    msgprint("Quantity for each Item must be the same.");
    validated = false;
}
Next Step
6

Save and Enable the Client Script

Ensure the 'Enabled' checkbox is checked, then save your Client Script. Clear your browser cache (Ctrl+Shift+R) and test the validation by creating or editing a document of the target DocType.

bash
// No code for this step. Save and reload.

Common Issues & Solutions

Troubleshoot problems you might encounter