How to Fetch and Populate a Child Table from Another DocType in Frappe?
Fetch Table All RowsLearn to use Frappe Client Scripts to dynamically fetch data from a source DocType's child table and populate a target child table upon a trigger event.
In Frappe and ERPNext development, a common requirement is to automate data entry by pulling information from a related document. This is especially true for child tables (also known as Table MultiSelect fields), where you might need to copy line items from one DocType to another, for example, from a Sales Order to a Purchase Order.
This collection of client scripts demonstrates the standard pattern for achieving this. By listening to an event, such as a Link Field change or a button click, the script fetches the source document, iterates through its child table, and programmatically adds new rows to the current form's child table, mapping the data as needed.
1 // Generic Example: Fetch Table on link field Event 2 frappe.ui.form.on("DocTypeB", "Trigger", function(frm) { 3 frappe.model.with_doc("DocTypeA", frm.doc.trigger, function() { 4 var qmtable= frappe.model.get_doc("DocTypeA", frm.doc.Trigger) 5 $.each(qmtable.ChildTableA, function(index, row){ 6 d = frm.add_child("ChildTableB"); 7 d.field1 = row.fielda; 8 d.field2 = row.fieldb; 9 cur_frm.refresh_field("ChildTableB"); 10 }) 11 }); 12 }); 13
14 // Example 1: Populate Maintenance Visit parts from Serial No master 15 frappe.ui.form.on("Maintenance Visit", "serial_no", function(frm) { 16 frappe.model.with_doc("Serial No", frm.doc.serial_no, function() { 17 var qmtable= frappe.model.get_doc("Serial No", frm.doc.serial_no) 18 $.each(qmtable.machine_parts, function(index, row){ 19 d = frm.add_child("parts_replaced"); 20 d.part_name = row.part_name; 21 d.serial_no = row.serial_no; 22 cur_frm.refresh_field("parts_replaced"); 23 }) 24 }); 25 }); 26
27 // Example 2: Clear table before fetching from Sales Order to Purchase Order 28 frappe.ui.form.on("Purchase Order", "for_so", function(frm) { 29 frm.clear_table("items"); 30 frappe.model.with_doc("Sales Order", frm.doc.for_so, function() { 31 var qmtable= frappe.model.get_doc("Sales Order", frm.doc.for_so) 32 $.each(qmtable.items, function(index, row){ 33 d = frm.add_child("items"); 34 d.item_code= row.item_code; 35 d.qty = row.qty; 36 d.warehouse= row.warehouse; 37 d.description = row.description; 38 cur_frm.refresh_field("items"); 39 }) 40 }); 41 }); 42
43 // Example 3: On a button click, populate transport details from linked Delivery Notes 44 frappe.ui.form.on("Sales Invoice", "get_transport_details", function(frm) { 45 $.each(frm.doc.items, function(n, m){ 46 if (m.delivery_note){ 47 frappe.model.with_doc("Delivery Note", m.delivery_note, function() { 48 var qmtable= frappe.model.get_doc("Delivery Note", m.delivery_note) 49 $.each(qmtable.transport, function(index, row){ 50 d = frm.add_child("transport"); 51 d.truck_no = row.truck_no; 52 //... map other fields 53 cur_frm.refresh_field("transport"); 54 }) 55 }); 56 } 57 }); 58 }); 59
60 // Example 4: Running serially for better performance 61 function get_qty(frm) { 62 frappe.model.with_doc("Outward Sample", frm.doc.sample_no, function() { 63 frappe.run_serially([ 64 () => { 65 let os_doc = frappe.model.get_doc("Outward Sample", frm.doc.sample_no) 66 $.each(os_doc.details, function(index, row){ 67 let d = frm.add_child("items"); 68 // ... map fields 69 }) 70 }, 71 () => { 72 frm.refresh_field("items"); // Refresh only once after the loop 73 }, 74 ]) 75 }); 76 }
Understanding This Code
What It Does
This script dynamically fetches all rows from a child table in a source DocType and populates them into a child table in the current form. This is triggered by an event, such as changing a Link Field value or clicking a custom button.
When To Use
Use this script when you need to automate data entry by copying related child table data from one document to another. It's ideal for onload, refresh, or field-specific change events (e.g., `frm.doc.my_link_field`).
Prerequisites
- •Basic understanding of Frappe DocTypes and Child Tables.
- •Familiarity with Frappe Client Scripting events.
Key Concepts
Important ideas to understand in this code
Form Event Listener: frappe.ui.form.on
This is the primary function for attaching event handlers to form fields or the form itself. It allows you to trigger custom logic when a user interacts with a field, like changing its value or clicking a button.
Learn moreLoading Documents: frappe.model.with_doc
A crucial API call that pre-loads a document from the server into the client's memory. This ensures the document's data is available for use by 'frappe.model.get_doc' without an additional server trip.
Learn morePopulating Child Tables: frm.add_child
This function creates a new row in a specified child table field. You can then access this new row object to set its field values before refreshing the grid.
Learn moreRefreshing UI: frm.refresh_field
After programmatically changing data in a child table, this function must be called to re-render the grid on the user interface and display the new rows.
Learn moreStep-by-Step Tutorial
Follow along to understand how this code works
Define the Event Trigger
First, create a new Client Script for the target DocType (e.g., 'Purchase Order'). Use `frappe.ui.form.on` to listen for changes to the Link Field that connects to the source DocType (e.g., 'for_so' which links to 'Sales Order').
frappe.ui.form.on("Purchase Order", "for_so", function(frm) {
// Your code will go here
});Clear the Target Child Table
Inside the event handler, it's best practice to clear any existing rows from the target child table to avoid duplicates. Use `frm.clear_table('items');` where 'items' is the fieldname of your child table.
frm.clear_table("items");Fetch the Source Document
Using `frappe.model.with_doc`, we load the selected source document from the server. This is an asynchronous operation, so the rest of our logic is placed inside its callback function.
frappe.model.with_doc("Sales Order", frm.doc.for_so, function() {
// Logic after document is loaded
});Iterate and Map Data
Inside the callback, get the document using `frappe.model.get_doc`. Then, use `$.each` to loop through the source child table. For each row, create a new row in the target child table using `frm.add_child` and map the corresponding fields.
var source_doc = frappe.model.get_doc("Sales Order", frm.doc.for_so);
$.each(source_doc.items, function(index, source_row) {
let new_row = frm.add_child("items");
new_row.item_code = source_row.item_code;
new_row.qty = source_row.qty;
new_row.description = source_row.description;
});Refresh the UI
Finally, after the loop has finished adding all the new rows, call `frm.refresh_field('items');` to update the form's UI and display the newly populated data in the child table grid.
frm.refresh_field("items");Common Issues & Solutions
Troubleshoot problems you might encounter