Merge pull request #39746 from s-aga-r/FIX-9365

fix: show warehouse title field in sales docs
diff --git a/README.md b/README.md
index 710187a..4f65ceb 100644
--- a/README.md
+++ b/README.md
@@ -7,8 +7,7 @@
         <p>ERP made simple</p>
     </p>
 
-[![CI](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml/badge.svg?branch=develop)](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml)
-[![UI](https://github.com/erpnext/erpnext_ui_tests/actions/workflows/ui-tests.yml/badge.svg?branch=develop&event=schedule)](https://github.com/erpnext/erpnext_ui_tests/actions/workflows/ui-tests.yml)
+[![CI](https://github.com/frappe/erpnext/actions/workflows/server-tests-mariadb.yml/badge.svg?event=schedule)](https://github.com/frappe/erpnext/actions/workflows/server-tests-mariadb.yml)
 [![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
 [![codecov](https://codecov.io/gh/frappe/erpnext/branch/develop/graph/badge.svg?token=0TwvyUg3I5)](https://codecov.io/gh/frappe/erpnext)
 [![docker pulls](https://img.shields.io/docker/pulls/frappe/erpnext-worker.svg)](https://hub.docker.com/r/frappe/erpnext-worker)
diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py
index ace4bb1..df4bd56 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account.py
@@ -9,6 +9,7 @@
 	load_address_and_contact,
 )
 from frappe.model.document import Document
+from frappe.utils import comma_and, get_link_to_form
 
 
 class BankAccount(Document):
@@ -52,6 +53,17 @@
 	def validate(self):
 		self.validate_company()
 		self.validate_iban()
+		self.validate_account()
+
+	def validate_account(self):
+		if self.account:
+			if accounts := frappe.db.get_all("Bank Account", filters={"account": self.account}, as_list=1):
+				frappe.throw(
+					_("'{0}' account is already used by {1}. Use another account.").format(
+						frappe.bold(self.account),
+						frappe.bold(comma_and([get_link_to_form(self.doctype, x[0]) for x in accounts])),
+					)
+				)
 
 	def validate_company(self):
 		if self.is_company_account and not self.company:
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
index 7bb3f41..1fe3608 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -32,8 +32,16 @@
 			frappe.db.delete(dt)
 		clear_loan_transactions()
 		make_pos_profile()
-		add_transactions()
-		add_vouchers()
+
+		# generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error
+		uniq_identifier = frappe.generate_hash(length=10)
+		gl_account = create_gl_account("_Test Bank " + uniq_identifier)
+		bank_account = create_bank_account(
+			gl_account=gl_account, bank_account_name="Checking Account " + uniq_identifier
+		)
+
+		add_transactions(bank_account=bank_account)
+		add_vouchers(gl_account=gl_account)
 
 	# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
 	def test_linked_payments(self):
@@ -219,7 +227,9 @@
 	frappe.db.delete("Loan Repayment")
 
 
-def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
+def create_bank_account(
+	bank_name="Citi Bank", gl_account="_Test Bank - _TC", bank_account_name="Checking Account"
+):
 	try:
 		frappe.get_doc(
 			{
@@ -231,21 +241,35 @@
 		pass
 
 	try:
-		frappe.get_doc(
+		bank_account = frappe.get_doc(
 			{
 				"doctype": "Bank Account",
-				"account_name": "Checking Account",
+				"account_name": bank_account_name,
 				"bank": bank_name,
-				"account": account_name,
+				"account": gl_account,
 			}
 		).insert(ignore_if_duplicate=True)
 	except frappe.DuplicateEntryError:
 		pass
 
+	return bank_account.name
 
-def add_transactions():
-	create_bank_account()
 
+def create_gl_account(gl_account_name="_Test Bank - _TC"):
+	gl_account = frappe.get_doc(
+		{
+			"doctype": "Account",
+			"company": "_Test Company",
+			"parent_account": "Current Assets - _TC",
+			"account_type": "Bank",
+			"is_group": 0,
+			"account_name": gl_account_name,
+		}
+	).insert()
+	return gl_account.name
+
+
+def add_transactions(bank_account="_Test Bank - _TC"):
 	doc = frappe.get_doc(
 		{
 			"doctype": "Bank Transaction",
@@ -253,7 +277,7 @@
 			"date": "2018-10-23",
 			"deposit": 1200,
 			"currency": "INR",
-			"bank_account": "Checking Account - Citi Bank",
+			"bank_account": bank_account,
 		}
 	).insert()
 	doc.submit()
@@ -265,7 +289,7 @@
 			"date": "2018-10-23",
 			"deposit": 1700,
 			"currency": "INR",
-			"bank_account": "Checking Account - Citi Bank",
+			"bank_account": bank_account,
 		}
 	).insert()
 	doc.submit()
@@ -277,7 +301,7 @@
 			"date": "2018-10-26",
 			"withdrawal": 690,
 			"currency": "INR",
-			"bank_account": "Checking Account - Citi Bank",
+			"bank_account": bank_account,
 		}
 	).insert()
 	doc.submit()
@@ -289,7 +313,7 @@
 			"date": "2018-10-27",
 			"deposit": 3900,
 			"currency": "INR",
-			"bank_account": "Checking Account - Citi Bank",
+			"bank_account": bank_account,
 		}
 	).insert()
 	doc.submit()
@@ -301,13 +325,13 @@
 			"date": "2018-10-27",
 			"withdrawal": 109080,
 			"currency": "INR",
-			"bank_account": "Checking Account - Citi Bank",
+			"bank_account": bank_account,
 		}
 	).insert()
 	doc.submit()
 
 
-def add_vouchers():
+def add_vouchers(gl_account="_Test Bank - _TC"):
 	try:
 		frappe.get_doc(
 			{
@@ -323,7 +347,7 @@
 
 	pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
 
-	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
+	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
 	pe.reference_no = "Conrad Oct 18"
 	pe.reference_date = "2018-10-24"
 	pe.insert()
@@ -342,14 +366,14 @@
 		pass
 
 	pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1200)
-	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
+	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
 	pe.reference_no = "Herr G Oct 18"
 	pe.reference_date = "2018-10-24"
 	pe.insert()
 	pe.submit()
 
 	pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1700)
-	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
+	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
 	pe.reference_no = "Herr G Nov 18"
 	pe.reference_date = "2018-11-01"
 	pe.insert()
@@ -380,10 +404,10 @@
 		pass
 
 	pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save=1)
-	pi.cash_bank_account = "_Test Bank - _TC"
+	pi.cash_bank_account = gl_account
 	pi.insert()
 	pi.submit()
-	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
+	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
 	pe.reference_no = "Poore Simon's Oct 18"
 	pe.reference_date = "2018-10-28"
 	pe.paid_amount = 690
@@ -392,7 +416,7 @@
 	pe.submit()
 
 	si = create_sales_invoice(customer="Poore Simon's", qty=1, rate=3900)
-	pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
+	pe = get_payment_entry("Sales Invoice", si.name, bank_account=gl_account)
 	pe.reference_no = "Poore Simon's Oct 18"
 	pe.reference_date = "2018-10-28"
 	pe.insert()
@@ -415,16 +439,12 @@
 	if not frappe.db.get_value(
 		"Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}
 	):
-		mode_of_payment.append(
-			"accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"}
-		)
+		mode_of_payment.append("accounts", {"company": "_Test Company", "default_account": gl_account})
 		mode_of_payment.save()
 
 	si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1)
 	si.is_pos = 1
-	si.append(
-		"payments", {"mode_of_payment": "Cash", "account": "_Test Bank - _TC", "amount": 109080}
-	)
+	si.append("payments", {"mode_of_payment": "Cash", "account": gl_account, "amount": 109080})
 	si.insert()
 	si.submit()
 
diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.py b/erpnext/accounts/doctype/payment_order/test_payment_order.py
index 0dcb179..60f288e 100644
--- a/erpnext/accounts/doctype/payment_order/test_payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/test_payment_order.py
@@ -4,9 +4,13 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import getdate
 
-from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import create_bank_account
+from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import (
+	create_bank_account,
+	create_gl_account,
+)
 from erpnext.accounts.doctype.payment_entry.payment_entry import (
 	get_payment_entry,
 	make_payment_order,
@@ -14,28 +18,32 @@
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 
 
-class TestPaymentOrder(unittest.TestCase):
+class TestPaymentOrder(FrappeTestCase):
 	def setUp(self):
-		create_bank_account()
+		# generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error
+		uniq_identifier = frappe.generate_hash(length=10)
+		self.gl_account = create_gl_account("_Test Bank " + uniq_identifier)
+		self.bank_account = create_bank_account(
+			gl_account=self.gl_account, bank_account_name="Checking Account " + uniq_identifier
+		)
 
 	def tearDown(self):
-		for bt in frappe.get_all("Payment Order"):
-			doc = frappe.get_doc("Payment Order", bt.name)
-			doc.cancel()
-			doc.delete()
+		frappe.db.rollback()
 
 	def test_payment_order_creation_against_payment_entry(self):
 		purchase_invoice = make_purchase_invoice()
 		payment_entry = get_payment_entry(
-			"Purchase Invoice", purchase_invoice.name, bank_account="_Test Bank - _TC"
+			"Purchase Invoice", purchase_invoice.name, bank_account=self.gl_account
 		)
 		payment_entry.reference_no = "_Test_Payment_Order"
 		payment_entry.reference_date = getdate()
-		payment_entry.party_bank_account = "Checking Account - Citi Bank"
+		payment_entry.party_bank_account = self.bank_account
 		payment_entry.insert()
 		payment_entry.submit()
 
-		doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry")
+		doc = create_payment_order_against_payment_entry(
+			payment_entry, "Payment Entry", self.bank_account
+		)
 		reference_doc = doc.get("references")[0]
 		self.assertEqual(reference_doc.reference_name, payment_entry.name)
 		self.assertEqual(reference_doc.reference_doctype, "Payment Entry")
@@ -43,13 +51,13 @@
 		self.assertEqual(reference_doc.amount, 250)
 
 
-def create_payment_order_against_payment_entry(ref_doc, order_type):
+def create_payment_order_against_payment_entry(ref_doc, order_type, bank_account):
 	payment_order = frappe.get_doc(
 		dict(
 			doctype="Payment Order",
 			company="_Test Company",
 			payment_order_type=order_type,
-			company_bank_account="Checking Account - Citi Bank",
+			company_bank_account=bank_account,
 		)
 	)
 	doc = make_payment_order(ref_doc.name, payment_order)
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 656ee9b..4d94868 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -645,6 +645,7 @@
 				update_sco_status(sco, "Closed" if self.status == "Closed" else None)
 
 
+@frappe.request_cache
 def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0):
 	"""get last purchase rate for an item"""
 
diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py
index 4cfe5d8..78df755 100644
--- a/erpnext/stock/serial_batch_bundle.py
+++ b/erpnext/stock/serial_batch_bundle.py
@@ -283,6 +283,7 @@
 				if (sn_table.purchase_document_no != self.sle.voucher_no and self.sle.is_cancelled != 1)
 				else "Inactive",
 			)
+			.set(sn_table.company, self.sle.company)
 			.where(sn_table.name.isin(serial_nos))
 		).run()