From a436c6a503463dfe804056db61141b08079120c4 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 25 Nov 2025 12:16:48 +0530 Subject: [PATCH 1/5] refactor: creation of purchase order from sales order --- .../doctype/sales_order/sales_order.js | 77 ++++---- .../doctype/sales_order/sales_order.py | 171 +++--------------- 2 files changed, 56 insertions(+), 192 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 235838f8f11..5c2f3666b48 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -1584,12 +1584,6 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex title: __("Select Items"), size: "large", fields: [ - { - fieldtype: "Check", - label: __("Against Default Supplier"), - fieldname: "against_default_supplier", - default: 0, - }, { fieldname: "items_for_po", fieldtype: "Table", @@ -1625,10 +1619,11 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex in_list_view: 1, }, { - fieldtype: "Data", + fieldtype: "Link", fieldname: "supplier", label: __("Supplier"), - read_only: 1, + reqd: 1, + options: "Supplier", in_list_view: 1, }, ], @@ -1647,13 +1642,17 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex }); } - dialog.hide(); + if (selected_items.some((item) => !item.supplier)) { + frappe.throw({ + message: "Supplier is required for all selected Items", + title: __("Supplier Required"), + indicator: "blue", + }); + } - var method = args.against_default_supplier - ? "make_purchase_order_for_default_supplier" - : "make_purchase_order"; + dialog.hide(); return frappe.call({ - method: "erpnext.selling.doctype.sales_order.sales_order." + method, + method: "erpnext.selling.doctype.sales_order.sales_order.make_purchase_order", freeze_message: __("Creating Purchase Order ..."), args: { source_name: me.frm.doc.name, @@ -1662,9 +1661,9 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex freeze: true, callback: function (r) { if (!r.exc) { - if (!args.against_default_supplier) { - frappe.model.sync(r.message); - frappe.set_route("Form", r.message.doctype, r.message.name); + if (r.message.length == 1) { + frappe.model.sync(r.message[0]); + frappe.set_route("Form", r.message[0].doctype, r.message[0].name); } else { frappe.route_options = { sales_order: me.frm.doc.name, @@ -1677,37 +1676,25 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex }, }); - dialog.fields_dict["against_default_supplier"].df.onchange = () => set_po_items_data(dialog); - function set_po_items_data(dialog) { - var against_default_supplier = dialog.get_value("against_default_supplier"); - var items_for_po = dialog.get_value("items_for_po"); + let po_items = []; + me.frm.doc.items.forEach((d) => { + let ordered_qty = me.get_ordered_qty(d, me.frm.doc); + let pending_qty = (flt(d.stock_qty) - ordered_qty) / flt(d.conversion_factor); + if (pending_qty > 0) { + po_items.push({ + name: d.name, + item_name: d.item_name, + item_code: d.item_code, + pending_qty: pending_qty, + uom: d.uom, + supplier: d.supplier, + }); + } + }); - if (against_default_supplier) { - let items_with_supplier = items_for_po.filter((item) => item.supplier); - - dialog.fields_dict["items_for_po"].df.data = items_with_supplier; - dialog.get_field("items_for_po").refresh(); - } else { - let po_items = []; - me.frm.doc.items.forEach((d) => { - let ordered_qty = me.get_ordered_qty(d, me.frm.doc); - let pending_qty = (flt(d.stock_qty) - ordered_qty) / flt(d.conversion_factor); - if (pending_qty > 0) { - po_items.push({ - name: d.name, - item_name: d.item_name, - item_code: d.item_code, - pending_qty: pending_qty, - uom: d.uom, - supplier: d.supplier, - }); - } - }); - - dialog.fields_dict["items_for_po"].df.data = po_items; - dialog.get_field("items_for_po").refresh(); - } + dialog.fields_dict["items_for_po"].df.data = po_items; + dialog.get_field("items_for_po").refresh(); } set_po_items_data(dialog); diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 811c7d0c08c..b3174b2bffa 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1546,7 +1546,7 @@ def get_events(start, end, filters=None): @frappe.whitelist() -def make_purchase_order_for_default_supplier(source_name, selected_items=None, target_doc=None): +def make_purchase_order(source_name, selected_items=None, target_doc=None): """Creates Purchase Order for each Supplier. Returns a list of doc objects.""" from erpnext.setup.utils import get_exchange_rate @@ -1615,11 +1615,28 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t def update_item_for_packed_item(source, target, source_parent): target.qty = flt(source.qty) - flt(source.ordered_qty) + def filter_items(item, supplier): + if ( + item.ordered_qty < item.stock_qty + and item.item_code in item_codes + and not is_product_bundle(item.item_code) + ): + item_supp_comb = next(iter(d for d in items_to_map if d["item_code"] == item.item_code), None) + if item_supp_comb and item_supp_comb.get("supplier") == supplier: + items_to_map.remove(item_supp_comb) + return True + + return False + suppliers = [item.get("supplier") for item in selected_items if item.get("supplier")] suppliers = list(dict.fromkeys(suppliers)) # remove duplicates while preserving order - items_to_map = [item.get("item_code") for item in selected_items if item.get("item_code")] - items_to_map = list(set(items_to_map)) + item_codes = [item.get("item_code") for item in selected_items if item.get("item_code")] + items_to_map = [ + {"item_code": item.get("item_code"), "supplier": item.get("supplier")} + for item in selected_items + if item.get("item_code") + ] if not suppliers: frappe.throw(_("Please set a Supplier against the Items to be considered in the Purchase Order.")) @@ -1665,10 +1682,7 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t "margin_rate_or_amount", ], "postprocess": update_item, - "condition": lambda doc: doc.ordered_qty < doc.stock_qty - and doc.supplier == supplier - and doc.item_code in items_to_map - and not is_product_bundle(doc.item_code), + "condition": lambda doc: filter_items(doc, supplier), }, "Packed Item": { "doctype": "Purchase Order Item", @@ -1689,7 +1703,7 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t "pricing_rules", ], "postprocess": update_item_for_packed_item, - "condition": lambda doc: doc.parent_item in items_to_map + "condition": lambda doc: doc.parent_item in item_codes and flt(doc.ordered_qty) < flt(doc.qty), }, }, @@ -1698,150 +1712,13 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t ) set_delivery_date(doc.items, source_name) - doc.insert() - frappe.db.commit() + if len(suppliers) > 1: + doc.insert() purchase_orders.append(doc) return purchase_orders -@frappe.whitelist() -def make_purchase_order(source_name, selected_items=None, target_doc=None): - if not selected_items: - return - - if isinstance(selected_items, str): - selected_items = json.loads(selected_items) - - items_to_map = [item.get("item_code") for item in selected_items if item.get("item_code")] - items_to_map = list(set(items_to_map)) - - def is_drop_ship_order(target): - drop_ship = True - for item in target.items: - if not item.delivered_by_supplier: - drop_ship = False - break - - return drop_ship - - def set_missing_values(source, target): - target.supplier = "" - target.apply_discount_on = "" - target.additional_discount_percentage = 0.0 - target.discount_amount = 0.0 - target.inter_company_order_reference = "" - target.shipping_rule = "" - target.tc_name = "" - target.terms = "" - target.payment_terms_template = "" - target.payment_schedule = [] - - if is_drop_ship_order(target): - if source.shipping_address_name: - target.shipping_address = source.shipping_address_name - target.shipping_address_display = source.shipping_address - else: - target.shipping_address = source.customer_address - target.shipping_address_display = source.address_display - - target.customer_contact_person = source.contact_person - target.customer_contact_display = source.contact_display - target.customer_contact_mobile = source.contact_mobile - target.customer_contact_email = source.contact_email - else: - target.customer = target.customer_name = target.shipping_address = None - - target.run_method("set_missing_values") - if not target.taxes: - target.append_taxes_from_item_tax_template() - target.run_method("calculate_taxes_and_totals") - - def update_item(source, target, source_parent): - target.schedule_date = source.delivery_date - target.qty = flt(source.qty) - (flt(source.ordered_qty) / flt(source.conversion_factor)) - target.stock_qty = flt(source.stock_qty) - flt(source.ordered_qty) - target.project = source_parent.project - - def update_item_for_packed_item(source, target, source_parent): - target.qty = flt(source.qty) - flt(source.ordered_qty) - - # po = frappe.get_list("Purchase Order", filters={"sales_order":source_name, "supplier":supplier, "docstatus": ("<", "2")}) - doc = get_mapped_doc( - "Sales Order", - source_name, - { - "Sales Order": { - "doctype": "Purchase Order", - "field_map": {"dispatch_address_name": "dispatch_address"}, - "field_no_map": [ - "address_display", - "contact_display", - "contact_mobile", - "contact_email", - "contact_person", - "taxes_and_charges", - "shipping_address", - ], - "validation": {"docstatus": ["=", 1]}, - }, - "Sales Order Item": { - "doctype": "Purchase Order Item", - "field_map": [ - ["name", "sales_order_item"], - ["parent", "sales_order"], - ["stock_uom", "stock_uom"], - ["uom", "uom"], - ["conversion_factor", "conversion_factor"], - ["delivery_date", "schedule_date"], - ], - "field_no_map": [ - "rate", - "price_list_rate", - "item_tax_template", - "discount_percentage", - "discount_amount", - "supplier", - "pricing_rules", - ], - "postprocess": update_item, - "condition": lambda doc: doc.ordered_qty < doc.stock_qty - and doc.item_code in items_to_map - and not is_product_bundle(doc.item_code), - }, - "Packed Item": { - "doctype": "Purchase Order Item", - "field_map": [ - ["name", "sales_order_packed_item"], - ["parent", "sales_order"], - ["uom", "uom"], - ["conversion_factor", "conversion_factor"], - ["parent_item", "product_bundle"], - ["rate", "rate"], - ], - "field_no_map": [ - "price_list_rate", - "item_tax_template", - "discount_percentage", - "discount_amount", - "supplier", - "pricing_rules", - ], - "postprocess": update_item_for_packed_item, - "condition": lambda doc: doc.parent_item in items_to_map - and flt(doc.ordered_qty) < flt(doc.qty), - }, - }, - target_doc, - set_missing_values, - ) - - set_delivery_date(doc.items, source_name) - doc.set_onload("load_after_mapping", False) - - return doc - - def set_delivery_date(items, sales_order): delivery_dates = frappe.get_all( "Sales Order Item", filters={"parent": sales_order}, fields=["delivery_date", "item_code"] From c93dba289581456ab4537454d00492ef2bedb92c Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 25 Nov 2025 12:33:51 +0530 Subject: [PATCH 2/5] fix: coderabbit suggestions --- erpnext/selling/doctype/sales_order/sales_order.js | 2 +- erpnext/selling/doctype/sales_order/sales_order.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 5c2f3666b48..b1fc67e8d44 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -1644,7 +1644,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex if (selected_items.some((item) => !item.supplier)) { frappe.throw({ - message: "Supplier is required for all selected Items", + message: __("Supplier is required for all selected Items"), title: __("Supplier Required"), indicator: "blue", }); diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index b3174b2bffa..adbc9839512 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1682,7 +1682,7 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): "margin_rate_or_amount", ], "postprocess": update_item, - "condition": lambda doc: filter_items(doc, supplier), + "condition": lambda doc, s=supplier: filter_items(doc, s), }, "Packed Item": { "doctype": "Purchase Order Item", From 5a17dd8d6d47019b8528c420245357d63e490e9f Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 25 Nov 2025 12:58:52 +0530 Subject: [PATCH 3/5] fix: addresses not being carried forward --- erpnext/public/js/controllers/buying.js | 9 +++++++-- erpnext/selling/doctype/sales_order/sales_order.js | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 873edfb7165..1ffa778d0b8 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -176,9 +176,14 @@ erpnext.buying = { callback: (r) => { if (!r.message) return; - this.frm.set_value("billing_address", r.message.primary_address || ""); + if (!this.frm.doc.billing_address) { + this.frm.set_value("billing_address", r.message.primary_address || ""); + } - if (frappe.meta.has_field(this.frm.doc.doctype, "shipping_address")) { + if ( + frappe.meta.has_field(this.frm.doc.doctype, "shipping_address") && + !this.frm.doc.shipping_address + ) { this.frm.set_value("shipping_address", r.message.shipping_address || ""); } }, diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index b1fc67e8d44..b6c0c496889 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -1588,6 +1588,8 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex fieldname: "items_for_po", fieldtype: "Table", label: __("Select Items"), + cannot_add_rows: true, + cannot_delete_rows: true, fields: [ { fieldtype: "Data", @@ -1698,7 +1700,6 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } set_po_items_data(dialog); - dialog.get_field("items_for_po").grid.only_sortable(); dialog.get_field("items_for_po").refresh(); dialog.wrapper.find(".grid-heading-row .grid-row-check").click(); dialog.show(); From 88b262abc79b0b63e0e21235a8d73a98dae1b150 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 25 Nov 2025 13:14:14 +0530 Subject: [PATCH 4/5] fix: more coderabbit issues --- erpnext/public/js/controllers/buying.js | 9 ++----- .../doctype/sales_order/sales_order.py | 27 +++++++------------ .../doctype/sales_order/test_sales_order.py | 20 +++++++------- 3 files changed, 22 insertions(+), 34 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 1ffa778d0b8..873edfb7165 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -176,14 +176,9 @@ erpnext.buying = { callback: (r) => { if (!r.message) return; - if (!this.frm.doc.billing_address) { - this.frm.set_value("billing_address", r.message.primary_address || ""); - } + this.frm.set_value("billing_address", r.message.primary_address || ""); - if ( - frappe.meta.has_field(this.frm.doc.doctype, "shipping_address") && - !this.frm.doc.shipping_address - ) { + if (frappe.meta.has_field(this.frm.doc.doctype, "shipping_address")) { this.frm.set_value("shipping_address", r.message.shipping_address || ""); } }, diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index adbc9839512..863bb74012b 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1612,34 +1612,27 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): target.stock_qty = flt(source.stock_qty) - flt(source.ordered_qty) target.project = source_parent.project - def update_item_for_packed_item(source, target, source_parent): + def update_item_for_packed_item(source, target, _): target.qty = flt(source.qty) - flt(source.ordered_qty) def filter_items(item, supplier): if ( item.ordered_qty < item.stock_qty - and item.item_code in item_codes and not is_product_bundle(item.item_code) + and items_to_map.get(item.item_code) == supplier ): - item_supp_comb = next(iter(d for d in items_to_map if d["item_code"] == item.item_code), None) - if item_supp_comb and item_supp_comb.get("supplier") == supplier: - items_to_map.remove(item_supp_comb) - return True + return True return False - suppliers = [item.get("supplier") for item in selected_items if item.get("supplier")] - suppliers = list(dict.fromkeys(suppliers)) # remove duplicates while preserving order - - item_codes = [item.get("item_code") for item in selected_items if item.get("item_code")] - items_to_map = [ - {"item_code": item.get("item_code"), "supplier": item.get("supplier")} - for item in selected_items - if item.get("item_code") - ] + items_to_map = { + item.get("item_code"): item.get("supplier") for item in selected_items if item.get("item_code") + } + item_codes = list(set(items_to_map.keys())) + suppliers = list(set(items_to_map.values())) if not suppliers: - frappe.throw(_("Please set a Supplier against the Items to be considered in the Purchase Order.")) + suppliers = [None] purchase_orders = [] for supplier in suppliers: @@ -1712,7 +1705,7 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): ) set_delivery_date(doc.items, source_name) - if len(suppliers) > 1: + if doc.supplier: doc.insert() purchase_orders.append(doc) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index a75e6eca08f..acf0a19943f 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1038,7 +1038,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): def test_drop_shipping(self): from erpnext.buying.doctype.purchase_order.purchase_order import update_status from erpnext.selling.doctype.sales_order.sales_order import ( - make_purchase_order_for_default_supplier, + make_purchase_order, ) from erpnext.selling.doctype.sales_order.sales_order import update_status as so_update_status @@ -1071,7 +1071,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): so = make_sales_order(item_list=so_items, do_not_submit=True) so.submit() - po = make_purchase_order_for_default_supplier(so.name, selected_items=[so_items[0]])[0] + po = make_purchase_order(so.name, selected_items=[so_items[0]])[0] po.submit() dn = create_dn_against_so(so.name, delivered_qty=2) @@ -1129,7 +1129,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): def test_drop_shipping_partial_order(self): from erpnext.selling.doctype.sales_order.sales_order import ( - make_purchase_order_for_default_supplier, + make_purchase_order, ) from erpnext.selling.doctype.sales_order.sales_order import update_status as so_update_status @@ -1165,7 +1165,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): so.submit() # create po for only one item - po1 = make_purchase_order_for_default_supplier(so.name, selected_items=[so_items[0]])[0] + po1 = make_purchase_order(so.name, selected_items=[so_items[0]])[0] po1.submit() self.assertEqual(so.customer, po1.customer) @@ -1175,7 +1175,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): self.assertEqual(len(po1.items), 1) # create po for remaining item - po2 = make_purchase_order_for_default_supplier(so.name, selected_items=[so_items[1]])[0] + po2 = make_purchase_order(so.name, selected_items=[so_items[1]])[0] po2.submit() # teardown @@ -1189,7 +1189,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): def test_drop_shipping_full_for_default_suppliers(self): """Test if multiple POs are generated in one go against different default suppliers.""" from erpnext.selling.doctype.sales_order.sales_order import ( - make_purchase_order_for_default_supplier, + make_purchase_order, ) if not frappe.db.exists("Item", "_Test Item for Drop Shipping 1"): @@ -1221,7 +1221,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): so = make_sales_order(item_list=so_items, do_not_submit=True) so.submit() - purchase_orders = make_purchase_order_for_default_supplier(so.name, selected_items=so_items) + purchase_orders = make_purchase_order(so.name, selected_items=so_items) self.assertEqual(len(purchase_orders), 2) self.assertEqual(purchase_orders[0].supplier, "_Test Supplier") @@ -1253,7 +1253,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): so = make_sales_order(item_list=so_items) - purchase_order = make_purchase_order(so.name, selected_items=so_items) + purchase_order = make_purchase_order(so.name, selected_items=so_items)[0] self.assertEqual(purchase_order.items[0].item_code, "_Test Bundle Item 1") self.assertEqual(purchase_order.items[1].item_code, "_Test Bundle Item 2") @@ -1283,7 +1283,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): so = make_sales_order(item_list=so_items) - purchase_order = make_purchase_order(so.name, selected_items=so_items) + purchase_order = make_purchase_order(so.name, selected_items=so_items)[0] purchase_order.supplier = "_Test Supplier" purchase_order.set_warehouse = "_Test Warehouse - _TC" purchase_order.save() @@ -2559,7 +2559,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): ) so.submit() - po = make_purchase_order(so.name, selected_items=so.items) + po = make_purchase_order(so.name, selected_items=so.items)[0] po.supplier = "_Test Supplier" po.items[0].rate = 100 po.submit() From 7b592d873721d32aa6017446a9e544db960f122b Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 25 Nov 2025 20:54:24 +0530 Subject: [PATCH 5/5] feat: add provision to mass select supplier --- .../doctype/sales_order/sales_order.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index b6c0c496889..6ad9f92c0ed 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -1584,6 +1584,28 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex title: __("Select Items"), size: "large", fields: [ + { + fieldname: "set_supplier", + fieldtype: "Link", + label: __("Set Supplier"), + options: "Supplier", + onchange: function () { + let supplier = dialog.get_value("set_supplier"); + let items_table = dialog.fields_dict.items_for_po.grid; + let selected_items = items_table.get_selected_children(); + + selected_items.forEach((item) => { + item.supplier = supplier; + items_table.refresh(); + }); + }, + }, + { + fieldtype: "Column Break", + }, + { + fieldtype: "Section Break", + }, { fieldname: "items_for_po", fieldtype: "Table",