diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 8639a6769cd..e17b984e3ce 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -912,9 +912,19 @@ class JobCard(Document): ) ) + self.validate_not_on_hold() self.validate_time_logs_present() self.validate_completed_qty_matches_for_quantity() + def validate_not_on_hold(self): + if self.is_paused: + frappe.throw( + _( + "Cannot submit Job Card {0} while it is On Hold. Please resume and complete the job before submission." + ).format(get_link_to_form("Job Card", self.name)), + title=_("Job Card On Hold"), + ) + def validate_time_logs_present(self): if not self.time_logs: frappe.throw( diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py index c44591d6ab3..518c13450cd 100644 --- a/erpnext/manufacturing/doctype/job_card/test_job_card.py +++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py @@ -197,6 +197,28 @@ class TestJobCard(ERPNextTestSuite): ) self.assertEqual(completed_qty, job_card.for_quantity) + def test_job_card_cannot_be_submitted_while_on_hold(self): + # Regression for #55756: a paused (On Hold) job card must not be submittable, otherwise + # the document gets locked in the On Hold state with Resume/Complete no longer available. + job_card = frappe.get_all( + "Job Card", + filters={"work_order": self.work_order.name}, + fields=["name", "for_quantity"], + )[0] + + doc = frappe.get_doc("Job Card", job_card.name) + doc.append( + "time_logs", + { + "from_time": "2024-01-01 08:00:00", + "to_time": "2024-01-01 09:00:00", + "time_in_mins": 60, + "completed_qty": job_card.for_quantity, + }, + ) + doc.is_paused = 1 + self.assertRaises(frappe.ValidationError, doc.submit) + def test_job_card_overlap(self): wo2 = make_wo_order_test_record(item="_Test FG Item 2", qty=2)