How to Validate All Rows in a Child Table in Frappe Framework?
Validate on LoopLearn 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.
1 // Validate on Loop 2 frappe.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
13 frappe.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 moreThe '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 morefrm.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 moreChild 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 morevalidated = 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 moreStep-by-Step Tutorial
Follow along to understand how this code works
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.
// No code for this step. Navigate in the UI.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'.
// Example: Set 'Applies To' to 'DocType' and 'DocType' to 'Quotation'.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.
frappe.ui.form.on("Quotation", "validate", function(frm) {
// Your validation logic goes here
});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.
$.each(frm.doc.items || [], function(i, d) {
// 'd' represents the current row object
if (d.qty != frm.doc.items[0].qty) {
// Condition failed
}
});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.
if (d.qty != frm.doc.items[0].qty) {
msgprint("Quantity for each Item must be the same.");
validated = false;
}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.
// No code for this step. Save and reload.Common Issues & Solutions
Troubleshoot problems you might encounter