From 98b74079499a71adb4972aaa917a9d703eedc93c Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 7 Aug 2026 12:44:28 +0530 Subject: [PATCH 1/2] fix: compare ordered qty to min order qty at stock_qty precision stock_qty is stored as raw qty * conversion_factor, so a UOM-converted order for exactly the minimum (e.g. LB to Kg) produces values like 1999.999999131832 vs a min_order_qty of 2000 and blocks the Purchase Order. Round both sides to the stock_qty field precision before comparing, and show the rounded qty in the error message. --- erpnext/buying/doctype/purchase_order/purchase_order.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 0a28177ba74..637a86846f9 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -310,12 +310,13 @@ class PurchaseOrder(BuyingController): itemwise_qty.setdefault(d.item_code, 0) itemwise_qty[d.item_code] += flt(d.stock_qty) + precision = self.items[0].precision("stock_qty") for item_code, qty in itemwise_qty.items(): - if flt(qty) < flt(itemwise_min_order_qty.get(item_code)): + if flt(qty, precision) < flt(itemwise_min_order_qty.get(item_code), precision): frappe.throw( _( "Item {0}: Ordered qty {1} cannot be less than minimum order qty {2} (defined in Item)." - ).format(item_code, qty, itemwise_min_order_qty.get(item_code)) + ).format(item_code, flt(qty, precision), itemwise_min_order_qty.get(item_code)) ) def get_schedule_dates(self): From c652f47931f97b9e98ff5e29283fffb50cd21d6f Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 7 Aug 2026 12:51:40 +0530 Subject: [PATCH 2/2] test: min order qty check tolerates UOM conversion dust --- .../purchase_order/test_purchase_order.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index d36dedb200a..05c6542939d 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -707,6 +707,23 @@ class TestPurchaseOrder(ERPNextTestSuite): po = create_purchase_order(qty=3.4, do_not_save=True) self.assertRaises(UOMMustBeIntegerError, po.insert) + def test_min_order_qty_with_uom_conversion_dust(self): + item_doc = make_item(properties={"min_order_qty": 2000, "stock_uom": "Kg"}) + item_doc.append("uoms", {"uom": "Litre", "conversion_factor": 0.6}) + item_doc.save() + item = item_doc.name + + precision = frappe.get_precision("Purchase Order Item", "stock_qty") + po = create_purchase_order(item_code=item, qty=flt(2000 / 0.6, precision), do_not_save=1) + po.items[0].uom = "Litre" + po.items[0].conversion_factor = 0.6 + po.insert() + + below_minimum = create_purchase_order(item_code=item, qty=3000, do_not_save=1) + below_minimum.items[0].uom = "Litre" + below_minimum.items[0].conversion_factor = 0.6 + self.assertRaises(frappe.ValidationError, below_minimum.insert) + def test_ordered_qty_for_closing_po(self): bin = frappe.get_all( "Bin",