fix: consider min order qty in the purchase/transfer flow of production plan

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.
This commit is contained in:
Mihir Kandoi
2026-07-16 12:46:21 +05:30
parent 7fe4dc1367
commit 2f8d588093
3 changed files with 26 additions and 6 deletions

View File

@@ -471,8 +471,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 {

View File

@@ -453,7 +453,13 @@ def _apply_other_locations(doc, mr_items, warehouses, ignore_ordered_qty, get_pa
new_mr_items = []
for item in mr_items:
get_materials_from_other_locations(item, warehouses, new_mr_items, doc.get("company"))
get_materials_from_other_locations(
item,
warehouses,
new_mr_items,
doc.get("company"),
consider_minimum_order_qty=doc.get("consider_minimum_order_qty"),
)
return new_mr_items
@@ -573,7 +579,9 @@ def _material_request_item_row(
}
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
locations = get_available_item_locations(
@@ -590,7 +598,7 @@ def get_materials_from_other_locations(item, warehouses, new_mr_items, company):
required_qty = required_qty * item.get("conversion_factor")
required_qty = _transfer_from_locations(item, locations, new_mr_items, required_qty)
_add_remaining_purchase_request(item, new_mr_items, required_qty)
_add_remaining_purchase_request(item, new_mr_items, required_qty, consider_minimum_order_qty)
def _transfer_from_locations(item, locations, new_mr_items, required_qty):
@@ -615,12 +623,15 @@ def _transfer_from_locations(item, locations, new_mr_items, required_qty):
return required_qty
def _add_remaining_purchase_request(item, new_mr_items, required_qty):
def _add_remaining_purchase_request(item, new_mr_items, required_qty, consider_minimum_order_qty=False):
# raise purchase request for remaining qty
precision = frappe.get_precision("Material Request Plan Item", "quantity")
if flt(required_qty, precision) <= 0:
return
if consider_minimum_order_qty:
required_qty = max(required_qty, flt(item.get("min_order_qty")))
purchase_uom = frappe.db.get_value("Item", item.get("item_code"), "purchase_uom")
if frappe.db.get_value("UOM", purchase_uom, "must_be_whole_number"):
required_qty = ceil(required_qty)

View File

@@ -2129,6 +2129,17 @@ class TestProductionPlan(ERPNextTestSuite):
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)
pln.ignore_existing_ordered_qty = 1
mr_items = get_items_for_material_requests(
pln.as_dict(), warehouses=[{"warehouse": source_warehouse}]
)
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