Merge pull request #34897 from barredterra/fix-translation-files

fix: translation files
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/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 5304273..3373d8a 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -379,7 +379,7 @@
 					)
 
 					outgoing_amount = d.base_net_amount
-					if self.is_internal_supplier and d.valuation_rate:
+					if self.is_internal_transfer() and d.valuation_rate:
 						outgoing_amount = abs(
 							frappe.db.get_value(
 								"Stock Ledger Entry",
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index aabc6fc..5053460 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -300,9 +300,7 @@
 
 
 def notify_error_to_stock_managers(doc, traceback):
-	recipients = get_users_with_role("Stock Manager")
-	if not recipients:
-		recipients = get_users_with_role("System Manager")
+	recipients = get_recipients()
 
 	subject = _("Error while reposting item valuation")
 	message = (
@@ -319,6 +317,17 @@
 	frappe.sendmail(recipients=recipients, subject=subject, message=message)
 
 
+def get_recipients():
+	role = (
+		frappe.db.get_single_value("Stock Reposting Settings", "notify_reposting_error_to_role")
+		or "Stock Manager"
+	)
+
+	recipients = get_users_with_role(role)
+
+	return recipients
+
+
 def repost_entries():
 	"""
 	Reposts 'Repost Item Valuation' entries in queue.
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":
diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
index 0facae8..7c712ce 100644
--- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
@@ -12,7 +12,9 @@
   "start_time",
   "end_time",
   "limits_dont_apply_on",
-  "item_based_reposting"
+  "item_based_reposting",
+  "errors_notification_section",
+  "notify_reposting_error_to_role"
  ],
  "fields": [
   {
@@ -52,12 +54,23 @@
    "fieldname": "item_based_reposting",
    "fieldtype": "Check",
    "label": "Use Item based reposting"
+  },
+  {
+   "fieldname": "notify_reposting_error_to_role",
+   "fieldtype": "Link",
+   "label": "Notify Reposting Error to Role",
+   "options": "Role"
+  },
+  {
+   "fieldname": "errors_notification_section",
+   "fieldtype": "Section Break",
+   "label": "Errors Notification"
   }
  ],
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-11-02 01:22:45.155841",
+ "modified": "2023-05-04 16:14:29.080697",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Reposting Settings",
@@ -76,5 +89,6 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py b/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py
index fad74d3..a6dc72d 100644
--- a/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py
+++ b/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py
@@ -1,9 +1,40 @@
 # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-# import frappe
 import unittest
 
+import frappe
+
+from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import get_recipients
+
 
 class TestStockRepostingSettings(unittest.TestCase):
-	pass
+	def test_notify_reposting_error_to_role(self):
+		role = "Notify Reposting Role"
+
+		if not frappe.db.exists("Role", role):
+			frappe.get_doc({"doctype": "Role", "role_name": role}).insert(ignore_permissions=True)
+
+		user = "notify_reposting_error@test.com"
+		if not frappe.db.exists("User", user):
+			frappe.get_doc(
+				{
+					"doctype": "User",
+					"email": user,
+					"first_name": "Test",
+					"language": "en",
+					"time_zone": "Asia/Kolkata",
+					"send_welcome_email": 0,
+					"roles": [{"role": role}],
+				}
+			).insert(ignore_permissions=True)
+
+		frappe.db.set_single_value("Stock Reposting Settings", "notify_reposting_error_to_role", "")
+
+		users = get_recipients()
+		self.assertFalse(user in users)
+
+		frappe.db.set_single_value("Stock Reposting Settings", "notify_reposting_error_to_role", role)
+
+		users = get_recipients()
+		self.assertTrue(user in users)
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 8b517bf..103ed4a 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -556,7 +556,7 @@
 			sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
 			and sle.voucher_detail_no
 			and sle.actual_qty < 0
-			and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier")
+			and is_internal_transfer(sle)
 		):
 			sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle)
 
@@ -679,7 +679,7 @@
 			elif (
 				sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
 				and sle.voucher_detail_no
-				and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier")
+				and is_internal_transfer(sle)
 			):
 				rate = get_incoming_rate_for_inter_company_transfer(sle)
 			else:
@@ -1609,3 +1609,15 @@
 		)
 
 	return rate
+
+
+def is_internal_transfer(sle):
+	data = frappe.get_cached_value(
+		sle.voucher_type,
+		sle.voucher_no,
+		["is_internal_supplier", "represents_company", "company"],
+		as_dict=True,
+	)
+
+	if data.is_internal_supplier and data.represents_company == data.company:
+		return True