diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 3d599e311bb..0d421ed4a63 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1545,6 +1545,38 @@ class TestWorkOrder(FrappeTestCase): 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.work_order import create_pick_list + from erpnext.stock.doctype.pick_list.pick_list 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(frappe.as_json(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", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index d6764005a80..d5f7b1dd976 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -406,7 +406,11 @@ class WorkOrder(Document): elif self.docstatus == 1: if status not in ["Closed", "Stopped"]: status = "Not Started" - if flt(self.material_transferred_for_manufacturing) > 0 or self.skip_transfer: + if ( + flt(self.material_transferred_for_manufacturing) > 0 + or self.skip_transfer + or self.has_transferred_material() + ): status = "In Process" precision = frappe.get_precision("Work Order", "produced_qty") @@ -425,6 +429,26 @@ class WorkOrder(Document): 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.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 update_work_order_qty(self): """Update **Manufactured Qty** and **Material Transferred for Qty** in Work Order based on Stock Entry"""