From 77ec6447c3457b45460223e078d240ed096654cb Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Thu, 16 Jul 2026 14:04:37 +0530 Subject: [PATCH] fix: consider min order qty in the purchase/transfer flow of production plan (backport #57204) (#57209) * fix: consider min order qty in the purchase/transfer flow of production plan (backport #57204) The transfer flow ignored Consider Minimum Order Qty twice: the JS handler force-reset the checkbox before fetching items, and the purchase remainder left after allocating transfers from other warehouses was never raised to min_order_qty (the check runs on the total requirement before the split). Drop the JS reset and apply min order qty to the purchase remainder, in stock UOM before the purchase UOM conversion. * test: do not set ignore_existing_ordered_qty; v15 gates the transfer split on it being unset --- .../doctype/production_plan/production_plan.js | 2 -- .../doctype/production_plan/production_plan.py | 15 ++++++++++++--- .../production_plan/test_production_plan.py | 11 +++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 09abcb6351e..347b0a2a333 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -365,8 +365,6 @@ frappe.ui.form.on("Production Plan", { frappe.throw(__("Select the Warehouse")); } - frm.set_value("consider_minimum_order_qty", 0); - if (frm.doc.ignore_existing_ordered_qty) { frm.events.get_items_for_material_requests(frm); } else { diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index cb8f24fc9f1..52f301bad11 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1708,7 +1708,13 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d if (not ignore_existing_ordered_qty or get_parent_warehouse_data) and warehouses: new_mr_items = [] for item in mr_items: - get_materials_from_other_locations(item, warehouses, new_mr_items, company) + get_materials_from_other_locations( + item, + warehouses, + new_mr_items, + company, + consider_minimum_order_qty=doc.get("consider_minimum_order_qty"), + ) mr_items = new_mr_items @@ -1728,7 +1734,9 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d return mr_items -def get_materials_from_other_locations(item, warehouses, new_mr_items, company): +def get_materials_from_other_locations( + item, warehouses, new_mr_items, company, consider_minimum_order_qty=False +): from erpnext.stock.doctype.pick_list.pick_list import get_available_item_locations stock_uom, purchase_uom = frappe.db.get_value( @@ -1773,7 +1781,8 @@ def get_materials_from_other_locations(item, warehouses, new_mr_items, company): precision = frappe.get_precision("Material Request Plan Item", "quantity") if flt(required_qty, precision) > 0: - required_qty = required_qty + if consider_minimum_order_qty: + required_qty = max(required_qty, flt(item.get("min_order_qty"))) if frappe.db.get_value("UOM", purchase_uom, "must_be_whole_number"): required_qty = ceil(required_qty) diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index e64a8c16b84..b1f1866a12c 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1832,6 +1832,17 @@ class TestProductionPlan(FrappeTestCase): for d in mr_items: self.assertEqual(d.get("quantity"), 1000.0) + source_warehouse = create_warehouse("MOQ Source Warehouse", company="_Test Company") + make_stock_entry(item_code=rm_item, qty=7, rate=100, target=source_warehouse) + + mr_items = get_items_for_material_requests( + pln.as_dict(), warehouses=[{"warehouse": source_warehouse}] + ) + self.assertEqual(len(mr_items), 2) + items_by_type = {d.get("material_request_type"): d for d in mr_items} + self.assertEqual(items_by_type["Material Transfer"].get("quantity"), 7.0) + self.assertEqual(items_by_type["Purchase"].get("quantity"), 1000.0) + def test_fg_item_quantity(self): fg_item = make_item(properties={"is_stock_item": 1}).name rm_item = make_item(properties={"is_stock_item": 1}).name