Frappe Client Scriptjavascript

How to Fetch and Populate a Child Table from Another DocType in Frappe?

Fetch Table All Rows

Learn 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.

javascript
1// Generic Example: Fetch Table on link field Event
2frappe.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
15frappe.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
28frappe.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
44frappe.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
61function 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 more

Loading 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 more

Populating 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 more

Refreshing 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 more

Step-by-Step Tutorial

Follow along to understand how this code works

1

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').

javascript
frappe.ui.form.on("Purchase Order", "for_so", function(frm) {
  // Your code will go here
});
Next Step
2

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.

javascript
frm.clear_table("items");
Next Step
3

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.

javascript
frappe.model.with_doc("Sales Order", frm.doc.for_so, function() {
  // Logic after document is loaded
});
Next Step
4

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.

javascript
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;
});
Next Step
5

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.

javascript
frm.refresh_field("items");

Common Issues & Solutions

Troubleshoot problems you might encounter