From 1aa6f8d1a77f6011cf5a1fc2f0fa962e3fb3db01 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 7 Aug 2026 22:34:06 +0530 Subject: [PATCH] feat: warn when ordered qty exceeds min order qty only by UOM rounding A minimum order qty defined in stock UOM often has no exact representation in the purchase UOM, so the smallest valid order slightly exceeds the minimum. Surface that overage on the Purchase Order with a toast on first save when an item's ordered stock qty is above its minimum by less than one purchase-UOM step, so the buyer sees the marginal increase before sending the order. Sub-precision dust stays silent. --- .../doctype/purchase_order/purchase_order.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 637a86846f9..a27689d6052 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -319,6 +319,38 @@ class PurchaseOrder(BuyingController): ).format(item_code, flt(qty, precision), itemwise_min_order_qty.get(item_code)) ) + self.warn_marginal_min_order_qty(itemwise_qty, itemwise_min_order_qty) + + def warn_marginal_min_order_qty(self, itemwise_qty, itemwise_min_order_qty): + """Toast when an item's ordered qty exceeds its minimum only by purchase UOM rounding.""" + if not self.is_new(): + return + + precision = self.items[0].precision("stock_qty") + itemwise_step = frappe._dict() + itemwise_stock_uom = frappe._dict() + for d in self.get("items"): + step = 10 ** -d.precision("qty") * flt(d.conversion_factor) + itemwise_step[d.item_code] = max(itemwise_step.get(d.item_code, 0), step) + itemwise_stock_uom[d.item_code] = d.stock_uom + + for item_code, qty in itemwise_qty.items(): + min_order_qty = flt(itemwise_min_order_qty.get(item_code)) + overage = flt(qty) - min_order_qty + if min_order_qty and flt(overage, precision) > 0 and overage < itemwise_step[item_code]: + frappe.toast( + _( + "Item {0}: Ordered qty {1} {2} exceeds the minimum order qty {3} {2} by {4} {2} due to purchase UOM rounding." + ).format( + item_code, + flt(qty, precision), + itemwise_stock_uom[item_code], + min_order_qty, + flt(overage, precision), + ), + indicator="orange", + ) + def get_schedule_dates(self): for d in self.get("items"): if d.material_request_item and not d.schedule_date: