From 0576752d3b59ae6fd16a97ce46c414a9a436594b Mon Sep 17 00:00:00 2001 From: Pandiyan37 Date: Tue, 17 Feb 2026 12:10:08 +0530 Subject: [PATCH 1/3] fix(manufacturing): add sales order fields in subassembly child table --- .../production_plan_sub_assembly_item.json | 22 +++++++++++++++++-- .../production_plan_sub_assembly_item.py | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json index b5f6a3ab065..9818beaa2c8 100644 --- a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json +++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json @@ -10,6 +10,8 @@ "fg_warehouse", "parent_item_code", "schedule_date", + "sales_order", + "sales_order_item", "column_break_3", "qty", "bom_no", @@ -212,20 +214,36 @@ "label": "Ordered Qty", "no_copy": 1, "read_only": 1 + }, + { + "fieldname": "sales_order", + "fieldtype": "Link", + "label": "Sales Order", + "options": "Sales Order", + "read_only": 1 + }, + { + "fieldname": "sales_order_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Sales Order Item", + "no_copy": 1, + "print_hide": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-06-10 13:36:24.759101", + "modified": "2026-02-17 12:06:02.309032", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Sub Assembly Item", "owner": "Administrator", "permissions": [], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "modified", "sort_order": "DESC", "states": [], "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py index 7e29675136c..42031d437b8 100644 --- a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py +++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py @@ -33,6 +33,8 @@ class ProductionPlanSubAssemblyItem(Document): purchase_order: DF.Link | None qty: DF.Float received_qty: DF.Float + sales_order: DF.Link | None + sales_order_item: DF.Data | None schedule_date: DF.Datetime | None stock_uom: DF.Link | None supplier: DF.Link | None From 53e18a9beb551d8c025f2964eb1560e2707878d8 Mon Sep 17 00:00:00 2001 From: Pandiyan37 Date: Tue, 17 Feb 2026 12:11:05 +0530 Subject: [PATCH 2/3] fix(manufacturing): set sales order references in subassembly child table --- .../doctype/production_plan/production_plan.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 29e4f4fb38a..351ba27a43e 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -790,6 +790,8 @@ class ProductionPlan(Document): "stock_uom", "bom_level", "schedule_date", + "sales_order", + "sales_order_item", ]: if row.get(field): wo_data[field] = row.get(field) @@ -829,6 +831,8 @@ class ProductionPlan(Document): "qty", "description", "production_plan_item", + "sales_order", + "sales_order_item", ]: po_data[field] = row.get(field) @@ -1015,6 +1019,10 @@ class ProductionPlan(Document): if not is_group_warehouse: data.fg_warehouse = self.sub_assembly_warehouse + if not self.combine_sub_items: + data.sales_order = row.sales_order + data.sales_order_item = row.sales_order_item + def set_default_supplier_for_subcontracting_order(self): items = [ d.production_item for d in self.sub_assembly_items if d.type_of_manufacturing == "Subcontract" From d2dc0a4c9aa97bce4c8b3ab332c37d4ff10a231e Mon Sep 17 00:00:00 2001 From: Pandiyan37 Date: Tue, 17 Feb 2026 12:15:33 +0530 Subject: [PATCH 3/3] test(manufacturing): add test to validate the sales order references for sub assembly items --- .../production_plan/test_production_plan.py | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 25fc4f26247..62aa4f6ea11 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -565,6 +565,90 @@ class TestProductionPlan(FrappeTestCase): self.assertEqual(po_doc.items[0].fg_item, fg_item) self.assertEqual(po_doc.items[0].item_code, service_item) + def test_sales_order_references_for_sub_assembly_items(self): + """ + Test that Sales Order and Sales Order Item references in Work Order and Purchase Order + are correctly propagated from the Production Plan. + """ + + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + + # Setup Test Items & BOM + fg_item = "Test FG Good Item" + sub_assembly_item1 = "Test Sub Assembly Item 1" + sub_assembly_item2 = "Test Sub Assembly Item 2" + + bom_tree = { + fg_item: { + sub_assembly_item1: {"Test Raw Material 1": {}}, + sub_assembly_item2: {"Test Raw Material 2": {}}, + } + } + + create_nested_bom(bom_tree, prefix="") + + # Create Sales Order + so = make_sales_order(item_code=fg_item, qty=10) + so_item_row = so.items[0].name + + # Create Production Plan from Sales Order + production_plan = frappe.new_doc("Production Plan") + production_plan.company = so.company + production_plan.get_items_from = "Sales Order" + production_plan.item_code = fg_item + + production_plan.get_open_sales_orders() + self.assertEqual(production_plan.sales_orders[0].sales_order, so.name) + + production_plan.get_so_items() + + production_plan.skip_available_sub_assembly_item = 0 + production_plan.get_sub_assembly_items() + + self.assertEqual(len(production_plan.sub_assembly_items), 2) + + # Validate Sales Order references in Sub Assembly Items + for row in production_plan.sub_assembly_items: + if row.production_item == sub_assembly_item1: + row.supplier = "_Test Supplier" + row.type_of_manufacturing = "Subcontract" + + self.assertEqual(row.sales_order, so.name) + self.assertEqual(row.sales_order_item, so_item_row) + + # Submit Production Plan + production_plan.save() + production_plan.submit() + production_plan.make_work_order() + + # Validate Purchase Order (Subcontracted Item) + po_items = frappe.get_all( + "Purchase Order Item", + { + "production_plan": production_plan.name, + "fg_item": sub_assembly_item1, + }, + ["sales_order", "sales_order_item"], + ) + + self.assertTrue(po_items) + self.assertEqual(po_items[0].sales_order, so.name) + self.assertEqual(po_items[0].sales_order_item, so_item_row) + + # Validate Work Order (In-house Item) + work_orders = frappe.get_all( + "Work Order", + { + "production_plan": production_plan.name, + "production_item": sub_assembly_item2, + }, + ["sales_order", "sales_order_item"], + ) + + self.assertTrue(work_orders) + self.assertEqual(work_orders[0].sales_order, so.name) + self.assertEqual(work_orders[0].sales_order_item, so_item_row) + def test_production_plan_combine_subassembly(self): """ Test combining Sub assembly items belonging to the same BOM in Prod Plan.