Merge pull request #35167 from rohitwaghchaure/fixed-job-card-excess-material-trafer

fix: not allow to transfer excess materials against the job card
diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py
index 4d2dab7..61766a6 100644
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py
@@ -272,6 +272,42 @@
 		transfer_entry_2.insert()
 		self.assertRaises(JobCardOverTransferError, transfer_entry_2.submit)
 
+	@change_settings("Manufacturing Settings", {"job_card_excess_transfer": 0})
+	def test_job_card_excess_material_transfer_with_no_reference(self):
+
+		self.transfer_material_against = "Job Card"
+		self.source_warehouse = "Stores - _TC"
+
+		self.generate_required_stock(self.work_order)
+
+		job_card_name = frappe.db.get_value("Job Card", {"work_order": self.work_order.name})
+
+		# fully transfer both RMs
+		transfer_entry_1 = make_stock_entry_from_jc(job_card_name)
+		row = transfer_entry_1.items[0]
+
+		# Add new row without reference of the job card item
+		transfer_entry_1.append(
+			"items",
+			{
+				"item_code": row.item_code,
+				"item_name": row.item_name,
+				"item_group": row.item_group,
+				"qty": row.qty,
+				"uom": row.uom,
+				"conversion_factor": row.conversion_factor,
+				"stock_uom": row.stock_uom,
+				"basic_rate": row.basic_rate,
+				"basic_amount": row.basic_amount,
+				"expense_account": row.expense_account,
+				"cost_center": row.cost_center,
+				"s_warehouse": row.s_warehouse,
+				"t_warehouse": row.t_warehouse,
+			},
+		)
+
+		self.assertRaises(frappe.ValidationError, transfer_entry_1.insert)
+
 	def test_job_card_partial_material_transfer(self):
 		"Test partial material transfer against Job Card"
 		self.transfer_material_against = "Job Card"
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index b5e5299..cc0923f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -127,6 +127,7 @@
 		self.validate_fg_completed_qty()
 		self.validate_difference_account()
 		self.set_job_card_data()
+		self.validate_job_card_item()
 		self.set_purpose_for_stock_entry()
 		self.clean_serial_nos()
 		self.validate_duplicate_serial_no()
@@ -211,6 +212,24 @@
 			self.from_bom = 1
 			self.bom_no = data.bom_no
 
+	def validate_job_card_item(self):
+		if not self.job_card:
+			return
+
+		if cint(frappe.db.get_single_value("Manufacturing Settings", "job_card_excess_transfer")):
+			return
+
+		for row in self.items:
+			if row.job_card_item:
+				continue
+
+			msg = f"""Row #{0}: The job card item reference
+				is missing. Kindly create the stock entry
+				from the job card. If you have added the row manually
+				then you won't be able to add job card item reference."""
+
+			frappe.throw(_(msg))
+
 	def validate_work_order_status(self):
 		pro_doc = frappe.get_doc("Work Order", self.work_order)
 		if pro_doc.status == "Completed":