fix(UX): use doc.status for Job Card status (#31320)

fix(UX): use doc.status for JC status

- Use doc.status directly for indicator - single source of truth
- Update status to cancelled when doc is cancelled
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 0199a5c..ed45106 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -626,14 +626,15 @@
 
 		self.status = {0: "Open", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0]
 
-		if self.for_quantity <= self.transferred_qty:
-			self.status = "Material Transferred"
+		if self.docstatus < 2:
+			if self.for_quantity <= self.transferred_qty:
+				self.status = "Material Transferred"
 
-		if self.time_logs:
-			self.status = "Work In Progress"
+			if self.time_logs:
+				self.status = "Work In Progress"
 
-		if self.docstatus == 1 and (self.for_quantity <= self.total_completed_qty or not self.items):
-			self.status = "Completed"
+			if self.docstatus == 1 and (self.for_quantity <= self.total_completed_qty or not self.items):
+				self.status = "Completed"
 
 		if update_status:
 			self.db_set("status", self.status)
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_list.js b/erpnext/manufacturing/doctype/job_card/job_card_list.js
index 7f60bdc..5d883bf 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_list.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card_list.js
@@ -1,16 +1,17 @@
 frappe.listview_settings['Job Card'] = {
 	has_indicator_for_draft: true,
+
 	get_indicator: function(doc) {
-		if (doc.status === "Work In Progress") {
-			return [__("Work In Progress"), "orange", "status,=,Work In Progress"];
-		} else if (doc.status === "Completed") {
-			return [__("Completed"), "green", "status,=,Completed"];
-		} else if (doc.docstatus == 2) {
-			return [__("Cancelled"), "red", "status,=,Cancelled"];
-		} else if (doc.status === "Material Transferred") {
-			return [__('Material Transferred'), "blue", "status,=,Material Transferred"];
-		} else {
-			return [__("Open"), "red", "status,=,Open"];
-		}
+		const status_colors = {
+			"Work In Progress": "orange",
+			"Completed": "green",
+			"Cancelled": "red",
+			"Material Transferred": "blue",
+			"Open": "red",
+		};
+		const status = doc.status || "Open";
+		const color = status_colors[status] || "blue";
+
+		return [__(status), color, `status,=,${status}`];
 	}
 };
diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py
index 7f3c7fe..ac71141 100644
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py
@@ -344,6 +344,30 @@
 		cost_after_cancel = self.work_order.total_operating_cost
 		self.assertEqual(cost_after_cancel, original_cost)
 
+	def test_job_card_statuses(self):
+		def assertStatus(status):
+			jc.set_status()
+			self.assertEqual(jc.status, status)
+
+		jc = frappe.new_doc("Job Card")
+		jc.for_quantity = 2
+		jc.transferred_qty = 1
+		jc.total_completed_qty = 0
+		assertStatus("Open")
+
+		jc.transferred_qty = jc.for_quantity
+		assertStatus("Material Transferred")
+
+		jc.append("time_logs", {})
+		assertStatus("Work In Progress")
+
+		jc.docstatus = 1
+		jc.total_completed_qty = jc.for_quantity
+		assertStatus("Completed")
+
+		jc.docstatus = 2
+		assertStatus("Cancelled")
+
 
 def create_bom_with_multiple_operations():
 	"Create a BOM with multiple operations and Material Transfer against Job Card"