From 9501149bd85c040d0d6eedea6a9ed864cdce2fe8 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 18 Jul 2025 21:26:28 +0530 Subject: [PATCH] Revert "fix: job card linter error (backport #47561) (#48695)" This reverts commit 4794e7acffb018cc9c07876dda3f7b4f05658bf8. --- .../doctype/work_order/test_work_order.py | 55 +++++++++++-------- .../doctype/work_order/work_order.py | 36 ++++-------- .../work_order_operation.json | 3 +- 3 files changed, 45 insertions(+), 49 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index c77d34950ec..5106ded95e8 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -2788,45 +2788,56 @@ class TestWorkOrder(FrappeTestCase): fg_warehouse="_Test Warehouse 2 - _TC", ) + # Initial check + self.assertEqual(wo.operations[0].operation, "Test Operation A") + self.assertEqual(wo.operations[1].operation, "Test Operation B") + self.assertEqual(wo.operations[2].operation, "Test Operation C") + self.assertEqual(wo.operations[3].operation, "Test Operation D") + wo = frappe.copy_doc(wo) - wo.operations[3].sequence_id = None - - # Test 1 : If any one operation does not have sequence ID then error will be thrown - self.assertRaises(frappe.ValidationError, wo.submit) - - for op in wo.operations: - op.sequence_id = None + wo.operations[3].sequence_id = 2 wo.submit() - # Test 2 : If none of the operations have sequence ID then they will be sequenced as per their idx - for op in wo.operations: - self.assertEqual(op.sequence_id, op.idx) + # Test 2 : Sort line items in child table based on sequence ID + self.assertEqual(wo.operations[0].operation, "Test Operation A") + self.assertEqual(wo.operations[1].operation, "Test Operation B") + self.assertEqual(wo.operations[2].operation, "Test Operation D") + self.assertEqual(wo.operations[3].operation, "Test Operation C") wo = frappe.copy_doc(wo) - wo.operations[0].sequence_id = 2 + wo.operations[3].sequence_id = 1 + wo.submit() - # Test 3 : Sequence IDs should not miss the correct sequence of numbers - self.assertRaises(frappe.ValidationError, wo.submit) + self.assertEqual(wo.operations[0].operation, "Test Operation A") + self.assertEqual(wo.operations[1].operation, "Test Operation C") + self.assertEqual(wo.operations[2].operation, "Test Operation B") + self.assertEqual(wo.operations[3].operation, "Test Operation D") - wo.operations[1].sequence_id = 1 + wo = frappe.copy_doc(wo) + wo.operations[0].sequence_id = 3 + wo.submit() - # Test 4 : Sequence IDs should be in the correct ascending order + self.assertEqual(wo.operations[0].operation, "Test Operation C") + self.assertEqual(wo.operations[1].operation, "Test Operation B") + self.assertEqual(wo.operations[2].operation, "Test Operation D") + self.assertEqual(wo.operations[3].operation, "Test Operation A") + + wo = frappe.copy_doc(wo) + wo.operations[1].sequence_id = 0 + + # Test 3 - Error should be thrown if any one operation does not have sequence id but others do self.assertRaises(frappe.ValidationError, wo.submit) workstation = frappe.get_doc("Workstation", "Test Workstation A") workstation.production_capacity = 4 workstation.save() + wo = frappe.copy_doc(wo) - wo.operations[0].sequence_id = 1 wo.operations[1].sequence_id = 2 - wo.operations[2].sequence_id = 2 - wo.operations[3].sequence_id = 3 wo.submit() - # Test 5 : If two operations have the same sequence ID then the next operation will start 10 mins after the longest previous operation ends - self.assertEqual( - wo.operations[3].planned_start_time, add_to_date(wo.operations[1].planned_end_time, minutes=10) - ) + # Test 4 - If Sequence ID is same then planned start time for both operations should be same + self.assertEqual(wo.operations[1].planned_start_time, wo.operations[2].planned_start_time) def make_stock_in_entries_and_get_batches(rm_item, source_warehouse, wip_warehouse): diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 9d862e84da7..9b1bf28f997 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -168,31 +168,6 @@ class WorkOrder(Document): validate_uom_is_integer(self, "stock_uom", ["required_qty"]) self.set_required_items(reset_only_qty=len(self.get("required_items"))) - self.validate_operations_sequence() - - def validate_operations_sequence(self): - if all([not op.sequence_id for op in self.operations]): - for op in self.operations: - op.sequence_id = op.idx - else: - sequence_id = 1 - for op in self.operations: - if op.idx == 1 and op.sequence_id != 1: - frappe.throw( - _("Row #1: Sequence ID must be 1 for Operation {0}.").format( - frappe.bold(op.operation) - ) - ) - elif op.sequence_id != sequence_id and op.sequence_id != sequence_id + 1: - frappe.throw( - _("Row #{0}: Sequence ID must be {1} or {2} for Operation {3}.").format( - op.idx, - frappe.bold(sequence_id), - frappe.bold(sequence_id + 1), - frappe.bold(op.operation), - ) - ) - sequence_id = op.sequence_id def set_warehouses(self): for row in self.required_items: @@ -662,6 +637,17 @@ class WorkOrder(Document): enable_capacity_planning = not cint(manufacturing_settings_doc.disable_capacity_planning) plan_days = cint(manufacturing_settings_doc.capacity_planning_for_days) or 30 + if all([op.sequence_id for op in self.operations]): + self.operations = sorted(self.operations, key=lambda op: op.sequence_id) + for idx, op in enumerate(self.operations): + op.idx = idx + 1 + elif any([op.sequence_id for op in self.operations]): + frappe.throw( + _( + "Row #{0}: Incorrect Sequence ID. If any single operation has a Sequence ID then all other operations must have one too." + ).format(next((op.idx for op in self.operations if not op.sequence_id), None)) + ) + for idx, row in enumerate(self.operations): qty = self.qty while qty > 0: diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json index 38b325b73ab..0185812a4b6 100644 --- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json +++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json @@ -194,7 +194,6 @@ "fieldname": "sequence_id", "fieldtype": "Int", "label": "Sequence ID", - "non_negative": 1, "print_hide": 1 }, { @@ -225,7 +224,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-05-15 15:10:06.885440", + "modified": "2025-04-09 16:21:47.110564", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order Operation",