diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index a6fad4ee1aa..510d4f511dd 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -1019,6 +1019,12 @@ class BOM(WebsiteGenerator): "Row {0}: Workstation or Workstation Type is mandatory for an operation {1}" ).format(d.idx, d.operation) ) + if not d.time_in_mins or d.time_in_mins <= 0: + frappe.throw( + _("Row {0}: Operation time should be greater than 0 for operation {1}").format( + d.idx, d.operation + ) + ) def get_tree_representation(self) -> BOMTree: """Get a complete tree representation preserving order of child items.""" diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index 83e722cce50..cc942d59c74 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -132,6 +132,15 @@ class TestBOM(FrappeTestCase): self.assertAlmostEqual(bom.base_raw_material_cost, base_raw_material_cost) self.assertAlmostEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost) + @timeout + def test_bom_no_operation_time_validation(self): + bom = frappe.copy_doc(test_records[2]) + bom.docstatus = 0 + for op_row in bom.operations: + op_row.time_in_mins = 0 + + self.assertRaises(frappe.ValidationError, bom.save) + @timeout def test_bom_cost_with_batch_size(self): bom = frappe.copy_doc(test_records[2])