From 6591ae195d8790f22d5d202df7ed3ae9e8d65603 Mon Sep 17 00:00:00 2001 From: Sudharsanan11 Date: Thu, 2 Jul 2026 17:38:14 +0530 Subject: [PATCH 1/2] fix(manufacturing): update work order status on partial pick-list transfer A stock entry created from a pick list has fg_completed_qty=0, so material_transferred_for_manufacturing is derived from the min-fraction of item-level transfers. When a pick list moves only some required items, the un-picked item stays at 0, which zeroes the aggregate and leaves the work order status at "not started" even though material is already in wip. Promote the status to "in process" when any raw material has been transferred via a pick list. material_transferred_for_manufacturing stays min-fraction based (0 correctly means no full finished good can be started yet). --- .../doctype/work_order/services/status.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/services/status.py b/erpnext/manufacturing/doctype/work_order/services/status.py index ce67978afd7..d22ee8bd937 100644 --- a/erpnext/manufacturing/doctype/work_order/services/status.py +++ b/erpnext/manufacturing/doctype/work_order/services/status.py @@ -132,7 +132,9 @@ class StatusService: status = ( "In Process" - if flt(self.doc.material_transferred_for_manufacturing) > 0 or self.doc.skip_transfer + if flt(self.doc.material_transferred_for_manufacturing) > 0 + or self.doc.skip_transfer + or self._has_transferred_material() else "Not Started" ) precision = frappe.get_precision("Work Order", "produced_qty") @@ -141,6 +143,26 @@ class StatusService: status = "Completed" return status + def _has_transferred_material(self): + """True if any raw material was transferred against this work order via a pick list + (these leave material_transferred_for_manufacturing at 0 via the min-fraction rule).""" + ste = frappe.qb.DocType("Stock Entry") + ste_child = frappe.qb.DocType("Stock Entry Detail") + qty = ( + frappe.qb.from_(ste) + .inner_join(ste_child) + .on(ste_child.parent == ste.name) + .select(Sum(ste_child.transfer_qty)) + .where( + (ste.work_order == self.doc.name) + & (ste.docstatus == 1) + & (ste.purpose == "Material Transfer for Manufacture") + & (ste.is_return == 0) + & (ste.pick_list.isnotnull()) + ) + ).run()[0][0] + return flt(qty) > 0 + def _is_partial_skip_transfer(self): return bool( self.doc.skip_transfer From f85f6be3cf1afa64e47b80ba4caad0c5d6e2175c Mon Sep 17 00:00:00 2001 From: Sudharsanan11 Date: Thu, 2 Jul 2026 17:39:23 +0530 Subject: [PATCH 2/2] test(manufacturing): add test to validate the work order status on partial pick-list transfer Cover the pick-list flow where a stock entry moves only one of the work order's required items: material_transferred_for_manufacturing stays 0 (min fraction) while the status must move to "in process". --- .../doctype/work_order/test_work_order.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 0a4bbcddd2a..a101bb04b4d 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1528,6 +1528,38 @@ class TestWorkOrder(ERPNextTestSuite): work_order.reload() self.assertEqual(work_order.material_transferred_for_manufacturing, 2.0) + def test_status_in_process_when_only_one_required_item_transferred(self): + """Stock Entry created from a Pick List that picked only one of the required items: + min-fraction keeps material_transferred_for_manufacturing at 0, but the work order must + still move to In Process because material is already in WIP.""" + from erpnext.manufacturing.doctype.work_order.mapper import create_pick_list + from erpnext.stock.doctype.pick_list.mapper import create_stock_entry + + work_order = make_wo_order_test_record( + planned_start_date=now(), qty=2, source_warehouse="Stores - _TC" + ) + test_stock_entry.make_stock_entry( + item_code="_Test Item", target="Stores - _TC", qty=10, basic_rate=5000.0 + ) + test_stock_entry.make_stock_entry( + item_code="_Test Item Home Desktop 100", target="Stores - _TC", qty=10, basic_rate=1000.0 + ) + + pick_list = create_pick_list(work_order.name, for_qty=work_order.qty) + # pick only _Test Item; the other required item is left out of this pick list + pick_list.pick_manually = 1 + pick_list.locations = [loc for loc in pick_list.locations if loc.item_code == "_Test Item"] + pick_list.save() + pick_list.submit() + + stock_entry = frappe.get_doc(create_stock_entry(pick_list.as_dict())) + self.assertEqual(stock_entry.fg_completed_qty, 0.0) + stock_entry.submit() + + work_order.reload() + self.assertEqual(work_order.material_transferred_for_manufacturing, 0.0) + self.assertEqual(work_order.status, "In Process") + def test_backflushed_batch_raw_materials_based_on_transferred(self): frappe.db.set_single_value( "Manufacturing Settings",