Merge pull request #40962 from deepeshgarg007/allow_on_submit_ad_v1

fix: Allow updating cost center and project for repostable doctypes
diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index f7c4d90..260ede5 100644
--- a/erpnext/accounts/doctype/dunning/dunning.py
+++ b/erpnext/accounts/doctype/dunning/dunning.py
@@ -146,6 +146,10 @@
 			)
 			row.dunning_level = len(past_dunnings) + 1
 
+	def on_cancel(self):
+		super().on_cancel()
+		self.ignore_linked_doctypes = ["GL Entry"]
+
 
 def resolve_dunning(doc, state):
 	"""
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index c4a99c0..41ed66c 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -92,7 +92,7 @@
 			self.status = "Draft"
 		self.validate_reference_document()
 		self.validate_payment_request_amount()
-		self.validate_currency()
+		# self.validate_currency()
 		self.validate_subscription_details()
 
 	def validate_reference_document(self):
@@ -335,21 +335,17 @@
 			}
 		)
 
+		if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency:
+			amount = payment_entry.base_paid_amount
+		else:
+			amount = self.grand_total
+
+		payment_entry.received_amount = amount
+		payment_entry.get("references")[0].allocated_amount = amount
+
 		for dimension in get_accounting_dimensions():
 			payment_entry.update({dimension: self.get(dimension)})
 
-		if payment_entry.difference_amount:
-			company_details = get_company_defaults(ref_doc.company)
-
-			payment_entry.append(
-				"deductions",
-				{
-					"account": company_details.exchange_gain_loss_account,
-					"cost_center": company_details.cost_center,
-					"amount": payment_entry.difference_amount,
-				},
-			)
-
 		if submit:
 			payment_entry.insert(ignore_permissions=True)
 			payment_entry.submit()
@@ -479,6 +475,12 @@
 		pr = frappe.get_doc("Payment Request", draft_payment_request)
 	else:
 		pr = frappe.new_doc("Payment Request")
+
+		if not args.get("payment_request_type"):
+			args["payment_request_type"] = (
+				"Outward" if args.get("dt") in ["Purchase Order", "Purchase Invoice"] else "Inward"
+			)
+
 		pr.update(
 			{
 				"payment_gateway_account": gateway_account.get("name"),
@@ -538,9 +540,9 @@
 	elif dt in ["Sales Invoice", "Purchase Invoice"]:
 		if not ref_doc.get("is_pos"):
 			if ref_doc.party_account_currency == ref_doc.currency:
-				grand_total = flt(ref_doc.outstanding_amount)
+				grand_total = flt(ref_doc.grand_total)
 			else:
-				grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
+				grand_total = flt(ref_doc.base_grand_total) / ref_doc.conversion_rate
 		elif dt == "Sales Invoice":
 			for pay in ref_doc.payments:
 				if pay.type == "Phone" and pay.account == payment_account:
diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py
index 70de886..9320608 100644
--- a/erpnext/accounts/doctype/payment_request/test_payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py
@@ -86,6 +86,8 @@
 		pr = make_payment_request(
 			dt="Purchase Invoice",
 			dn=si_usd.name,
+			party_type="Supplier",
+			party="_Test Supplier USD",
 			recipient_id="user@example.com",
 			mute_email=1,
 			payment_gateway_account="_Test Gateway - USD",
@@ -98,6 +100,51 @@
 
 		self.assertEqual(pr.status, "Paid")
 
+	def test_multiple_payment_entry_against_purchase_invoice(self):
+		purchase_invoice = make_purchase_invoice(
+			customer="_Test Supplier USD",
+			debit_to="_Test Payable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
+
+		pr = make_payment_request(
+			dt="Purchase Invoice",
+			party_type="Supplier",
+			party="_Test Supplier USD",
+			dn=purchase_invoice.name,
+			recipient_id="user@example.com",
+			mute_email=1,
+			payment_gateway_account="_Test Gateway - USD",
+			return_doc=1,
+		)
+
+		pr.grand_total = pr.grand_total / 2
+
+		pr.submit()
+		pr.create_payment_entry()
+
+		purchase_invoice.load_from_db()
+		self.assertEqual(purchase_invoice.status, "Partly Paid")
+
+		pr = make_payment_request(
+			dt="Purchase Invoice",
+			party_type="Supplier",
+			party="_Test Supplier USD",
+			dn=purchase_invoice.name,
+			recipient_id="user@example.com",
+			mute_email=1,
+			payment_gateway_account="_Test Gateway - USD",
+			return_doc=1,
+		)
+
+		pr.save()
+		pr.submit()
+		pr.create_payment_entry()
+
+		purchase_invoice.load_from_db()
+		self.assertEqual(purchase_invoice.status, "Paid")
+
 	def test_payment_entry(self):
 		frappe.db.set_value(
 			"Company", "_Test Company", "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC"
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 374ecf3..4e14c8a 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -655,13 +655,13 @@
 			elif self.delivery_notes.get((row.parent, row.item_code), None):
 				#  check if Invoice has delivery notes
 				dn = self.delivery_notes.get((row.parent, row.item_code))
-				parenttype, parent, item_row, _warehouse = (
+				parenttype, parent, item_row, dn_warehouse = (
 					"Delivery Note",
 					dn["delivery_note"],
 					dn["item_row"],
 					dn["warehouse"],
 				)
-				my_sle = self.get_stock_ledger_entries(item_code, _warehouse)
+				my_sle = self.get_stock_ledger_entries(item_code, dn_warehouse)
 				return self.calculate_buying_amount_from_sle(
 					row, my_sle, parenttype, parent, item_row, item_code
 				)
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 66097ce..bd7d926 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1439,7 +1439,8 @@
 
 						dr_or_cr = "debit" if d.exchange_gain_loss > 0 else "credit"
 
-						if d.reference_doctype == "Purchase Invoice":
+						# Inverse debit/credit for payable accounts
+						if self.is_payable_account(d.reference_doctype, party_account):
 							dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
 
 						reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
@@ -1473,6 +1474,14 @@
 							)
 						)
 
+	def is_payable_account(self, reference_doctype, account):
+		if reference_doctype == "Purchase Invoice" or (
+			reference_doctype == "Journal Entry"
+			and frappe.get_cached_value("Account", account, "account_type") == "Payable"
+		):
+			return True
+		return False
+
 	def update_against_document_in_jv(self):
 		"""
 		Links invoice and advance voucher:
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 60ba0d1..f87dc4f 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -441,7 +441,7 @@
 				get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return")
 			):
 				# Get incoming rate based on original item cost based on valuation method
-				qty = flt(d.get("stock_qty") or d.get("actual_qty"))
+				qty = flt(d.get("stock_qty") or d.get("actual_qty") or d.get("qty"))
 
 				if (
 					not d.incoming_rate
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 5916f2e..84bf6aa 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -166,7 +166,7 @@
 				# remove extra whitespace and store one serial no on each line
 				row.serial_no = clean_serial_no_string(row.serial_no)
 
-	def make_bundle_using_old_serial_batch_fields(self, table_name=None):
+	def make_bundle_using_old_serial_batch_fields(self, table_name=None, via_landed_cost_voucher=False):
 		if self.get("_action") == "update_after_submit":
 			return
 
@@ -205,7 +205,7 @@
 					"company": self.company,
 					"is_rejected": 1 if row.get("rejected_warehouse") else 0,
 					"use_serial_batch_fields": row.use_serial_batch_fields,
-					"do_not_submit": True,
+					"do_not_submit": True if not via_landed_cost_voucher else False,
 				}
 
 				if row.get("qty") or row.get("consumed_qty"):
@@ -1119,7 +1119,7 @@
 		message += _("Please adjust the qty or edit {0} to proceed.").format(rule_link)
 		return message
 
-	def repost_future_sle_and_gle(self, force=False):
+	def repost_future_sle_and_gle(self, force=False, via_landed_cost_voucher=False):
 		args = frappe._dict(
 			{
 				"posting_date": self.posting_date,
@@ -1127,6 +1127,7 @@
 				"voucher_type": self.doctype,
 				"voucher_no": self.name,
 				"company": self.company,
+				"via_landed_cost_voucher": via_landed_cost_voucher,
 			}
 		)
 
@@ -1138,7 +1139,11 @@
 				frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting")
 			)
 			if item_based_reposting:
-				create_item_wise_repost_entries(voucher_type=self.doctype, voucher_no=self.name)
+				create_item_wise_repost_entries(
+					voucher_type=self.doctype,
+					voucher_no=self.name,
+					via_landed_cost_voucher=via_landed_cost_voucher,
+				)
 			else:
 				create_repost_item_valuation_entry(args)
 
@@ -1510,11 +1515,14 @@
 	repost_entry.allow_zero_rate = args.allow_zero_rate
 	repost_entry.flags.ignore_links = True
 	repost_entry.flags.ignore_permissions = True
+	repost_entry.via_landed_cost_voucher = args.via_landed_cost_voucher
 	repost_entry.save()
 	repost_entry.submit()
 
 
-def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=False):
+def create_item_wise_repost_entries(
+	voucher_type, voucher_no, allow_zero_rate=False, via_landed_cost_voucher=False
+):
 	"""Using a voucher create repost item valuation records for all item-warehouse pairs."""
 
 	stock_ledger_entries = get_items_to_be_repost(voucher_type, voucher_no)
@@ -1538,6 +1546,7 @@
 		repost_entry.allow_zero_rate = allow_zero_rate
 		repost_entry.flags.ignore_links = True
 		repost_entry.flags.ignore_permissions = True
+		repost_entry.via_landed_cost_voucher = via_landed_cost_voucher
 		repost_entry.submit()
 		repost_entries.append(repost_entry)
 
diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py
index 6218bd6..f91acb2 100644
--- a/erpnext/controllers/tests/test_accounts_controller.py
+++ b/erpnext/controllers/tests/test_accounts_controller.py
@@ -135,6 +135,27 @@
 			acc = frappe.get_doc("Account", name)
 		self.debtors_usd = acc.name
 
+		account_name = "Creditors USD"
+		if not frappe.db.get_value(
+			"Account", filters={"account_name": account_name, "company": self.company}
+		):
+			acc = frappe.new_doc("Account")
+			acc.account_name = account_name
+			acc.parent_account = "Accounts Payable - " + self.company_abbr
+			acc.company = self.company
+			acc.account_currency = "USD"
+			acc.account_type = "Payable"
+			acc.insert()
+		else:
+			name = frappe.db.get_value(
+				"Account",
+				filters={"account_name": account_name, "company": self.company},
+				fieldname="name",
+				pluck=True,
+			)
+			acc = frappe.get_doc("Account", name)
+		self.creditors_usd = acc.name
+
 	def create_sales_invoice(
 		self,
 		qty=1,
@@ -174,7 +195,9 @@
 		)
 		return sinv
 
-	def create_payment_entry(self, amount=1, source_exc_rate=75, posting_date=None, customer=None):
+	def create_payment_entry(
+		self, amount=1, source_exc_rate=75, posting_date=None, customer=None, submit=True
+	):
 		"""
 		Helper function to populate default values in payment entry
 		"""
@@ -1606,3 +1629,72 @@
 		exc_je_for_je2 = self.get_journals_for(je2.doctype, je2.name)
 		self.assertEqual(exc_je_for_je1, [])
 		self.assertEqual(exc_je_for_je2, [])
+
+	def test_61_payment_entry_against_journal_for_payable_accounts(self):
+		# Invoices
+		exc_rate1 = 75
+		exc_rate2 = 77
+		amount = 1
+		je1 = self.create_journal_entry(
+			acc1=self.creditors_usd,
+			acc1_exc_rate=exc_rate1,
+			acc2=self.cash,
+			acc1_amount=-amount,
+			acc2_amount=(-amount * 75),
+			acc2_exc_rate=1,
+		)
+		je1.accounts[0].party_type = "Supplier"
+		je1.accounts[0].party = self.supplier
+		je1 = je1.save().submit()
+
+		# Payment
+		pe = create_payment_entry(
+			company=self.company,
+			payment_type="Pay",
+			party_type="Supplier",
+			party=self.supplier,
+			paid_from=self.cash,
+			paid_to=self.creditors_usd,
+			paid_amount=amount,
+		)
+		pe.target_exchange_rate = exc_rate2
+		pe.received_amount = amount
+		pe.paid_amount = amount * exc_rate2
+		pe.save().submit()
+
+		pr = frappe.get_doc(
+			{
+				"doctype": "Payment Reconciliation",
+				"company": self.company,
+				"party_type": "Supplier",
+				"party": self.supplier,
+				"receivable_payable_account": get_party_account("Supplier", self.supplier, self.company),
+			}
+		)
+		pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate()
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.invoices), 1)
+		self.assertEqual(len(pr.payments), 1)
+		invoices = [x.as_dict() for x in pr.invoices]
+		payments = [x.as_dict() for x in pr.payments]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.reconcile()
+		self.assertEqual(len(pr.invoices), 0)
+		self.assertEqual(len(pr.payments), 0)
+
+		# There should be no outstanding in both currencies
+		self.assert_ledger_outstanding(je1.doctype, je1.name, 0.0, 0.0)
+
+		# Exchange Gain/Loss Journal should've been created
+		exc_je_for_je1 = self.get_journals_for(je1.doctype, je1.name)
+		self.assertEqual(len(exc_je_for_je1), 1)
+
+		# Cancel Payment
+		pe.reload()
+		pe.cancel()
+
+		self.assert_ledger_outstanding(je1.doctype, je1.name, (amount * exc_rate1), amount)
+
+		# Exchange Gain/Loss Journal should've been cancelled
+		exc_je_for_je1 = self.get_journals_for(je1.doctype, je1.name)
+		self.assertEqual(exc_je_for_je1, [])
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index a31f011..c2a028b 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -534,6 +534,7 @@
 	"Supplier Quotation Item",
 	"Payment Reconciliation",
 	"Payment Reconciliation Allocation",
+	"Payment Request",
 ]
 
 get_matching_queries = (
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 7c2c439..263501f 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -357,6 +357,7 @@
 erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes
 erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool
 erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22
+erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request
 # below migration patch should always run last
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
 erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
diff --git a/erpnext/patches/v15_0/create_accounting_dimensions_in_payment_request.py b/erpnext/patches/v15_0/create_accounting_dimensions_in_payment_request.py
new file mode 100644
index 0000000..fc50b60
--- /dev/null
+++ b/erpnext/patches/v15_0/create_accounting_dimensions_in_payment_request.py
@@ -0,0 +1,7 @@
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+	create_accounting_dimensions_for_doctype,
+)
+
+
+def execute():
+	create_accounting_dimensions_for_doctype(doctype="Payment Request")
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 17fc2b7..4b23af1 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -384,7 +384,6 @@
 				)
 
 		target.flags.ignore_permissions = ignore_permissions
-		target.delivery_date = nowdate()
 		target.run_method("set_missing_values")
 		target.run_method("calculate_taxes_and_totals")
 
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index 57c14db..01dfe80 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -134,6 +134,7 @@
 
 		sales_order.naming_series = "_T-Quotation-"
 		sales_order.transaction_date = nowdate()
+		sales_order.delivery_date = nowdate()
 		sales_order.insert()
 
 	def test_make_sales_order_with_terms(self):
@@ -164,6 +165,7 @@
 
 		sales_order.naming_series = "_T-Quotation-"
 		sales_order.transaction_date = nowdate()
+		sales_order.delivery_date = nowdate()
 		sales_order.insert()
 
 		# Remove any unknown taxes if applied
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 69bbae3..b33562c 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -169,6 +169,27 @@
 		);
 	},
 
+	// When multiple companies are set up. in case company name is changed set default company address
+	company: function (frm) {
+		if (frm.doc.company) {
+			frappe.call({
+				method: "erpnext.setup.doctype.company.company.get_default_company_address",
+				args: {
+					name: frm.doc.company,
+					existing_address: frm.doc.company_address || "",
+				},
+				debounce: 2000,
+				callback: function (r) {
+					if (r.message) {
+						frm.set_value("company_address", r.message);
+					} else {
+						frm.set_value("company_address", "");
+					}
+				},
+			});
+		}
+	},
+
 	onload: function (frm) {
 		if (!frm.doc.transaction_date) {
 			frm.set_value("transaction_date", frappe.datetime.get_today());
@@ -288,6 +309,7 @@
 					label: __("Items to Reserve"),
 					allow_bulk_edit: false,
 					cannot_add_rows: true,
+					cannot_delete_rows: true,
 					data: [],
 					fields: [
 						{
@@ -356,7 +378,7 @@
 			],
 			primary_action_label: __("Reserve Stock"),
 			primary_action: () => {
-				var data = { items: dialog.fields_dict.items.grid.data };
+				var data = { items: dialog.fields_dict.items.grid.get_selected_children() };
 
 				if (data.items && data.items.length > 0) {
 					frappe.call({
@@ -373,9 +395,11 @@
 							frm.reload_doc();
 						},
 					});
-				}
 
-				dialog.hide();
+					dialog.hide();
+				} else {
+					frappe.msgprint(__("Please select items to reserve."));
+				}
 			},
 		});
 
@@ -390,6 +414,7 @@
 
 				if (unreserved_qty > 0) {
 					dialog.fields_dict.items.df.data.push({
+						__checked: 1,
 						sales_order_item: item.name,
 						item_code: item.item_code,
 						warehouse: item.warehouse,
@@ -414,6 +439,7 @@
 					label: __("Reserved Stock"),
 					allow_bulk_edit: false,
 					cannot_add_rows: true,
+					cannot_delete_rows: true,
 					in_place_edit: true,
 					data: [],
 					fields: [
@@ -457,7 +483,7 @@
 			],
 			primary_action_label: __("Unreserve Stock"),
 			primary_action: () => {
-				var data = { sr_entries: dialog.fields_dict.sr_entries.grid.data };
+				var data = { sr_entries: dialog.fields_dict.sr_entries.grid.get_selected_children() };
 
 				if (data.sr_entries && data.sr_entries.length > 0) {
 					frappe.call({
@@ -473,9 +499,11 @@
 							frm.reload_doc();
 						},
 					});
-				}
 
-				dialog.hide();
+					dialog.hide();
+				} else {
+					frappe.msgprint(__("Please select items to unreserve."));
+				}
 			},
 		});
 
diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py
index 0b783c0..7b725c3 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.py
+++ b/erpnext/setup/doctype/customer_group/customer_group.py
@@ -40,14 +40,9 @@
 			self.parent_customer_group = get_root_of("Customer Group")
 
 	def on_update(self):
-		self.validate_name_with_customer()
 		super().on_update()
 		self.validate_one_root()
 
-	def validate_name_with_customer(self):
-		if frappe.db.exists("Customer", self.name):
-			frappe.msgprint(_("A customer with the same name already exists"), raise_exception=1)
-
 
 def get_parent_customer_groups(customer_group):
 	lft, rgt = frappe.db.get_value("Customer Group", customer_group, ["lft", "rgt"])
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index 5e5efb5..4325f26 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -250,9 +250,10 @@
 
 			# update stock & gl entries for submit state of PR
 			doc.docstatus = 1
+			doc.make_bundle_using_old_serial_batch_fields(via_landed_cost_voucher=True)
 			doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
 			doc.make_gl_entries()
-			doc.repost_future_sle_and_gle()
+			doc.repost_future_sle_and_gle(via_landed_cost_voucher=True)
 
 	def validate_asset_qty_and_status(self, receipt_document_type, receipt_document):
 		for item in self.get("items"):
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 9ec2d69..13b7f97 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -596,6 +596,356 @@
 		lcv.cancel()
 		pr.cancel()
 
+	def test_landed_cost_voucher_with_serial_batch_for_legacy_pr(self):
+		from erpnext.stock.doctype.item.test_item import make_item
+
+		frappe.flags.ignore_serial_batch_bundle_validation = True
+		frappe.flags.use_serial_and_batch_fields = True
+		sn_item = "Test Landed Cost Voucher Serial NO for Legacy PR"
+		batch_item = "Test Landed Cost Voucher Batch NO for Legacy PR"
+		sn_item_doc = make_item(
+			sn_item,
+			{
+				"has_serial_no": 1,
+				"serial_no_series": "SN-TLCVSNO-.####",
+				"is_stock_item": 1,
+			},
+		)
+
+		batch_item_doc = make_item(
+			batch_item,
+			{
+				"has_batch_no": 1,
+				"batch_number_series": "BATCH-TLCVSNO-.####",
+				"create_new_batch": 1,
+				"is_stock_item": 1,
+			},
+		)
+
+		serial_nos = [
+			"SN-TLCVSNO-0001",
+			"SN-TLCVSNO-0002",
+			"SN-TLCVSNO-0003",
+			"SN-TLCVSNO-0004",
+			"SN-TLCVSNO-0005",
+		]
+
+		for sn in serial_nos:
+			if not frappe.db.exists("Serial No", sn):
+				sn_doc = frappe.get_doc(
+					{
+						"doctype": "Serial No",
+						"item_code": sn_item,
+						"serial_no": sn,
+					}
+				)
+				sn_doc.insert()
+
+		if not frappe.db.exists("Batch", "BATCH-TLCVSNO-0001"):
+			batch_doc = frappe.get_doc(
+				{
+					"doctype": "Batch",
+					"item": batch_item,
+					"batch_id": "BATCH-TLCVSNO-0001",
+				}
+			)
+			batch_doc.insert()
+
+		warehouse = "_Test Warehouse - _TC"
+		company = frappe.db.get_value("Warehouse", warehouse, "company")
+
+		pr = make_purchase_receipt(
+			company=company,
+			warehouse=warehouse,
+			item_code=sn_item,
+			qty=5,
+			rate=100,
+			uom=sn_item_doc.stock_uom,
+			stock_uom=sn_item_doc.stock_uom,
+			do_not_submit=True,
+		)
+
+		pr.append(
+			"items",
+			{
+				"item_code": batch_item,
+				"item_name": batch_item,
+				"description": "Test Batch Item",
+				"uom": batch_item_doc.stock_uom,
+				"stock_uom": batch_item_doc.stock_uom,
+				"qty": 5,
+				"rate": 100,
+				"warehouse": warehouse,
+			},
+		)
+
+		pr.submit()
+		pr.reload()
+
+		for row in pr.items:
+			self.assertEqual(row.valuation_rate, 100)
+			self.assertFalse(row.serial_no)
+			self.assertFalse(row.batch_no)
+			self.assertFalse(row.serial_and_batch_bundle)
+
+			if row.item_code == sn_item:
+				row.db_set("serial_no", ", ".join(serial_nos))
+			else:
+				row.db_set("batch_no", "BATCH-TLCVSNO-0001")
+
+		for sn in serial_nos:
+			sn_doc = frappe.get_doc("Serial No", sn)
+			sn_doc.db_set(
+				{
+					"warehouse": warehouse,
+					"status": "Active",
+				}
+			)
+
+		batch_doc.db_set(
+			{
+				"batch_qty": 5,
+			}
+		)
+
+		frappe.flags.ignore_serial_batch_bundle_validation = False
+		frappe.flags.use_serial_and_batch_fields = False
+
+		lcv = make_landed_cost_voucher(
+			company=pr.company,
+			receipt_document_type="Purchase Receipt",
+			receipt_document=pr.name,
+			charges=20,
+			distribute_charges_based_on="Qty",
+			do_not_save=True,
+		)
+
+		lcv.get_items_from_purchase_receipts()
+		lcv.save()
+		lcv.submit()
+
+		pr.reload()
+
+		for row in pr.items:
+			self.assertEqual(row.valuation_rate, 102)
+			self.assertTrue(row.serial_and_batch_bundle)
+			self.assertEqual(
+				row.valuation_rate,
+				frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"),
+			)
+
+		lcv.cancel()
+		pr.reload()
+
+		for row in pr.items:
+			self.assertEqual(row.valuation_rate, 100)
+			self.assertTrue(row.serial_and_batch_bundle)
+			self.assertEqual(
+				row.valuation_rate,
+				frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"),
+			)
+
+	def test_do_not_validate_landed_cost_voucher_with_serial_batch_for_legacy_pr(self):
+		from erpnext.stock.doctype.item.test_item import make_item
+		from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import get_auto_batch_nos
+
+		frappe.flags.ignore_serial_batch_bundle_validation = True
+		frappe.flags.use_serial_and_batch_fields = True
+		sn_item = "Test Don't Validate Landed Cost Voucher Serial NO for Legacy PR"
+		batch_item = "Test Don't Validate Landed Cost Voucher Batch NO for Legacy PR"
+		sn_item_doc = make_item(
+			sn_item,
+			{
+				"has_serial_no": 1,
+				"serial_no_series": "SN-TDVLCVSNO-.####",
+				"is_stock_item": 1,
+			},
+		)
+
+		batch_item_doc = make_item(
+			batch_item,
+			{
+				"has_batch_no": 1,
+				"batch_number_series": "BATCH-TDVLCVSNO-.####",
+				"create_new_batch": 1,
+				"is_stock_item": 1,
+			},
+		)
+
+		serial_nos = [
+			"SN-TDVLCVSNO-0001",
+			"SN-TDVLCVSNO-0002",
+			"SN-TDVLCVSNO-0003",
+			"SN-TDVLCVSNO-0004",
+			"SN-TDVLCVSNO-0005",
+		]
+
+		for sn in serial_nos:
+			if not frappe.db.exists("Serial No", sn):
+				sn_doc = frappe.get_doc(
+					{
+						"doctype": "Serial No",
+						"item_code": sn_item,
+						"serial_no": sn,
+					}
+				)
+				sn_doc.insert()
+
+		if not frappe.db.exists("Batch", "BATCH-TDVLCVSNO-0001"):
+			batch_doc = frappe.get_doc(
+				{
+					"doctype": "Batch",
+					"item": batch_item,
+					"batch_id": "BATCH-TDVLCVSNO-0001",
+				}
+			)
+			batch_doc.insert()
+
+		warehouse = "_Test Warehouse - _TC"
+		company = frappe.db.get_value("Warehouse", warehouse, "company")
+
+		pr = make_purchase_receipt(
+			company=company,
+			warehouse=warehouse,
+			item_code=sn_item,
+			qty=5,
+			rate=100,
+			uom=sn_item_doc.stock_uom,
+			stock_uom=sn_item_doc.stock_uom,
+			do_not_submit=True,
+		)
+
+		pr.append(
+			"items",
+			{
+				"item_code": batch_item,
+				"item_name": batch_item,
+				"description": "Test Batch Item",
+				"uom": batch_item_doc.stock_uom,
+				"stock_uom": batch_item_doc.stock_uom,
+				"qty": 5,
+				"rate": 100,
+				"warehouse": warehouse,
+			},
+		)
+
+		pr.submit()
+		pr.reload()
+
+		for sn in serial_nos:
+			sn_doc = frappe.get_doc("Serial No", sn)
+			sn_doc.db_set(
+				{
+					"warehouse": warehouse,
+					"status": "Active",
+				}
+			)
+
+		batch_doc.db_set(
+			{
+				"batch_qty": 5,
+			}
+		)
+
+		for row in pr.items:
+			if row.item_code == sn_item:
+				row.db_set("serial_no", ", ".join(serial_nos))
+			else:
+				row.db_set("batch_no", "BATCH-TDVLCVSNO-0001")
+
+		stock_ledger_entries = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": pr.name})
+		for sle in stock_ledger_entries:
+			doc = frappe.get_doc("Stock Ledger Entry", sle.name)
+			if doc.item_code == sn_item:
+				doc.db_set("serial_no", ", ".join(serial_nos))
+			else:
+				doc.db_set("batch_no", "BATCH-TDVLCVSNO-0001")
+
+		dn = create_delivery_note(
+			company=company,
+			warehouse=warehouse,
+			item_code=sn_item,
+			qty=5,
+			rate=100,
+			uom=sn_item_doc.stock_uom,
+			stock_uom=sn_item_doc.stock_uom,
+			do_not_submit=True,
+		)
+
+		dn.append(
+			"items",
+			{
+				"item_code": batch_item,
+				"item_name": batch_item,
+				"description": "Test Batch Item",
+				"uom": batch_item_doc.stock_uom,
+				"stock_uom": batch_item_doc.stock_uom,
+				"qty": 5,
+				"rate": 100,
+				"warehouse": warehouse,
+			},
+		)
+
+		dn.submit()
+
+		stock_ledger_entries = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": dn.name})
+		for sle in stock_ledger_entries:
+			doc = frappe.get_doc("Stock Ledger Entry", sle.name)
+			if doc.item_code == sn_item:
+				doc.db_set("serial_no", ", ".join(serial_nos))
+			else:
+				doc.db_set("batch_no", "BATCH-TDVLCVSNO-0001")
+
+		available_batches = get_auto_batch_nos(
+			frappe._dict(
+				{
+					"item_code": batch_item,
+					"warehouse": warehouse,
+					"batch_no": ["BATCH-TDVLCVSNO-0001"],
+					"consider_negative_batches": True,
+				}
+			)
+		)[0]
+
+		self.assertFalse(available_batches.get("qty"))
+
+		frappe.flags.ignore_serial_batch_bundle_validation = False
+		frappe.flags.use_serial_and_batch_fields = False
+
+		lcv = make_landed_cost_voucher(
+			company=pr.company,
+			receipt_document_type="Purchase Receipt",
+			receipt_document=pr.name,
+			charges=20,
+			distribute_charges_based_on="Qty",
+			do_not_save=True,
+		)
+
+		lcv.get_items_from_purchase_receipts()
+		lcv.save()
+		lcv.submit()
+
+		pr.reload()
+
+		for row in pr.items:
+			self.assertEqual(row.valuation_rate, 102)
+			self.assertTrue(row.serial_and_batch_bundle)
+			self.assertEqual(
+				row.valuation_rate,
+				frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"),
+			)
+
+		lcv.cancel()
+		pr.reload()
+
+		for row in pr.items:
+			self.assertEqual(row.valuation_rate, 100)
+			self.assertTrue(row.serial_and_batch_bundle)
+			self.assertEqual(
+				row.valuation_rate,
+				frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"),
+			)
+
 
 def make_landed_cost_voucher(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 19aad3f..286a220 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -860,6 +860,12 @@
 		self.validate_batch_inventory()
 
 	def validate_batch_inventory(self):
+		if (
+			self.voucher_type in ["Purchase Invoice", "Purchase Receipt"]
+			and frappe.db.get_value(self.voucher_type, self.voucher_no, "docstatus") == 1
+		):
+			return
+
 		if not self.has_batch_no:
 			return
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 238de34..feb0f64 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -9,7 +9,17 @@
 from frappe import _
 from frappe.model.mapper import get_mapped_doc
 from frappe.query_builder.functions import Sum
-from frappe.utils import cint, comma_or, cstr, flt, format_time, formatdate, getdate, nowdate
+from frappe.utils import (
+	cint,
+	comma_or,
+	cstr,
+	flt,
+	format_time,
+	formatdate,
+	get_link_to_form,
+	getdate,
+	nowdate,
+)
 
 import erpnext
 from erpnext.accounts.general_ledger import process_gl_map
@@ -645,8 +655,8 @@
 						)
 					)
 
-				work_order_link = frappe.utils.get_link_to_form("Work Order", self.work_order)
-				job_card_link = frappe.utils.get_link_to_form("Job Card", job_card)
+				work_order_link = get_link_to_form("Work Order", self.work_order)
+				job_card_link = get_link_to_form("Job Card", job_card)
 				frappe.throw(
 					_(
 						"Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}."
@@ -1355,9 +1365,24 @@
 
 		return finished_item_row
 
+	def validate_serial_batch_bundle_type(self, serial_and_batch_bundle):
+		if (
+			frappe.db.get_value("Serial and Batch Bundle", serial_and_batch_bundle, "type_of_transaction")
+			!= "Outward"
+		):
+			frappe.throw(
+				_(
+					"The Serial and Batch Bundle {0} is not valid for this transaction. The 'Type of Transaction' should be 'Outward' instead of 'Inward' in Serial and Batch Bundle {0}"
+				).format(get_link_to_form("Serial and Batch Bundle", serial_and_batch_bundle)),
+				title=_("Invalid Serial and Batch Bundle"),
+			)
+
 	def get_sle_for_source_warehouse(self, sl_entries, finished_item_row):
 		for d in self.get("items"):
 			if cstr(d.s_warehouse):
+				if d.serial_and_batch_bundle and self.docstatus == 1:
+					self.validate_serial_batch_bundle_type(d.serial_and_batch_bundle)
+
 				sle = self.get_sl_entries(
 					d,
 					{
@@ -1374,6 +1399,21 @@
 				):
 					sle.dependant_sle_voucher_detail_no = finished_item_row.name
 
+				if sle.serial_and_batch_bundle and self.docstatus == 2:
+					bundle_id = frappe.get_cached_value(
+						"Serial and Batch Bundle",
+						{
+							"voucher_detail_no": d.name,
+							"voucher_no": self.name,
+							"is_cancelled": 0,
+							"type_of_transaction": "Outward",
+						},
+						"name",
+					)
+
+					if bundle_id:
+						sle.serial_and_batch_bundle = bundle_id
+
 				sl_entries.append(sle)
 
 	def make_serial_and_batch_bundle_for_transfer(self):
@@ -1606,11 +1646,7 @@
 			ret.update(get_uom_details(args.get("item_code"), args.get("uom"), args.get("qty")))
 
 		if self.purpose == "Material Issue":
-			ret["expense_account"] = (
-				item.get("expense_account")
-				or item_group_defaults.get("expense_account")
-				or frappe.get_cached_value("Company", self.company, "default_expense_account")
-			)
+			ret["expense_account"] = item.get("expense_account") or item_group_defaults.get("expense_account")
 
 		for company_field, field in {
 			"stock_adjustment_account": "expense_account",
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 34b4ac2..1a7b01c 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -1754,6 +1754,41 @@
 			self.assertTrue(frappe.db.exists("Serial No", serial_no))
 			self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Delivered")
 
+	def test_serial_batch_bundle_type_of_transaction(self):
+		item = make_item(
+			"Test Use Serial and Batch Item SN Item",
+			{
+				"has_batch_no": 1,
+				"is_stock_item": 1,
+				"create_new_batch": 1,
+				"batch_naming_series": "Test-SBBTYT-NNS.#####",
+			},
+		).name
+
+		se = make_stock_entry(
+			item_code=item,
+			qty=2,
+			target="_Test Warehouse - _TC",
+			use_serial_batch_fields=1,
+		)
+
+		batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
+
+		se = make_stock_entry(
+			item_code=item,
+			qty=2,
+			source="_Test Warehouse - _TC",
+			target="Stores - _TC",
+			use_serial_batch_fields=0,
+			batch_no=batch_no,
+			do_not_submit=True,
+		)
+
+		se.reload()
+		sbb = se.items[0].serial_and_batch_bundle
+		frappe.db.set_value("Serial and Batch Bundle", sbb, "type_of_transaction", "Inward")
+		self.assertRaises(frappe.ValidationError, se.submit)
+
 
 def make_serialized_item(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.py b/erpnext/stock/report/delayed_item_report/delayed_item_report.py
index 0bfb4da..88a188e 100644
--- a/erpnext/stock/report/delayed_item_report/delayed_item_report.py
+++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.py
@@ -86,7 +86,11 @@
 			filters = {"parent": ("in", sales_orders), "name": ("in", sales_order_items)}
 
 		so_data = {}
-		for d in frappe.get_all(doctype, filters=filters, fields=["delivery_date", "parent", "name"]):
+		fields = ["delivery_date", "name"]
+		if frappe.db.has_column(doctype, "parent"):
+			fields.append("parent")
+
+		for d in frappe.get_all(doctype, filters=filters, fields=fields):
 			key = d.name if consolidated else (d.parent, d.name)
 			if key not in so_data:
 				so_data.setdefault(key, d.delivery_date)
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
index 49b7b0f..52193c5 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
@@ -498,8 +498,6 @@
 		return process_gl_map(gl_entries)
 
 	def make_item_gl_entries(self, gl_entries, warehouse_account=None):
-		stock_rbnb = self.get_company_default("stock_received_but_not_billed")
-
 		warehouse_with_no_account = []
 
 		for item in self.items:
@@ -517,31 +515,41 @@
 						"stock_value_difference",
 					)
 
-					warehouse_account_name = warehouse_account[item.warehouse]["account"]
-					warehouse_account_currency = warehouse_account[item.warehouse]["account_currency"]
+					accepted_warehouse_account = warehouse_account[item.warehouse]["account"]
 					supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get(
 						"account"
 					)
-					supplier_warehouse_account_currency = warehouse_account.get(
-						self.supplier_warehouse, {}
-					).get("account_currency")
 					remarks = self.get("remarks") or _("Accounting Entry for Stock")
 
-					# FG Warehouse Account (Debit)
+					# Accepted Warehouse Account (Debit)
 					self.add_gl_entry(
 						gl_entries=gl_entries,
-						account=warehouse_account_name,
+						account=accepted_warehouse_account,
 						cost_center=item.cost_center,
 						debit=stock_value_diff,
 						credit=0.0,
 						remarks=remarks,
-						against_account=stock_rbnb,
-						account_currency=warehouse_account_currency,
+						against_account=item.expense_account,
+						account_currency=get_account_currency(accepted_warehouse_account),
+						project=item.project,
+						item=item,
+					)
+					# Expense Account (Credit)
+					self.add_gl_entry(
+						gl_entries=gl_entries,
+						account=item.expense_account,
+						cost_center=item.cost_center,
+						debit=0.0,
+						credit=stock_value_diff,
+						remarks=remarks,
+						against_account=accepted_warehouse_account,
+						account_currency=get_account_currency(item.expense_account),
+						project=item.project,
 						item=item,
 					)
 
-					# Supplier Warehouse Account (Credit)
-					if flt(item.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
+					if flt(item.rm_supp_cost) and supplier_warehouse_account:
+						# Supplier Warehouse Account (Credit)
 						self.add_gl_entry(
 							gl_entries=gl_entries,
 							account=supplier_warehouse_account,
@@ -549,40 +557,66 @@
 							debit=0.0,
 							credit=flt(item.rm_supp_cost),
 							remarks=remarks,
-							against_account=warehouse_account_name,
-							account_currency=supplier_warehouse_account_currency,
+							against_account=item.expense_account,
+							account_currency=get_account_currency(supplier_warehouse_account),
+							project=item.project,
 							item=item,
 						)
-
-					# Expense Account (Credit)
-					if flt(item.service_cost_per_qty):
+						# Expense Account (Debit)
 						self.add_gl_entry(
 							gl_entries=gl_entries,
 							account=item.expense_account,
 							cost_center=item.cost_center,
-							debit=0.0,
-							credit=flt(item.service_cost_per_qty) * flt(item.qty),
+							debit=flt(item.rm_supp_cost),
+							credit=0.0,
 							remarks=remarks,
-							against_account=warehouse_account_name,
+							against_account=supplier_warehouse_account,
 							account_currency=get_account_currency(item.expense_account),
+							project=item.project,
 							item=item,
 						)
 
-					# Loss Account (Credit)
-					divisional_loss = flt(item.amount - stock_value_diff, item.precision("amount"))
+					# Expense Account (Debit)
+					if item.additional_cost_per_qty:
+						self.add_gl_entry(
+							gl_entries=gl_entries,
+							account=item.expense_account,
+							cost_center=self.cost_center or self.get_company_default("cost_center"),
+							debit=item.qty * item.additional_cost_per_qty,
+							credit=0.0,
+							remarks=remarks,
+							against_account=None,
+							account_currency=get_account_currency(item.expense_account),
+						)
 
-					if divisional_loss:
-						loss_account = item.expense_account
+					if divisional_loss := flt(item.amount - stock_value_diff, item.precision("amount")):
+						loss_account = self.get_company_default(
+							"stock_adjustment_account", ignore_validation=True
+						)
 
+						# Loss Account (Credit)
 						self.add_gl_entry(
 							gl_entries=gl_entries,
 							account=loss_account,
 							cost_center=item.cost_center,
+							debit=0.0,
+							credit=divisional_loss,
+							remarks=remarks,
+							against_account=item.expense_account,
+							account_currency=get_account_currency(loss_account),
+							project=item.project,
+							item=item,
+						)
+						# Expense Account (Debit)
+						self.add_gl_entry(
+							gl_entries=gl_entries,
+							account=item.expense_account,
+							cost_center=item.cost_center,
 							debit=divisional_loss,
 							credit=0.0,
 							remarks=remarks,
-							against_account=warehouse_account_name,
-							account_currency=get_account_currency(loss_account),
+							against_account=loss_account,
+							account_currency=get_account_currency(item.expense_account),
 							project=item.project,
 							item=item,
 						)
@@ -592,7 +626,6 @@
 				):
 					warehouse_with_no_account.append(item.warehouse)
 
-		# Additional Costs Expense Accounts (Credit)
 		for row in self.additional_costs:
 			credit_amount = (
 				flt(row.base_amount)
@@ -600,6 +633,7 @@
 				else flt(row.amount)
 			)
 
+			# Additional Cost Expense Account (Credit)
 			self.add_gl_entry(
 				gl_entries=gl_entries,
 				account=row.expense_account,
@@ -608,6 +642,7 @@
 				credit=credit_amount,
 				remarks=remarks,
 				against_account=None,
+				account_currency=get_account_currency(row.expense_account),
 			)
 
 		if warehouse_with_no_account:
@@ -687,6 +722,7 @@
 						"purchase_order": item.purchase_order,
 						"purchase_order_item": item.purchase_order_item,
 						"subcontracting_receipt_item": item.name,
+						"project": po_item.project,
 					}
 					target_doc.append("items", item_row)
 
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
index 564096f..996a990 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
@@ -10,6 +10,7 @@
 
 import erpnext
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
+from erpnext.accounts.utils import get_company_default
 from erpnext.controllers.sales_and_purchase_return import make_return_doc
 from erpnext.controllers.tests.test_subcontracting_controller import (
 	get_rm_items,
@@ -351,26 +352,15 @@
 		self.assertEqual(cint(erpnext.is_perpetual_inventory_enabled(scr.company)), 1)
 
 		gl_entries = get_gl_entries("Subcontracting Receipt", scr.name)
-
 		self.assertTrue(gl_entries)
 
 		fg_warehouse_ac = get_inventory_account(scr.company, scr.items[0].warehouse)
-		supplier_warehouse_ac = get_inventory_account(scr.company, scr.supplier_warehouse)
 		expense_account = scr.items[0].expense_account
-
-		if fg_warehouse_ac == supplier_warehouse_ac:
-			expected_values = {
-				fg_warehouse_ac: [2100.0, 1000.0],  # FG Amount (D), RM Cost (C)
-				expense_account: [0.0, 1000.0],  # Service Cost (C)
-				additional_costs_expense_account: [0.0, 100.0],  # Additional Cost (C)
-			}
-		else:
-			expected_values = {
-				fg_warehouse_ac: [2100.0, 0.0],  # FG Amount (D)
-				supplier_warehouse_ac: [0.0, 1000.0],  # RM Cost (C)
-				expense_account: [0.0, 1000.0],  # Service Cost (C)
-				additional_costs_expense_account: [0.0, 100.0],  # Additional Cost (C)
-			}
+		expected_values = {
+			fg_warehouse_ac: [2100.0, 1000],
+			expense_account: [1100, 2100],
+			additional_costs_expense_account: [0.0, 100.0],
+		}
 
 		for gle in gl_entries:
 			self.assertEqual(expected_values[gle.account][0], gle.debit)
@@ -381,6 +371,53 @@
 		self.assertTrue(get_gl_entries("Subcontracting Receipt", scr.name))
 		frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
 
+	@change_settings("Stock Settings", {"use_serial_batch_fields": 0})
+	def test_subcontracting_receipt_with_zero_service_cost(self):
+		warehouse = "Stores - TCP1"
+		service_items = [
+			{
+				"warehouse": warehouse,
+				"item_code": "Subcontracted Service Item 7",
+				"qty": 10,
+				"rate": 0,
+				"fg_item": "Subcontracted Item SA7",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(
+			company="_Test Company with perpetual inventory",
+			warehouse=warehouse,
+			supplier_warehouse="Work In Progress - TCP1",
+			service_items=service_items,
+		)
+		rm_items = get_rm_items(sco.supplied_items)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		scr = make_subcontracting_receipt(sco.name)
+		scr.save()
+		scr.submit()
+
+		gl_entries = get_gl_entries("Subcontracting Receipt", scr.name)
+		self.assertTrue(gl_entries)
+
+		fg_warehouse_ac = get_inventory_account(scr.company, scr.items[0].warehouse)
+		expense_account = scr.items[0].expense_account
+		expected_values = {
+			fg_warehouse_ac: [1000, 1000],
+			expense_account: [1000, 1000],
+		}
+
+		for gle in gl_entries:
+			self.assertEqual(expected_values[gle.account][0], gle.debit)
+			self.assertEqual(expected_values[gle.account][1], gle.credit)
+
+		scr.reload()
+		scr.cancel()
+
 	def test_supplied_items_consumed_qty(self):
 		# Set Backflush Based On as "Material Transferred for Subcontracting" to transfer RM's more than the required qty
 		set_backflush_based_on("Material Transferred for Subcontract")