Merge pull request #52703 from frappe/mergify/bp/version-16-hotfix/pr-52626

fix(manufacturing): add sales order fields in subassembly child table (backport #52626)
This commit is contained in:
Mihir Kandoi
2026-02-16 23:58:13 +05:30
committed by GitHub
4 changed files with 117 additions and 1 deletions

View File

@@ -844,6 +844,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)
@@ -898,6 +900,8 @@ class ProductionPlan(Document):
"qty",
"description",
"production_plan_item",
"sales_order",
"sales_order_item",
]:
po_data[field] = row.get(field)
@@ -1122,6 +1126,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"

View File

@@ -626,6 +626,90 @@ class TestProductionPlan(IntegrationTestCase):
frappe.db.count("Purchase Order Item", {"production_plan": plan.name, "docstatus": 1}), 2
) # 2 since we have already created and submitted 2 POs
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_for_mr_items(self):
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom

View File

@@ -21,6 +21,9 @@
"subcontracting_section",
"supplier",
"purchase_order",
"column_break_oqry",
"sales_order",
"sales_order_item",
"work_order_details_section",
"production_plan_item",
"wo_produced_qty",
@@ -240,13 +243,32 @@
"label": "Ordered Qty",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "column_break_oqry",
"fieldtype": "Column Break"
},
{
"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
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2025-11-03 14:33:50.677717",
"modified": "2026-02-11 13:00:09.092676",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan Sub Assembly Item",

View File

@@ -34,6 +34,8 @@ class ProductionPlanSubAssemblyItem(Document):
qty: DF.Float
received_qty: DF.Float
required_qty: DF.Float
sales_order: DF.Link | None
sales_order_item: DF.Data | None
schedule_date: DF.Datetime | None
stock_reserved_qty: DF.Float
stock_uom: DF.Link | None