fix: validate Work Order qty against Production Plan (#29721) (#30003)
* fix: validate Work Order qty against Production Plan
* chore: err msg when max_qty is 0
* test: add test for overproduction
* fix: CI
(cherry picked from commit 067ede76ea0851a29e270e150f482368e0afa194)
Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index d88e10a..2359815 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -9,6 +9,7 @@
get_sales_orders,
get_warehouse_list,
)
+from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
@@ -466,26 +467,29 @@
bom = make_bom(item=item, raw_materials=raw_materials)
# Create Production Plan
- pln = create_production_plan(item_code=bom.item, planned_qty=10)
+ pln = create_production_plan(item_code=bom.item, planned_qty=5)
# All the created Work Orders
wo_list = []
- # Create and Submit 1st Work Order for 5 qty
- create_work_order(item, pln, 5)
+ # Create and Submit 1st Work Order for 3 qty
+ create_work_order(item, pln, 3)
+ pln.reload()
+ self.assertEqual(pln.po_items[0].ordered_qty, 3)
+
+ # Create and Submit 2nd Work Order for 2 qty
+ create_work_order(item, pln, 2)
pln.reload()
self.assertEqual(pln.po_items[0].ordered_qty, 5)
- # Create and Submit 2nd Work Order for 3 qty
- create_work_order(item, pln, 3)
- pln.reload()
- self.assertEqual(pln.po_items[0].ordered_qty, 8)
+ # Overproduction
+ self.assertRaises(OverProductionError, create_work_order, item=item, pln=pln, qty=2)
# Cancel 1st Work Order
wo1 = frappe.get_doc("Work Order", wo_list[0])
wo1.cancel()
pln.reload()
- self.assertEqual(pln.po_items[0].ordered_qty, 3)
+ self.assertEqual(pln.po_items[0].ordered_qty, 2)
# Cancel 2nd Work Order
wo2 = frappe.get_doc("Work Order", wo_list[1])
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index ed6a029..e1120c7 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -636,6 +636,21 @@
if not self.qty > 0:
frappe.throw(_("Quantity to Manufacture must be greater than 0."))
+ if self.production_plan and self.production_plan_item:
+ qty_dict = frappe.db.get_value("Production Plan Item", self.production_plan_item, ["planned_qty", "ordered_qty"], as_dict=1)
+
+ allowance_qty =flt(frappe.db.get_single_value("Manufacturing Settings",
+ "overproduction_percentage_for_work_order"))/100 * qty_dict.get("planned_qty", 0)
+
+ max_qty = qty_dict.get("planned_qty", 0) + allowance_qty - qty_dict.get("ordered_qty", 0)
+
+ if max_qty < 1:
+ frappe.throw(_("Cannot produce more item for {0}")
+ .format(self.production_item), OverProductionError)
+ elif self.qty > max_qty:
+ frappe.throw(_("Cannot produce more than {0} items for {1}")
+ .format(max_qty, self.production_item), OverProductionError)
+
def validate_transfer_against(self):
if not self.docstatus == 1:
# let user configure operations until they're ready to submit