chore: rebase
diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml
index ccdfc8c..1e5125e 100644
--- a/.github/workflows/server-tests-mariadb.yml
+++ b/.github/workflows/server-tests-mariadb.yml
@@ -31,6 +31,9 @@
   test:
     runs-on: ubuntu-latest
     timeout-minutes: 60
+    env:
+      NODE_ENV: "production"
+      WITH_COVERAGE: ${{ github.event_name != 'pull_request' }}
 
     strategy:
       fail-fast: false
@@ -117,11 +120,11 @@
           FRAPPE_BRANCH: ${{ github.event.inputs.branch }}
 
       - name: Run Tests
-        run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --with-coverage --total-builds 4 --build-number ${{ matrix.container }}'
+        run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --total-builds 4 --build-number ${{ matrix.container }}'
         env:
           TYPE: server
-          CI_BUILD_ID: ${{ github.run_id }}
-          ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
+          CAPTURE_COVERAGE: ${{ github.event_name != 'pull_request' }}
+
 
       - name: Show bench output
         if: ${{ always() }}
@@ -129,6 +132,7 @@
 
       - name: Upload coverage data
         uses: actions/upload-artifact@v3
+        if: github.event_name != 'pull_request'
         with:
           name: coverage-${{ matrix.container }}
           path: /home/runner/frappe-bench/sites/coverage.xml
@@ -137,6 +141,7 @@
     name: Coverage Wrap Up
     needs: test
     runs-on: ubuntu-latest
+    if: ${{ github.event_name != 'pull_request' }}
     steps:
       - name: Clone
         uses: actions/checkout@v2
@@ -148,5 +153,6 @@
         uses: codecov/codecov-action@v2
         with:
           name: MariaDB
+          token: ${{ secrets.CODECOV_TOKEN }}
           fail_ci_if_error: true
           verbose: true
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/account/account.py b/erpnext/accounts/doctype/account/account.py
index 651599d..3f11798 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -118,6 +118,7 @@
 		self.validate_balance_must_be_debit_or_credit()
 		self.validate_account_currency()
 		self.validate_root_company_and_sync_account_to_children()
+		self.validate_receivable_payable_account_type()
 
 	def validate_parent_child_account_type(self):
 		if self.parent_account:
@@ -188,6 +189,24 @@
 				"Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
 			)
 
+	def validate_receivable_payable_account_type(self):
+		doc_before_save = self.get_doc_before_save()
+		receivable_payable_types = ["Receivable", "Payable"]
+		if (
+			doc_before_save
+			and doc_before_save.account_type in receivable_payable_types
+			and doc_before_save.account_type != self.account_type
+		):
+			# check for ledger entries
+			if frappe.db.get_all("GL Entry", filters={"account": self.name, "is_cancelled": 0}, limit=1):
+				msg = _(
+					"There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
+				).format(
+					frappe.bold("Account Type"), doc_before_save.account_type, doc_before_save.account_type
+				)
+				frappe.msgprint(msg)
+				self.add_comment("Comment", msg)
+
 	def validate_root_details(self):
 		doc_before_save = self.get_doc_before_save()
 
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index eb3e00b..7d0869b 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -6,6 +6,7 @@
 
 import frappe
 from frappe.test_runner import make_test_records
+from frappe.utils import nowdate
 
 from erpnext.accounts.doctype.account.account import (
 	InvalidAccountMergeError,
@@ -324,6 +325,19 @@
 		acc.account_currency = "USD"
 		self.assertRaises(frappe.ValidationError, acc.save)
 
+	def test_account_balance(self):
+		from erpnext.accounts.utils import get_balance_on
+
+		if not frappe.db.exists("Account", "Test Percent Account %5 - _TC"):
+			acc = frappe.new_doc("Account")
+			acc.account_name = "Test Percent Account %5"
+			acc.parent_account = "Tax Assets - _TC"
+			acc.company = "_Test Company"
+			acc.insert()
+
+		balance = get_balance_on(account="Test Percent Account %5 - _TC", date=nowdate())
+		self.assertEqual(balance, 0)
+
 
 def _make_test_records(verbose=None):
 	from frappe.test_runner import make_test_objects
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_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
index 4b97619..8a505a8 100644
--- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
@@ -5,7 +5,9 @@
 import frappe
 from frappe import _, msgprint
 from frappe.model.document import Document
+from frappe.query_builder.custom import ConstantColumn
 from frappe.utils import flt, fmt_money, getdate
+from pypika import Order
 
 import erpnext
 
@@ -179,39 +181,62 @@
 
 	pos_sales_invoices, pos_purchase_invoices = [], []
 	if include_pos_transactions:
-		pos_sales_invoices = frappe.db.sql(
-			"""
-				select
-					"Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit,
-					si.posting_date, si.customer as against_account, sip.clearance_date,
-					account.account_currency, 0 as credit
-				from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account
-				where
-					sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
-					and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s
-				order by
-					si.posting_date ASC, si.name DESC
-			""",
-			{"account": account, "from": from_date, "to": to_date},
-			as_dict=1,
-		)
+		si_payment = frappe.qb.DocType("Sales Invoice Payment")
+		si = frappe.qb.DocType("Sales Invoice")
+		acc = frappe.qb.DocType("Account")
 
-		pos_purchase_invoices = frappe.db.sql(
-			"""
-				select
-					"Purchase Invoice" as payment_document, pi.name as payment_entry, pi.paid_amount as credit,
-					pi.posting_date, pi.supplier as against_account, pi.clearance_date,
-					account.account_currency, 0 as debit
-				from `tabPurchase Invoice` pi, `tabAccount` account
-				where
-					pi.cash_bank_account=%(account)s and pi.docstatus=1 and account.name = pi.cash_bank_account
-					and pi.posting_date >= %(from)s and pi.posting_date <= %(to)s
-				order by
-					pi.posting_date ASC, pi.name DESC
-			""",
-			{"account": account, "from": from_date, "to": to_date},
-			as_dict=1,
-		)
+		pos_sales_invoices = (
+			frappe.qb.from_(si_payment)
+			.inner_join(si)
+			.on(si_payment.parent == si.name)
+			.inner_join(acc)
+			.on(si_payment.account == acc.name)
+			.select(
+				ConstantColumn("Sales Invoice").as_("payment_document"),
+				si.name.as_("payment_entry"),
+				si_payment.reference_no.as_("cheque_number"),
+				si_payment.amount.as_("debit"),
+				si.posting_date,
+				si.customer.as_("against_account"),
+				si_payment.clearance_date,
+				acc.account_currency,
+				ConstantColumn(0).as_("credit"),
+			)
+			.where(
+				(si.docstatus == 1)
+				& (si_payment.account == account)
+				& (si.posting_date >= from_date)
+				& (si.posting_date <= to_date)
+			)
+			.orderby(si.posting_date)
+			.orderby(si.name, order=Order.desc)
+		).run(as_dict=True)
+
+		pi = frappe.qb.DocType("Purchase Invoice")
+
+		pos_purchase_invoices = (
+			frappe.qb.from_(pi)
+			.inner_join(acc)
+			.on(pi.cash_bank_account == acc.name)
+			.select(
+				ConstantColumn("Purchase Invoice").as_("payment_document"),
+				pi.name.as_("payment_entry"),
+				pi.paid_amount.as_("credit"),
+				pi.posting_date,
+				pi.supplier.as_("against_account"),
+				pi.clearance_date,
+				acc.account_currency,
+				ConstantColumn(0).as_("debit"),
+			)
+			.where(
+				(pi.docstatus == 1)
+				& (pi.cash_bank_account == account)
+				& (pi.posting_date >= from_date)
+				& (pi.posting_date <= to_date)
+			)
+			.orderby(pi.posting_date)
+			.orderby(pi.name, order=Order.desc)
+		).run(as_dict=True)
 
 	entries = (
 		list(payment_entries)
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
index 1a4747c..30e564c 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -80,7 +80,8 @@
 		from frappe.utils.background_jobs import is_job_enqueued
 		from frappe.utils.scheduler import is_scheduler_inactive
 
-		if is_scheduler_inactive() and not frappe.flags.in_test:
+		run_now = frappe.flags.in_test or frappe.conf.developer_mode
+		if is_scheduler_inactive() and not run_now:
 			frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
 
 		job_id = f"bank_statement_import::{self.name}"
@@ -97,7 +98,7 @@
 				google_sheets_url=self.google_sheets_url,
 				bank=self.bank,
 				template_options=self.template_options,
-				now=frappe.conf.developer_mode or frappe.flags.in_test,
+				now=run_now,
 			)
 			return True
 
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/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 777a5bb..def2838 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -13,16 +13,9 @@
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_checks_for_pl_and_bs_accounts,
 )
-from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
-	get_dimension_filter_map,
-)
 from erpnext.accounts.party import validate_party_frozen_disabled, validate_party_gle_currency
 from erpnext.accounts.utils import get_account_currency, get_fiscal_year
-from erpnext.exceptions import (
-	InvalidAccountCurrency,
-	InvalidAccountDimensionError,
-	MandatoryAccountDimensionError,
-)
+from erpnext.exceptions import InvalidAccountCurrency
 
 exclude_from_linked_with = True
 
@@ -98,7 +91,6 @@
 		if not self.flags.from_repost and self.voucher_type != "Period Closing Voucher":
 			self.validate_account_details(adv_adj)
 			self.validate_dimensions_for_pl_and_bs()
-			self.validate_allowed_dimensions()
 			validate_balance_type(self.account, adv_adj)
 			validate_frozen_account(self.account, adv_adj)
 
@@ -208,42 +200,6 @@
 						)
 					)
 
-	def validate_allowed_dimensions(self):
-		dimension_filter_map = get_dimension_filter_map()
-		for key, value in dimension_filter_map.items():
-			dimension = key[0]
-			account = key[1]
-
-			if self.account == account:
-				if value["is_mandatory"] and not self.get(dimension):
-					frappe.throw(
-						_("{0} is mandatory for account {1}").format(
-							frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)
-						),
-						MandatoryAccountDimensionError,
-					)
-
-				if value["allow_or_restrict"] == "Allow":
-					if self.get(dimension) and self.get(dimension) not in value["allowed_dimensions"]:
-						frappe.throw(
-							_("Invalid value {0} for {1} against account {2}").format(
-								frappe.bold(self.get(dimension)),
-								frappe.bold(frappe.unscrub(dimension)),
-								frappe.bold(self.account),
-							),
-							InvalidAccountDimensionError,
-						)
-				else:
-					if self.get(dimension) and self.get(dimension) in value["allowed_dimensions"]:
-						frappe.throw(
-							_("Invalid value {0} for {1} against account {2}").format(
-								frappe.bold(self.get(dimension)),
-								frappe.bold(frappe.unscrub(dimension)),
-								frappe.bold(self.account),
-							),
-							InvalidAccountDimensionError,
-						)
-
 	def check_pl_account(self):
 		if (
 			self.is_opening == "Yes"
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index c0dfaff..df67faf 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -1167,7 +1167,9 @@
 
 
 @frappe.whitelist()
-def get_default_bank_cash_account(company, account_type=None, mode_of_payment=None, account=None):
+def get_default_bank_cash_account(
+	company, account_type=None, mode_of_payment=None, account=None, ignore_permissions=False
+):
 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
 
 	if mode_of_payment:
@@ -1205,7 +1207,7 @@
 		return frappe._dict(
 			{
 				"account": account,
-				"balance": get_balance_on(account),
+				"balance": get_balance_on(account, ignore_account_permission=ignore_permissions),
 				"account_currency": account_details.account_currency,
 				"account_type": account_details.account_type,
 			}
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index f23d2c9..c55c820 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -2220,6 +2220,7 @@
 	party_type=None,
 	payment_type=None,
 	reference_date=None,
+	ignore_permissions=False,
 ):
 	doc = frappe.get_doc(dt, dn)
 	over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
@@ -2242,14 +2243,14 @@
 	)
 
 	# bank or cash
-	bank = get_bank_cash_account(doc, bank_account)
+	bank = get_bank_cash_account(doc, bank_account, ignore_permissions=ignore_permissions)
 
 	# if default bank or cash account is not set in company master and party has default company bank account, fetch it
 	if party_type in ["Customer", "Supplier"] and not bank:
 		party_bank_account = get_party_bank_account(party_type, doc.get(scrub(party_type)))
 		if party_bank_account:
 			account = frappe.db.get_value("Bank Account", party_bank_account, "account")
-			bank = get_bank_cash_account(doc, account)
+			bank = get_bank_cash_account(doc, account, ignore_permissions=ignore_permissions)
 
 	paid_amount, received_amount = set_paid_amount_and_received_amount(
 		dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc
@@ -2389,9 +2390,13 @@
 		pe.set(dimension, doc.get(dimension))
 
 
-def get_bank_cash_account(doc, bank_account):
+def get_bank_cash_account(doc, bank_account, ignore_permissions=False):
 	bank = get_default_bank_cash_account(
-		doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
+		doc.company,
+		"Bank",
+		mode_of_payment=doc.get("mode_of_payment"),
+		account=bank_account,
+		ignore_permissions=ignore_permissions,
 	)
 
 	if not bank:
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/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
index d7a73f0..fb75a0f 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
@@ -591,6 +591,70 @@
 		self.assertEqual(si.status, "Paid")
 		self.assertEqual(si.outstanding_amount, 0)
 
+	def test_invoice_status_after_cr_note_cancellation(self):
+		# This test case is made after the 'always standalone Credit/Debit notes' feature is introduced
+		transaction_date = nowdate()
+		amount = 100
+
+		si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
+
+		cr_note = self.create_sales_invoice(
+			qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
+		)
+		cr_note.is_return = 1
+		cr_note.return_against = si.name
+		cr_note = cr_note.save().submit()
+
+		pr = self.create_payment_reconciliation()
+
+		pr.get_unreconciled_entries()
+		invoices = [x.as_dict() for x in pr.get("invoices")]
+		payments = [x.as_dict() for x in pr.get("payments")]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.reconcile()
+
+		pr.get_unreconciled_entries()
+		self.assertEqual(pr.get("invoices"), [])
+		self.assertEqual(pr.get("payments"), [])
+
+		journals = frappe.db.get_all(
+			"Journal Entry",
+			filters={
+				"is_system_generated": 1,
+				"docstatus": 1,
+				"voucher_type": "Credit Note",
+				"reference_type": si.doctype,
+				"reference_name": si.name,
+			},
+			pluck="name",
+		)
+		self.assertEqual(len(journals), 1)
+
+		# assert status and outstanding
+		si.reload()
+		self.assertEqual(si.status, "Credit Note Issued")
+		self.assertEqual(si.outstanding_amount, 0)
+
+		cr_note.reload()
+		cr_note.cancel()
+		# 'Credit Note' Journal should be auto cancelled
+		journals = frappe.db.get_all(
+			"Journal Entry",
+			filters={
+				"is_system_generated": 1,
+				"docstatus": 1,
+				"voucher_type": "Credit Note",
+				"reference_type": si.doctype,
+				"reference_name": si.name,
+			},
+			pluck="name",
+		)
+		self.assertEqual(len(journals), 0)
+		# assert status and outstanding
+		si.reload()
+		self.assertEqual(si.status, "Unpaid")
+		self.assertEqual(si.outstanding_amount, 100)
+
 	def test_cr_note_partial_against_invoice(self):
 		transaction_date = nowdate()
 		amount = 100
diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
index 5a281aa..ad2889d 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
@@ -80,13 +80,16 @@
   "target_warehouse",
   "quality_inspection",
   "serial_and_batch_bundle",
-  "batch_no",
+  "use_serial_batch_fields",
   "col_break5",
   "allow_zero_valuation_rate",
-  "serial_no",
   "item_tax_rate",
   "actual_batch_qty",
   "actual_qty",
+  "section_break_tlhi",
+  "serial_no",
+  "column_break_ciit",
+  "batch_no",
   "edit_references",
   "sales_order",
   "so_detail",
@@ -628,13 +631,13 @@
    "options": "Quality Inspection"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "batch_no",
    "fieldtype": "Link",
    "hidden": 1,
    "label": "Batch No",
    "options": "Batch",
-   "print_hide": 1,
-   "read_only": 1
+   "print_hide": 1
   },
   {
    "fieldname": "col_break5",
@@ -649,14 +652,14 @@
    "print_hide": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
    "fieldtype": "Small Text",
    "hidden": 1,
    "in_list_view": 1,
    "label": "Serial No",
    "oldfieldname": "serial_no",
-   "oldfieldtype": "Small Text",
-   "read_only": 1
+   "oldfieldtype": "Small Text"
   },
   {
    "fieldname": "item_tax_rate",
@@ -824,17 +827,33 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial and Batch Bundle",
    "no_copy": 1,
    "options": "Serial and Batch Bundle",
    "print_hide": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
+   "fieldname": "section_break_tlhi",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_ciit",
+   "fieldtype": "Column Break"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2023-11-14 18:33:22.585715",
+ "modified": "2024-02-04 16:36:25.665743",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice Item",
diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
index e2a62f1..55a577b 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
@@ -82,6 +82,7 @@
 		target_warehouse: DF.Link | None
 		total_weight: DF.Float
 		uom: DF.Link
+		use_serial_batch_fields: DF.Check
 		warehouse: DF.Link | None
 		weight_per_unit: DF.Float
 		weight_uom: DF.Link | None
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index c03b18a..083c8fc 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -120,18 +120,6 @@
 	statement_dict = {}
 	ageing = ""
 
-	err_journals = None
-	if doc.report == "General Ledger" and doc.ignore_exchange_rate_revaluation_journals:
-		err_journals = frappe.db.get_all(
-			"Journal Entry",
-			filters={
-				"company": doc.company,
-				"docstatus": 1,
-				"voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]),
-			},
-			as_list=True,
-		)
-
 	for entry in doc.customers:
 		if doc.include_ageing:
 			ageing = set_ageing(doc, entry)
@@ -144,8 +132,8 @@
 		)
 
 		filters = get_common_filters(doc)
-		if err_journals:
-			filters.update({"voucher_no_not_in": [x[0] for x in err_journals]})
+		if doc.ignore_exchange_rate_revaluation_journals:
+			filters.update({"ignore_err": True})
 
 		if doc.report == "General Ledger":
 			filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency))
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 01821fc..3312f2e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -696,6 +696,7 @@
 		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating ordered qty in bin depends upon updated ordered qty in PO
 		if self.update_stock == 1:
+			self.make_bundle_using_old_serial_batch_fields()
 			self.update_stock_ledger()
 
 			if self.is_old_subcontracting_flow:
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 26984d9..3ee4214 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -62,16 +62,19 @@
   "rm_supp_cost",
   "warehouse_section",
   "warehouse",
-  "from_warehouse",
-  "quality_inspection",
   "add_serial_batch_bundle",
   "serial_and_batch_bundle",
-  "serial_no",
+  "use_serial_batch_fields",
   "col_br_wh",
+  "from_warehouse",
+  "quality_inspection",
   "rejected_warehouse",
   "rejected_serial_and_batch_bundle",
-  "batch_no",
+  "section_break_rqbe",
+  "serial_no",
   "rejected_serial_no",
+  "column_break_vbbb",
+  "batch_no",
   "manufacture_details",
   "manufacturer",
   "column_break_13",
@@ -440,13 +443,11 @@
    "print_hide": 1
   },
   {
-   "depends_on": "eval:!doc.is_fixed_asset",
+   "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
    "fieldname": "batch_no",
    "fieldtype": "Link",
-   "hidden": 1,
    "label": "Batch No",
    "options": "Batch",
-   "read_only": 1,
    "search_index": 1
   },
   {
@@ -454,21 +455,18 @@
    "fieldtype": "Column Break"
   },
   {
-   "depends_on": "eval:!doc.is_fixed_asset",
+   "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
    "fieldname": "serial_no",
    "fieldtype": "Text",
-   "hidden": 1,
-   "label": "Serial No",
-   "read_only": 1
+   "label": "Serial No"
   },
   {
-   "depends_on": "eval:!doc.is_fixed_asset",
+   "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
    "fieldname": "rejected_serial_no",
    "fieldtype": "Text",
    "label": "Rejected Serial No",
    "no_copy": 1,
-   "print_hide": 1,
-   "read_only": 1
+   "print_hide": 1
   },
   {
    "fieldname": "accounting",
@@ -891,7 +889,7 @@
    "label": "Apply TDS"
   },
   {
-   "depends_on": "eval:parent.update_stock == 1",
+   "depends_on": "eval:parent.update_stock == 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial and Batch Bundle",
@@ -901,7 +899,7 @@
    "search_index": 1
   },
   {
-   "depends_on": "eval:parent.update_stock == 1",
+   "depends_on": "eval:parent.update_stock == 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)",
    "fieldname": "rejected_serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Rejected Serial and Batch Bundle",
@@ -916,16 +914,31 @@
    "options": "Asset"
   },
   {
-   "depends_on": "eval:parent.update_stock === 1",
+   "depends_on": "eval:parent.update_stock === 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)",
    "fieldname": "add_serial_batch_bundle",
    "fieldtype": "Button",
    "label": "Add Serial / Batch No"
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
+   "fieldname": "section_break_rqbe",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_vbbb",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2024-01-21 19:46:25.537861",
+ "modified": "2024-02-04 14:11:52.742228",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py
index e48d223..ccbc347 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py
@@ -88,6 +88,7 @@
 		stock_uom_rate: DF.Currency
 		total_weight: DF.Float
 		uom: DF.Link
+		use_serial_batch_fields: DF.Check
 		valuation_rate: DF.Currency
 		warehouse: DF.Link | None
 		weight_per_unit: DF.Float
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 7ce6fd0..76ec4a4 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -447,6 +447,7 @@
 		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating reserved qty in bin depends upon updated delivered qty in SO
 		if self.update_stock == 1:
+			self.make_bundle_using_old_serial_batch_fields()
 			self.update_stock_ledger()
 
 		# this sequence because outstanding may get -ve
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index ec9e792..d06c786 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -83,14 +83,17 @@
   "quality_inspection",
   "pick_serial_and_batch",
   "serial_and_batch_bundle",
-  "batch_no",
-  "incoming_rate",
+  "use_serial_batch_fields",
   "col_break5",
   "allow_zero_valuation_rate",
-  "serial_no",
+  "incoming_rate",
   "item_tax_rate",
   "actual_batch_qty",
   "actual_qty",
+  "section_break_eoec",
+  "serial_no",
+  "column_break_ytgd",
+  "batch_no",
   "edit_references",
   "sales_order",
   "so_detail",
@@ -600,12 +603,11 @@
    "options": "Quality Inspection"
   },
   {
+   "depends_on": "eval: doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
    "fieldname": "batch_no",
    "fieldtype": "Link",
-   "hidden": 1,
    "label": "Batch No",
    "options": "Batch",
-   "read_only": 1,
    "search_index": 1
   },
   {
@@ -621,13 +623,12 @@
    "print_hide": 1
   },
   {
+   "depends_on": "eval: doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
    "fieldname": "serial_no",
    "fieldtype": "Small Text",
-   "hidden": 1,
    "label": "Serial No",
    "oldfieldname": "serial_no",
-   "oldfieldtype": "Small Text",
-   "read_only": 1
+   "oldfieldtype": "Small Text"
   },
   {
    "fieldname": "item_group",
@@ -891,6 +892,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:parent.update_stock == 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial and Batch Bundle",
@@ -904,12 +906,27 @@
    "fieldname": "pick_serial_and_batch",
    "fieldtype": "Button",
    "label": "Pick Serial / Batch No"
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
+   "fieldname": "section_break_eoec",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_ytgd",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-12-29 13:03:14.121298",
+ "modified": "2024-02-04 11:52:16.106541",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
index 80f6774..c71d08e 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
@@ -86,6 +86,7 @@
 		target_warehouse: DF.Link | None
 		total_weight: DF.Float
 		uom: DF.Link
+		use_serial_batch_fields: DF.Check
 		warehouse: DF.Link | None
 		weight_per_unit: DF.Float
 		weight_uom: DF.Link | None
diff --git a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json
index 5ab46b7..bd59f65 100644
--- a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json
+++ b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json
@@ -8,6 +8,7 @@
   "default",
   "mode_of_payment",
   "amount",
+  "reference_no",
   "column_break_3",
   "account",
   "type",
@@ -75,11 +76,16 @@
    "hidden": 1,
    "label": "Default",
    "read_only": 1
+  },
+  {
+   "fieldname": "reference_no",
+   "fieldtype": "Data",
+   "label": "Reference No"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-08-03 12:45:39.986598",
+ "modified": "2024-01-23 16:20:06.436979",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Payment",
@@ -87,5 +93,6 @@
  "permissions": [],
  "quick_entry": 1,
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.py b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.py
index 57d0142..e460a01 100644
--- a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.py
+++ b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.py
@@ -23,6 +23,7 @@
 		parent: DF.Data
 		parentfield: DF.Data
 		parenttype: DF.Data
+		reference_no: DF.Data | None
 		type: DF.ReadOnly | None
 	# end: auto-generated types
 
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 1c8ac2f..2e82886 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -13,9 +13,13 @@
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_accounting_dimensions,
 )
+from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
+	get_dimension_filter_map,
+)
 from erpnext.accounts.doctype.accounting_period.accounting_period import ClosedAccountingPeriod
 from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
 from erpnext.accounts.utils import create_payment_ledger_entry
+from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
 
 
 def make_gl_entries(
@@ -355,6 +359,7 @@
 
 	process_debit_credit_difference(gl_map)
 
+	dimension_filter_map = get_dimension_filter_map()
 	if gl_map:
 		check_freezing_date(gl_map[0]["posting_date"], adv_adj)
 		is_opening = any(d.get("is_opening") == "Yes" for d in gl_map)
@@ -362,6 +367,7 @@
 			validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"])
 
 	for entry in gl_map:
+		validate_allowed_dimensions(entry, dimension_filter_map)
 		make_entry(entry, adv_adj, update_outstanding, from_repost)
 
 
@@ -700,3 +706,39 @@
 		where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
 		(now(), frappe.session.user, voucher_type, voucher_no),
 	)
+
+
+def validate_allowed_dimensions(gl_entry, dimension_filter_map):
+	for key, value in dimension_filter_map.items():
+		dimension = key[0]
+		account = key[1]
+
+		if gl_entry.account == account:
+			if value["is_mandatory"] and not gl_entry.get(dimension):
+				frappe.throw(
+					_("{0} is mandatory for account {1}").format(
+						frappe.bold(frappe.unscrub(dimension)), frappe.bold(gl_entry.account)
+					),
+					MandatoryAccountDimensionError,
+				)
+
+			if value["allow_or_restrict"] == "Allow":
+				if gl_entry.get(dimension) and gl_entry.get(dimension) not in value["allowed_dimensions"]:
+					frappe.throw(
+						_("Invalid value {0} for {1} against account {2}").format(
+							frappe.bold(gl_entry.get(dimension)),
+							frappe.bold(frappe.unscrub(dimension)),
+							frappe.bold(gl_entry.account),
+						),
+						InvalidAccountDimensionError,
+					)
+			else:
+				if gl_entry.get(dimension) and gl_entry.get(dimension) in value["allowed_dimensions"]:
+					frappe.throw(
+						_("Invalid value {0} for {1} against account {2}").format(
+							frappe.bold(gl_entry.get(dimension)),
+							frappe.bold(frappe.unscrub(dimension)),
+							frappe.bold(gl_entry.account),
+						),
+						InvalidAccountDimensionError,
+					)
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 79b5e4d..b7b9d34 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -203,8 +203,14 @@
 			"fieldname": "show_remarks",
 			"label": __("Show Remarks"),
 			"fieldtype": "Check"
+		},
+		{
+			"fieldname": "ignore_err",
+			"label": __("Ignore Exchange Rate Revaluation Journals"),
+			"fieldtype": "Check"
 		}
 
+
 	]
 }
 
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 110ec75..cea3a7b 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -241,6 +241,19 @@
 	if filters.get("against_voucher_no"):
 		conditions.append("against_voucher=%(against_voucher_no)s")
 
+	if filters.get("ignore_err"):
+		err_journals = frappe.db.get_all(
+			"Journal Entry",
+			filters={
+				"company": filters.get("company"),
+				"docstatus": 1,
+				"voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]),
+			},
+			as_list=True,
+		)
+		if err_journals:
+			filters.update({"voucher_no_not_in": [x[0] for x in err_journals]})
+
 	if filters.get("voucher_no_not_in"):
 		conditions.append("voucher_no not in %(voucher_no_not_in)s")
 
diff --git a/erpnext/accounts/report/general_ledger/test_general_ledger.py b/erpnext/accounts/report/general_ledger/test_general_ledger.py
index a8c362e..75f9430 100644
--- a/erpnext/accounts/report/general_ledger/test_general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/test_general_ledger.py
@@ -3,7 +3,7 @@
 
 import frappe
 from frappe.tests.utils import FrappeTestCase
-from frappe.utils import today
+from frappe.utils import flt, today
 
 from erpnext.accounts.report.general_ledger.general_ledger import execute
 
@@ -148,3 +148,105 @@
 		self.assertEqual(data[2]["credit"], 900)
 		self.assertEqual(data[3]["debit"], 100)
 		self.assertEqual(data[3]["credit"], 100)
+
+	def test_ignore_exchange_rate_journals_filter(self):
+		# create a new account with USD currency
+		account_name = "Test Debtors USD"
+		company = "_Test Company"
+		account = frappe.get_doc(
+			{
+				"account_name": account_name,
+				"is_group": 0,
+				"company": company,
+				"root_type": "Asset",
+				"report_type": "Balance Sheet",
+				"account_currency": "USD",
+				"parent_account": "Accounts Receivable - _TC",
+				"account_type": "Receivable",
+				"doctype": "Account",
+			}
+		)
+		account.insert(ignore_if_duplicate=True)
+		# create a JV to debit 1000 USD at 75 exchange rate
+		jv = frappe.new_doc("Journal Entry")
+		jv.posting_date = today()
+		jv.company = company
+		jv.multi_currency = 1
+		jv.cost_center = "_Test Cost Center - _TC"
+		jv.set(
+			"accounts",
+			[
+				{
+					"account": account.name,
+					"party_type": "Customer",
+					"party": "_Test Customer USD",
+					"debit_in_account_currency": 1000,
+					"credit_in_account_currency": 0,
+					"exchange_rate": 75,
+					"cost_center": "_Test Cost Center - _TC",
+				},
+				{
+					"account": "Cash - _TC",
+					"debit_in_account_currency": 0,
+					"credit_in_account_currency": 75000,
+					"cost_center": "_Test Cost Center - _TC",
+				},
+			],
+		)
+		jv.save()
+		jv.submit()
+
+		revaluation = frappe.new_doc("Exchange Rate Revaluation")
+		revaluation.posting_date = today()
+		revaluation.company = company
+		accounts = revaluation.get_accounts_data()
+		revaluation.extend("accounts", accounts)
+		row = revaluation.accounts[0]
+		row.new_exchange_rate = 83
+		row.new_balance_in_base_currency = flt(
+			row.new_exchange_rate * flt(row.balance_in_account_currency)
+		)
+		row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
+		revaluation.set_total_gain_loss()
+		revaluation = revaluation.save().submit()
+
+		# post journal entry for Revaluation doc
+		frappe.db.set_value(
+			"Company", company, "unrealized_exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC"
+		)
+		revaluation_jv = revaluation.make_jv_for_revaluation()
+		revaluation_jv.cost_center = "_Test Cost Center - _TC"
+		for acc in revaluation_jv.get("accounts"):
+			acc.cost_center = "_Test Cost Center - _TC"
+		revaluation_jv.save()
+		revaluation_jv.submit()
+
+		# With ignore_err enabled
+		columns, data = execute(
+			frappe._dict(
+				{
+					"company": company,
+					"from_date": today(),
+					"to_date": today(),
+					"account": [account.name],
+					"group_by": "Group by Voucher (Consolidated)",
+					"ignore_err": True,
+				}
+			)
+		)
+		self.assertNotIn(revaluation_jv.name, set([x.voucher_no for x in data]))
+
+		# Without ignore_err enabled
+		columns, data = execute(
+			frappe._dict(
+				{
+					"company": company,
+					"from_date": today(),
+					"to_date": today(),
+					"account": [account.name],
+					"group_by": "Group by Voucher (Consolidated)",
+					"ignore_err": False,
+				}
+			)
+		)
+		self.assertIn(revaluation_jv.name, set([x.voucher_no for x in data]))
diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
index 4a80dd0..0e3acd7 100644
--- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
+++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
@@ -63,16 +63,14 @@
 				tax_amount += entry.credit - entry.debit
 				# infer tax withholding category from the account if it's the single account for this category
 				tax_withholding_category = tds_accounts.get(entry.account)
-				rate = tax_rate_map.get(tax_withholding_category)
 				# or else the consolidated value from the voucher document
 				if not tax_withholding_category:
-					# or else from the party default
 					tax_withholding_category = tax_category_map.get(name)
-					rate = tax_rate_map.get(tax_withholding_category)
+				# or else from the party default
 				if not tax_withholding_category:
 					tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
-					rate = tax_rate_map.get(tax_withholding_category)
 
+				rate = tax_rate_map.get(tax_withholding_category)
 			if net_total_map.get(name):
 				if voucher_type == "Journal Entry" and tax_amount and rate:
 					# back calcalute total amount from rate and tax_amount
@@ -295,7 +293,7 @@
 	tds_accounts = {}
 	for tds_acc in _tds_accounts:
 		# if it turns out not to be the only tax withholding category, then don't include in the map
-		if tds_accounts.get(tds_acc["account"]):
+		if tds_acc["account"] in tds_accounts:
 			tds_accounts[tds_acc["account"]] = None
 		else:
 			tds_accounts[tds_acc["account"]] = tds_acc["parent"]
@@ -354,9 +352,6 @@
 	if filters.get("to_date"):
 		query = query.where(gle.posting_date <= filters.get("to_date"))
 
-	if bank_accounts:
-		query = query.where(gle.against.notin(bank_accounts))
-
 	if filters.get("party"):
 		party = [filters.get("party")]
 		jv_condition = gle.against.isin(party) | (
@@ -368,7 +363,14 @@
 			(gle.voucher_type == "Journal Entry")
 			& ((gle.party_type == filters.get("party_type")) | (gle.party_type == ""))
 		)
-	query = query.where((gle.account.isin(tds_accounts) & jv_condition) | gle.party.isin(party))
+
+	query.where((gle.account.isin(tds_accounts) & jv_condition) | gle.party.isin(party))
+	if bank_accounts:
+		query = query.where(
+			gle.against.notin(bank_accounts) & (gle.account.isin(tds_accounts) & jv_condition)
+			| gle.party.isin(party)
+		)
+
 	return query
 
 
@@ -408,7 +410,7 @@
 			"paid_amount_after_tax",
 			"base_paid_amount",
 		],
-		"Journal Entry": ["tax_withholding_category", "total_amount"],
+		"Journal Entry": ["total_amount"],
 	}
 
 	entries = frappe.get_all(
diff --git a/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py
index b3f6737..7515616 100644
--- a/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py
+++ b/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py
@@ -5,7 +5,6 @@
 from frappe.tests.utils import FrappeTestCase
 from frappe.utils import today
 
-from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
 from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
@@ -17,36 +16,63 @@
 from erpnext.accounts.utils import get_fiscal_year
 
 
-class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase):
+class TestTaxWithholdingDetails(AccountsTestMixin, FrappeTestCase):
 	def setUp(self):
 		self.create_company()
 		self.clear_old_entries()
 		create_tax_accounts()
-		create_tcs_category()
 
 	def test_tax_withholding_for_customers(self):
+		create_tax_category(cumulative_threshold=300)
+		frappe.db.set_value("Customer", "_Test Customer", "tax_withholding_category", "TCS")
 		si = create_sales_invoice(rate=1000)
 		pe = create_tcs_payment_entry()
+		jv = create_tcs_journal_entry()
+
 		filters = frappe._dict(
 			company="_Test Company", party_type="Customer", from_date=today(), to_date=today()
 		)
 		result = execute(filters)[1]
 		expected_values = [
+			# Check for JV totals using back calculation logic
+			[jv.name, "TCS", 0.075, -10000.0, -7.5, -10000.0],
 			[pe.name, "TCS", 0.075, 2550, 0.53, 2550.53],
 			[si.name, "TCS", 0.075, 1000, 0.52, 1000.52],
 		]
 		self.check_expected_values(result, expected_values)
 
+	def test_single_account_for_multiple_categories(self):
+		create_tax_category("TDS - 1", rate=10, account="TDS - _TC")
+		inv_1 = make_purchase_invoice(rate=1000, do_not_submit=True)
+		inv_1.tax_withholding_category = "TDS - 1"
+		inv_1.submit()
+
+		create_tax_category("TDS - 2", rate=20, account="TDS - _TC")
+		inv_2 = make_purchase_invoice(rate=1000, do_not_submit=True)
+		inv_2.tax_withholding_category = "TDS - 2"
+		inv_2.submit()
+		result = execute(
+			frappe._dict(company="_Test Company", party_type="Supplier", from_date=today(), to_date=today())
+		)[1]
+		expected_values = [
+			[inv_1.name, "TDS - 1", 10, 5000, 500, 5500],
+			[inv_2.name, "TDS - 2", 20, 5000, 1000, 6000],
+		]
+		self.check_expected_values(result, expected_values)
+
 	def check_expected_values(self, result, expected_values):
 		for i in range(len(result)):
 			voucher = frappe._dict(result[i])
 			voucher_expected_values = expected_values[i]
-			self.assertEqual(voucher.ref_no, voucher_expected_values[0])
-			self.assertEqual(voucher.section_code, voucher_expected_values[1])
-			self.assertEqual(voucher.rate, voucher_expected_values[2])
-			self.assertEqual(voucher.base_total, voucher_expected_values[3])
-			self.assertAlmostEqual(voucher.tax_amount, voucher_expected_values[4])
-			self.assertAlmostEqual(voucher.grand_total, voucher_expected_values[5])
+			voucher_actual_values = (
+				voucher.ref_no,
+				voucher.section_code,
+				voucher.rate,
+				voucher.base_total,
+				voucher.tax_amount,
+				voucher.grand_total,
+			)
+			self.assertSequenceEqual(voucher_actual_values, voucher_expected_values)
 
 	def tearDown(self):
 		self.clear_old_entries()
@@ -67,24 +93,20 @@
 		).insert(ignore_if_duplicate=True)
 
 
-def create_tcs_category():
+def create_tax_category(category="TCS", rate=0.075, account="TCS - _TC", cumulative_threshold=0):
 	fiscal_year = get_fiscal_year(today(), company="_Test Company")
 	from_date = fiscal_year[1]
 	to_date = fiscal_year[2]
 
-	tax_category = create_tax_withholding_category(
-		category_name="TCS",
-		rate=0.075,
+	create_tax_withholding_category(
+		category_name=category,
+		rate=rate,
 		from_date=from_date,
 		to_date=to_date,
-		account="TCS - _TC",
-		cumulative_threshold=300,
+		account=account,
+		cumulative_threshold=cumulative_threshold,
 	)
 
-	customer = frappe.get_doc("Customer", "_Test Customer")
-	customer.tax_withholding_category = "TCS"
-	customer.save()
-
 
 def create_tcs_payment_entry():
 	payment_entry = create_payment_entry(
@@ -109,3 +131,32 @@
 	)
 	payment_entry.submit()
 	return payment_entry
+
+
+def create_tcs_journal_entry():
+	jv = frappe.new_doc("Journal Entry")
+	jv.posting_date = today()
+	jv.company = "_Test Company"
+	jv.set(
+		"accounts",
+		[
+			{
+				"account": "Debtors - _TC",
+				"party_type": "Customer",
+				"party": "_Test Customer",
+				"credit_in_account_currency": 10000,
+			},
+			{
+				"account": "Debtors - _TC",
+				"party_type": "Customer",
+				"party": "_Test Customer",
+				"debit_in_account_currency": 9992.5,
+			},
+			{
+				"account": "TCS - _TC",
+				"debit_in_account_currency": 7.5,
+			},
+		],
+	)
+	jv.insert()
+	return jv.submit()
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js
index 2c4c762..5374ac1 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.js
+++ b/erpnext/accounts/report/trial_balance/trial_balance.js
@@ -78,8 +78,14 @@
 			"options": erpnext.get_presentation_currency_list()
 		},
 		{
-			"fieldname": "with_period_closing_entry",
-			"label": __("Period Closing Entry"),
+			"fieldname": "with_period_closing_entry_for_opening",
+			"label": __("With Period Closing Entry For Opening Balances"),
+			"fieldtype": "Check",
+			"default": 1
+		},
+		{
+			"fieldname": "with_period_closing_entry_for_current_period",
+			"label": __("Period Closing Entry For Current Period"),
 			"fieldtype": "Check",
 			"default": 1
 		},
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 8b7f0bb..2ff0eff 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -116,7 +116,7 @@
 		max_rgt,
 		filters,
 		gl_entries_by_account,
-		ignore_closing_entries=not flt(filters.with_period_closing_entry),
+		ignore_closing_entries=not flt(filters.with_period_closing_entry_for_current_period),
 		ignore_opening_entries=True,
 	)
 
@@ -249,7 +249,7 @@
 	):
 		opening_balance = opening_balance.where(closing_balance.posting_date >= filters.year_start_date)
 
-	if not flt(filters.with_period_closing_entry):
+	if not flt(filters.with_period_closing_entry_for_opening):
 		if doctype == "Account Closing Balance":
 			opening_balance = opening_balance.where(closing_balance.is_period_closing_voucher_entry == 0)
 		else:
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 30700d0..64bc39a 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -237,7 +237,7 @@
 			)
 
 		else:
-			cond.append("""gle.cost_center = %s """ % (frappe.db.escape(cost_center, percent=False),))
+			cond.append("""gle.cost_center = %s """ % (frappe.db.escape(cost_center),))
 
 	if account:
 		if not (frappe.flags.ignore_account_permission or ignore_account_permission):
@@ -258,7 +258,7 @@
 			if acc.account_currency == frappe.get_cached_value("Company", acc.company, "default_currency"):
 				in_account_currency = False
 		else:
-			cond.append("""gle.account = %s """ % (frappe.db.escape(account, percent=False),))
+			cond.append("""gle.account = %s """ % (frappe.db.escape(account),))
 
 	if account_type:
 		accounts = frappe.db.get_all(
@@ -278,11 +278,11 @@
 	if party_type and party:
 		cond.append(
 			"""gle.party_type = %s and gle.party = %s """
-			% (frappe.db.escape(party_type), frappe.db.escape(party, percent=False))
+			% (frappe.db.escape(party_type), frappe.db.escape(party))
 		)
 
 	if company:
-		cond.append("""gle.company = %s """ % (frappe.db.escape(company, percent=False)))
+		cond.append("""gle.company = %s """ % (frappe.db.escape(company)))
 
 	if account or (party_type and party) or account_type:
 		precision = get_currency_precision()
@@ -348,7 +348,7 @@
 				% (acc.lft, acc.rgt)
 			)
 		else:
-			cond.append("""gle.account = %s """ % (frappe.db.escape(account, percent=False),))
+			cond.append("""gle.account = %s """ % (frappe.db.escape(account),))
 
 		entries = frappe.db.sql(
 			"""
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index 5e251a5..c9ed806 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -126,6 +126,7 @@
 		self.create_target_asset()
 
 	def on_submit(self):
+		self.make_bundle_using_old_serial_batch_fields()
 		self.update_stock_ledger()
 		self.make_gl_entries()
 		self.update_target_asset()
diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
index 26e1c3c..8eda441 100644
--- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
+++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
@@ -18,9 +18,12 @@
   "amount",
   "batch_and_serial_no_section",
   "serial_and_batch_bundle",
+  "use_serial_batch_fields",
   "column_break_13",
-  "batch_no",
+  "section_break_bfqc",
   "serial_no",
+  "column_break_mbuv",
+  "batch_no",
   "accounting_dimensions_section",
   "cost_center",
   "dimension_col_break"
@@ -39,13 +42,13 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "batch_no",
    "fieldtype": "Link",
    "label": "Batch No",
    "no_copy": 1,
    "options": "Batch",
-   "print_hide": 1,
-   "read_only": 1
+   "print_hide": 1
   },
   {
    "fieldname": "section_break_6",
@@ -102,12 +105,12 @@
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
    "fieldtype": "Small Text",
    "hidden": 1,
    "label": "Serial No",
-   "print_hide": 1,
-   "read_only": 1
+   "print_hide": 1
   },
   {
    "fieldname": "item_code",
@@ -148,18 +151,34 @@
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial and Batch Bundle",
    "no_copy": 1,
    "options": "Serial and Batch Bundle",
    "print_hide": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
+   "fieldname": "section_break_bfqc",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_mbuv",
+   "fieldtype": "Column Break"
   }
  ],
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-04-06 01:10:17.947952",
+ "modified": "2024-02-04 16:41:09.239762",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Capitalization Stock Item",
diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py
index 122cbb6..d2b075c 100644
--- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py
+++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py
@@ -27,6 +27,7 @@
 		serial_no: DF.SmallText | None
 		stock_qty: DF.Float
 		stock_uom: DF.Link
+		use_serial_batch_fields: DF.Check
 		valuation_rate: DF.Currency
 		warehouse: DF.Link
 	# end: auto-generated types
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 4efbb27..4d94868 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -457,6 +457,7 @@
 		self.update_ordered_qty()
 		self.update_reserved_qty_for_subcontract()
 		self.update_subcontracting_order_status()
+		self.update_blanket_order()
 		self.notify_update()
 		clear_doctype_notifications(self)
 
@@ -644,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/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 5405799..a30de68 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -822,6 +822,30 @@
 		# To test if the PO does NOT have a Blanket Order
 		self.assertEqual(po_doc.items[0].blanket_order, None)
 
+	def test_blanket_order_on_po_close_and_open(self):
+		# Step - 1: Create Blanket Order
+		bo = make_blanket_order(blanket_order_type="Purchasing", quantity=10, rate=10)
+
+		# Step - 2: Create Purchase Order
+		po = create_purchase_order(
+			item_code="_Test Item", qty=5, against_blanket_order=1, against_blanket=bo.name
+		)
+
+		bo.load_from_db()
+		self.assertEqual(bo.items[0].ordered_qty, 5)
+
+		# Step - 3: Close Purchase Order
+		po.update_status("Closed")
+
+		bo.load_from_db()
+		self.assertEqual(bo.items[0].ordered_qty, 0)
+
+		# Step - 4: Re-Open Purchase Order
+		po.update_status("Re-open")
+
+		bo.load_from_db()
+		self.assertEqual(bo.items[0].ordered_qty, 5)
+
 	def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
 			create_payment_terms_template,
@@ -1148,6 +1172,7 @@
 				"schedule_date": add_days(nowdate(), 1),
 				"include_exploded_items": args.get("include_exploded_items", 1),
 				"against_blanket_order": args.against_blanket_order,
+				"against_blanket": args.against_blanket,
 				"material_request": args.material_request,
 				"material_request_item": args.material_request_item,
 			},
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 5a24cc2..e3e8def 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -545,7 +545,6 @@
    "fieldname": "blanket_order",
    "fieldtype": "Link",
    "label": "Blanket Order",
-   "no_copy": 1,
    "options": "Blanket Order"
   },
   {
@@ -553,7 +552,6 @@
    "fieldname": "blanket_order_rate",
    "fieldtype": "Currency",
    "label": "Blanket Order Rate",
-   "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
   },
@@ -917,7 +915,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-24 13:24:41.298416",
+ "modified": "2024-02-05 11:23:24.859435",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 678b293..f11db1a 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -693,7 +693,7 @@
 					if self.get("is_subcontracted"):
 						args["is_subcontracted"] = self.is_subcontracted
 
-					ret = get_item_details(args, self, for_validate=True, overwrite_warehouse=False)
+					ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False)
 
 					for fieldname, value in ret.items():
 						if item.meta.get_field(fieldname) and value is not None:
@@ -1476,6 +1476,24 @@
 						x.update({dim.fieldname: self.get(dim.fieldname)})
 			reconcile_against_document(lst, active_dimensions=active_dimensions)
 
+	def cancel_system_generated_credit_debit_notes(self):
+		# Cancel 'Credit/Debit' Note Journal Entries, if found.
+		if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
+			voucher_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note"
+			journals = frappe.db.get_all(
+				"Journal Entry",
+				filters={
+					"is_system_generated": 1,
+					"reference_type": self.doctype,
+					"reference_name": self.name,
+					"voucher_type": voucher_type,
+					"docstatus": 1,
+				},
+				pluck="name",
+			)
+			for x in journals:
+				frappe.get_doc("Journal Entry", x).cancel()
+
 	def on_cancel(self):
 		from erpnext.accounts.doctype.bank_transaction.bank_transaction import (
 			remove_from_bank_transaction,
@@ -1488,6 +1506,8 @@
 		remove_from_bank_transaction(self.doctype, self.name)
 
 		if self.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]:
+			self.cancel_system_generated_credit_debit_notes()
+
 			# Cancel Exchange Gain/Loss Journal before unlinking
 			cancel_exchange_gain_loss_journal(self)
 
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index e234eec..c46ef50 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -729,17 +729,24 @@
 	conditions, bin_conditions = [], []
 	filter_dict = get_doctype_wise_filters(filters)
 
-	query = """select `tabWarehouse`.name,
+	warehouse_field = "name"
+	meta = frappe.get_meta("Warehouse")
+	if meta.get("show_title_field_in_link") and meta.get("title_field"):
+		searchfield = meta.get("title_field")
+		warehouse_field = meta.get("title_field")
+
+	query = """select `tabWarehouse`.`{warehouse_field}`,
 		CONCAT_WS(' : ', 'Actual Qty', ifnull(round(`tabBin`.actual_qty, 2), 0 )) actual_qty
 		from `tabWarehouse` left join `tabBin`
 		on `tabBin`.warehouse = `tabWarehouse`.name {bin_conditions}
 		where
 			`tabWarehouse`.`{key}` like {txt}
 			{fcond} {mcond}
-		order by ifnull(`tabBin`.actual_qty, 0) desc
+		order by ifnull(`tabBin`.actual_qty, 0) desc, `tabWarehouse`.`{warehouse_field}` asc
 		limit
 			{page_len} offset {start}
 		""".format(
+		warehouse_field=warehouse_field,
 		bin_conditions=get_filters_cond(
 			doctype, filter_dict.get("Bin"), bin_conditions, ignore_permissions=True
 		),
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 8c43842..dc49023 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -599,7 +599,7 @@
 		if self.doctype in ["Sales Order", "Quotation"]:
 			for item in self.items:
 				item.gross_profit = flt(
-					((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item)
+					((item.base_rate - flt(item.valuation_rate)) * item.stock_qty), self.precision("amount", item)
 				)
 
 	def set_customer_address(self):
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 11e9f9f..74c835c 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -21,6 +21,9 @@
 from erpnext.stock.doctype.inventory_dimension.inventory_dimension import (
 	get_evaluated_inventory_dimension,
 )
+from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
+	get_type_of_transaction,
+)
 from erpnext.stock.stock_ledger import get_items_to_be_repost
 
 
@@ -126,6 +129,81 @@
 				# 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):
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+		from erpnext.stock.serial_batch_bundle import SerialBatchCreation
+
+		# To handle test cases
+		if frappe.flags.in_test and frappe.flags.use_serial_and_batch_fields:
+			return
+
+		table_name = "items"
+		if self.doctype == "Asset Capitalization":
+			table_name = "stock_items"
+
+		for row in self.get(table_name):
+			if not row.serial_no and not row.batch_no and not row.get("rejected_serial_no"):
+				continue
+
+			if not row.use_serial_batch_fields and (
+				row.serial_no or row.batch_no or row.get("rejected_serial_no")
+			):
+				frappe.throw(_("Please enable Use Old Serial / Batch Fields to make_bundle"))
+
+			if row.use_serial_batch_fields and (
+				not row.serial_and_batch_bundle and not row.get("rejected_serial_and_batch_bundle")
+			):
+				if self.doctype == "Stock Reconciliation":
+					qty = row.qty
+					type_of_transaction = "Inward"
+				else:
+					qty = row.stock_qty
+					type_of_transaction = get_type_of_transaction(self, row)
+
+				sn_doc = SerialBatchCreation(
+					{
+						"item_code": row.item_code,
+						"warehouse": row.warehouse,
+						"posting_date": self.posting_date,
+						"posting_time": self.posting_time,
+						"voucher_type": self.doctype,
+						"voucher_no": self.name,
+						"voucher_detail_no": row.name,
+						"qty": qty,
+						"type_of_transaction": type_of_transaction,
+						"company": self.company,
+						"is_rejected": 1 if row.get("rejected_warehouse") else 0,
+						"serial_nos": get_serial_nos(row.serial_no) if row.serial_no else None,
+						"batches": frappe._dict({row.batch_no: qty}) if row.batch_no else None,
+						"batch_no": row.batch_no,
+						"use_serial_batch_fields": row.use_serial_batch_fields,
+						"do_not_submit": True,
+					}
+				).make_serial_and_batch_bundle()
+
+				if sn_doc.is_rejected:
+					row.rejected_serial_and_batch_bundle = sn_doc.name
+					row.db_set(
+						{
+							"rejected_serial_and_batch_bundle": sn_doc.name,
+							"rejected_serial_no": "",
+						}
+					)
+				else:
+					row.serial_and_batch_bundle = sn_doc.name
+					row.db_set(
+						{
+							"serial_and_batch_bundle": sn_doc.name,
+							"serial_no": "",
+							"batch_no": "",
+						}
+					)
+
+	def set_use_serial_batch_fields(self):
+		if frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields"):
+			for row in self.items:
+				row.use_serial_batch_fields = 1
+
 	def get_gl_entries(
 		self, warehouse_account=None, default_expense_account=None, default_cost_center=None
 	):
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 8cb1a0e..3d7a947 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -98,6 +98,7 @@
 				item_doc = frappe.get_cached_doc("Item", item.item_code)
 				args = {
 					"net_rate": item.net_rate or item.rate,
+					"base_net_rate": item.base_net_rate or item.base_rate,
 					"tax_category": self.doc.get("tax_category"),
 					"posting_date": self.doc.get("posting_date"),
 					"bill_date": self.doc.get("bill_date"),
diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py
index fad216d..d2a3574 100644
--- a/erpnext/controllers/tests/test_accounts_controller.py
+++ b/erpnext/controllers/tests/test_accounts_controller.py
@@ -1108,18 +1108,18 @@
 		cr_note.reload()
 		cr_note.cancel()
 
-		# Exchange Gain/Loss Journal should've been created.
+		# with the introduction of 'cancel_system_generated_credit_debit_notes' in accounts controller
+		# JE(Credit Note) will be cancelled once the parent is cancelled
 		exc_je_for_si = self.get_journals_for(si.doctype, si.name)
 		exc_je_for_cr = self.get_journals_for(cr_note.doctype, cr_note.name)
-		self.assertNotEqual(exc_je_for_si, [])
-		self.assertEqual(len(exc_je_for_si), 1)
+		self.assertEqual(exc_je_for_si, [])
+		self.assertEqual(len(exc_je_for_si), 0)
 		self.assertEqual(len(exc_je_for_cr), 0)
 
-		# The Credit Note JE is still active and is referencing the sales invoice
-		# So, outstanding stays the same
+		# No references, full outstanding
 		si.reload()
-		self.assertEqual(si.outstanding_amount, 1)
-		self.assert_ledger_outstanding(si.doctype, si.name, 80.0, 1.0)
+		self.assertEqual(si.outstanding_amount, 2)
+		self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0)
 
 	def test_40_cost_center_from_payment_entry(self):
 		"""
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 14b7656..308e6ca 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -42,7 +42,6 @@
 
 before_install = [
 	"erpnext.setup.install.check_setup_wizard_not_completed",
-	"erpnext.setup.install.check_frappe_version",
 ]
 after_install = "erpnext.setup.install.after_install"
 
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
index 6a72c4f..dcf122c 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
@@ -90,6 +90,7 @@
 	def update_item(source, target, source_parent):
 		target_qty = source.get("qty") - source.get("ordered_qty")
 		target.qty = target_qty if flt(target_qty) >= 0 else 0
+		target.rate = source.get("rate")
 		item = get_item_defaults(target.item_code, source_parent.company)
 		if item:
 			target.item_name = item.get("item_name")
@@ -111,6 +112,10 @@
 			},
 		},
 	)
+
+	if target_doc.doctype == "Purchase Order":
+		target_doc.set_missing_values()
+
 	return target_doc
 
 
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 5dc5c38..6e9d1fc 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -312,9 +312,10 @@
 				so_item.parent,
 				so_item.item_code,
 				so_item.warehouse,
-				(
-					(so_item.qty - so_item.work_order_qty - so_item.delivered_qty) * so_item.conversion_factor
-				).as_("pending_qty"),
+				so_item.qty,
+				so_item.work_order_qty,
+				so_item.delivered_qty,
+				so_item.conversion_factor,
 				so_item.description,
 				so_item.name,
 				so_item.bom_no,
@@ -337,6 +338,11 @@
 
 		items = items_query.run(as_dict=True)
 
+		for item in items:
+			item.pending_qty = (
+				flt(item.qty) - max(item.work_order_qty, item.delivered_qty, 0) * item.conversion_factor
+			)
+
 		pi = frappe.qb.DocType("Packed Item")
 
 		packed_items_query = (
@@ -1334,10 +1340,10 @@
 	)
 
 	date_field_mapper = {
-		"from_date": self.from_date >= so.transaction_date,
-		"to_date": self.to_date <= so.transaction_date,
-		"from_delivery_date": self.from_delivery_date >= so_item.delivery_date,
-		"to_delivery_date": self.to_delivery_date <= so_item.delivery_date,
+		"from_date": so.transaction_date >= self.from_date,
+		"to_date": so.transaction_date <= self.to_date,
+		"from_delivery_date": so_item.delivery_date >= self.from_delivery_date,
+		"to_delivery_date": so_item.delivery_date <= self.to_delivery_date,
 	}
 
 	for field, value in date_field_mapper.items():
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index aa7bc5b..39beb36 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -1511,14 +1511,14 @@
 
 
 def validate_operation_data(row):
-	if row.get("qty") <= 0:
+	if flt(row.get("qty")) <= 0:
 		frappe.throw(
 			_("Quantity to Manufacture can not be zero for the operation {0}").format(
 				frappe.bold(row.get("operation"))
 			)
 		)
 
-	if row.get("qty") > row.get("pending_qty"):
+	if flt(row.get("qty")) > flt(row.get("pending_qty")):
 		frappe.throw(
 			_("For operation {0}: Quantity ({1}) can not be greater than pending quantity({2})").format(
 				frappe.bold(row.get("operation")),
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 4ea834b..ba53cf8 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -7,6 +7,7 @@
 		super.setup();
 		let me = this;
 
+		this.set_fields_onload_for_line_item();
 		this.frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle'];
 
 		frappe.flags.hide_serial_batch_dialog = true;
@@ -105,6 +106,7 @@
 
 		frappe.ui.form.on(this.frm.doctype + " Item", {
 			items_add: function(frm, cdt, cdn) {
+				debugger
 				var item = frappe.get_doc(cdt, cdn);
 				if (!item.warehouse && frm.doc.set_warehouse) {
 					item.warehouse = frm.doc.set_warehouse;
@@ -118,6 +120,13 @@
 					item.from_warehouse = frm.doc.set_from_warehouse;
 				}
 
+				if (item.docstatus === 0
+					&& frappe.meta.has_field(item.doctype, "use_serial_batch_fields")
+					&& cint(frappe.user_defaults?.use_serial_batch_fields) === 1
+				) {
+					frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1);
+				}
+
 				erpnext.accounts.dimensions.copy_dimension_from_first_row(frm, cdt, cdn, 'items');
 			}
 		});
@@ -222,7 +231,19 @@
 				};
 			});
 		}
+	}
 
+	set_fields_onload_for_line_item() {
+		if (this.frm.is_new && this.frm.doc?.items) {
+			this.frm.doc.items.forEach(item => {
+				if (item.docstatus === 0
+					&& frappe.meta.has_field(item.doctype, "use_serial_batch_fields")
+					&& cint(frappe.user_defaults?.use_serial_batch_fields) === 1
+				) {
+					frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1);
+				}
+			})
+		}
 	}
 
 	toggle_enable_for_stock_uom(field) {
@@ -462,6 +483,11 @@
 			this.frm.doc.doctype === 'Delivery Note') {
 			show_batch_dialog = 1;
 		}
+
+		if (show_batch_dialog && item.use_serial_batch_fields === 1) {
+			show_batch_dialog = 0;
+		}
+
 		item.barcode = null;
 
 
@@ -502,6 +528,7 @@
 							project: item.project || me.frm.doc.project,
 							qty: item.qty || 1,
 							net_rate: item.rate,
+							base_net_rate: item.base_net_rate,
 							stock_qty: item.stock_qty,
 							conversion_factor: item.conversion_factor,
 							weight_per_unit: item.weight_per_unit,
@@ -705,10 +732,10 @@
 				item.serial_no = item.serial_no.replace(/,/g, '\n');
 				item.conversion_factor = item.conversion_factor || 1;
 				refresh_field("serial_no", item.name, item.parentfield);
-				if (!doc.is_return && cint(frappe.user_defaults.set_qty_in_transactions_based_on_serial_no_input)) {
+				if (!doc.is_return) {
 					setTimeout(() => {
 						me.update_qty(cdt, cdn);
-					}, 10000);
+					}, 3000);
 				}
 			}
 		}
@@ -1241,20 +1268,6 @@
 		}
 	}
 
-	sync_bundle_data() {
-		let doctypes = ["Sales Invoice", "Purchase Invoice", "Delivery Note", "Purchase Receipt"];
-
-		if (this.frm.is_new() && doctypes.includes(this.frm.doc.doctype)) {
-			const barcode_scanner = new erpnext.utils.BarcodeScanner({frm:this.frm});
-			barcode_scanner.sync_bundle_data();
-			barcode_scanner.remove_item_from_localstorage();
-		}
-	}
-
-	before_save(doc) {
-		this.sync_bundle_data();
-	}
-
 	service_start_date(frm, cdt, cdn) {
 		var child = locals[cdt][cdn];
 
@@ -1902,7 +1915,7 @@
 			if (item.item_code) {
 				// Use combination of name and item code in case same item is added multiple times
 				item_codes.push([item.item_code, item.name]);
-				item_rates[item.name] = item.net_rate;
+				item_rates[item.name] = item.base_net_rate;
 				item_tax_templates[item.name] = item.item_tax_template;
 			}
 		});
diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js
index aacab0f..4d1c0c1 100644
--- a/erpnext/public/js/utils/barcode_scanner.js
+++ b/erpnext/public/js/utils/barcode_scanner.js
@@ -1,12 +1,15 @@
 erpnext.utils.BarcodeScanner = class BarcodeScanner {
 	constructor(opts) {
 		this.frm = opts.frm;
+		// frappe.flags.trigger_from_barcode_scanner is used for custom scripts
 
 		// field from which to capture input of scanned data
 		this.scan_field_name = opts.scan_field_name || "scan_barcode";
 		this.scan_barcode_field = this.frm.fields_dict[this.scan_field_name];
 
 		this.barcode_field = opts.barcode_field || "barcode";
+		this.serial_no_field = opts.serial_no_field || "serial_no";
+		this.batch_no_field = opts.batch_no_field || "batch_no";
 		this.uom_field = opts.uom_field || "uom";
 		this.qty_field = opts.qty_field || "qty";
 		// field name on row which defines max quantity to be scanned e.g. picklist
@@ -105,53 +108,52 @@
 				this.frm.has_items = false;
 			}
 
-			if (serial_no) {
-				this.is_duplicate_serial_no(row, item_code, serial_no)
-					.then((is_duplicate) => {
-						if (!is_duplicate) {
-							this.run_serially_tasks(row, data, resolve);
-						} else {
-							this.clean_up();
-							reject();
-							return;
-						}
-					});
-			} else {
-				this.run_serially_tasks(row, data, resolve);
+			if (this.is_duplicate_serial_no(row, serial_no)) {
+				this.clean_up();
+				reject();
+				return;
 			}
 
-
+			frappe.run_serially([
+				() => this.set_selector_trigger_flag(data),
+				() => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => {
+					this.show_scan_message(row.idx, row.item_code, qty);
+				}),
+				() => this.set_barcode_uom(row, uom),
+				() => this.set_serial_no(row, serial_no),
+				() => this.set_batch_no(row, batch_no),
+				() => this.set_barcode(row, barcode),
+				() => this.clean_up(),
+				() => this.revert_selector_flag(),
+				() => resolve(row)
+			]);
 		});
 	}
 
-	run_serially_tasks(row, data, resolve) {
-		const {item_code, barcode, batch_no, serial_no, uom} = data;
+	// batch and serial selector is reduandant when all info can be added by scan
+	// this flag on item row is used by transaction.js to avoid triggering selector
+	set_selector_trigger_flag(data) {
+		const {batch_no, serial_no, has_batch_no, has_serial_no} = data;
 
-		frappe.run_serially([
-			() => this.set_serial_and_batch(row, item_code, serial_no, batch_no),
-			() => this.set_barcode(row, barcode),
-			() => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => {
-				this.show_scan_message(row.idx, row.item_code, qty);
-			}),
-			() => this.set_barcode_uom(row, uom),
-			() => this.clean_up(),
-			() => {
-				if (row.serial_and_batch_bundle && !this.frm.is_new()) {
-					this.frm.save();
-				}
+		const require_selecting_batch = has_batch_no && !batch_no;
+		const require_selecting_serial = has_serial_no && !serial_no;
 
-				frappe.flags.trigger_from_barcode_scanner = false;
-			},
-			() => resolve(row),
-		]);
+		if (!(require_selecting_batch || require_selecting_serial)) {
+			frappe.flags.hide_serial_batch_dialog = true;
+		}
+	}
+
+	revert_selector_flag() {
+		frappe.flags.hide_serial_batch_dialog = false;
+		frappe.flags.trigger_from_barcode_scanner = false;
 	}
 
 	set_item(row, item_code, barcode, batch_no, serial_no) {
 		return new Promise(resolve => {
 			const increment = async (value = 1) => {
-				const item_data = {item_code: item_code};
-				item_data[this.qty_field] = Number((row[this.qty_field] || 0)) + Number(value);
+				const item_data = {item_code: item_code, use_serial_batch_fields: 1.0};
 				frappe.flags.trigger_from_barcode_scanner = true;
+				item_data[this.qty_field] = Number((row[this.qty_field] || 0)) + Number(value);
 				await frappe.model.set_value(row.doctype, row.name, item_data);
 				return value;
 			};
@@ -160,6 +162,8 @@
 				frappe.prompt(__("Please enter quantity for item {0}", [item_code]), ({value}) => {
 					increment(value).then((value) => resolve(value));
 				});
+			} else if (this.frm.has_items) {
+				this.prepare_item_for_scan(row, item_code, barcode, batch_no, serial_no);
 			} else {
 				increment().then((value) => resolve(value));
 			}
@@ -182,8 +186,9 @@
 			frappe.model.set_value(row.doctype, row.name, item_data);
 
 			frappe.run_serially([
+				() => this.set_batch_no(row, this.dialog.get_value("batch_no")),
 				() => this.set_barcode(row, this.dialog.get_value("barcode")),
-				() => this.set_serial_and_batch(row, item_code, this.dialog.get_value("serial_no"), this.dialog.get_value("batch_no")),
+				() => this.set_serial_no(row, this.dialog.get_value("serial_no")),
 				() => this.add_child_for_remaining_qty(row),
 				() => this.clean_up()
 			]);
@@ -337,144 +342,32 @@
 		}
 	}
 
-	async set_serial_and_batch(row, item_code, serial_no, batch_no) {
-		if (this.frm.is_new() || !row.serial_and_batch_bundle) {
-			this.set_bundle_in_localstorage(row, item_code, serial_no, batch_no);
-		} else if(row.serial_and_batch_bundle) {
-			frappe.call({
-				method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.update_serial_or_batch",
-				args: {
-					bundle_id: row.serial_and_batch_bundle,
-					serial_no: serial_no,
-					batch_no: batch_no,
-				},
-			})
-		}
-	}
+	async set_serial_no(row, serial_no) {
+		if (serial_no && frappe.meta.has_field(row.doctype, this.serial_no_field)) {
+			const existing_serial_nos = row[this.serial_no_field];
+			let new_serial_nos = "";
 
-	get_key_for_localstorage() {
-		let parts = this.frm.doc.name.split("-");
-		return parts[parts.length - 1] + this.frm.doc.doctype;
-	}
-
-	update_localstorage_scanned_data() {
-		let docname = this.frm.doc.name
-		if (localStorage[docname]) {
-			let items = JSON.parse(localStorage[docname]);
-			let existing_items = this.frm.doc.items.map(d => d.item_code);
-			if (!existing_items.length) {
-				localStorage.removeItem(docname);
-				return;
+			if (!!existing_serial_nos) {
+				new_serial_nos = existing_serial_nos + "\n" + serial_no;
+			} else {
+				new_serial_nos = serial_no;
 			}
-
-			for (let item_code in items) {
-				if (!existing_items.includes(item_code)) {
-					delete items[item_code];
-				}
-			}
-
-			localStorage[docname] = JSON.stringify(items);
+			await frappe.model.set_value(row.doctype, row.name, this.serial_no_field, new_serial_nos);
 		}
 	}
 
-	async set_bundle_in_localstorage(row, item_code, serial_no, batch_no) {
-		let docname = this.frm.doc.name
-
-		let entries = JSON.parse(localStorage.getItem(docname));
-		if (!entries) {
-			entries = {};
-		}
-
-		let key = item_code;
-		if (!entries[key]) {
-			entries[key] = [];
-		}
-
-		let existing_row = [];
-		if (!serial_no && batch_no) {
-			existing_row = entries[key].filter((e) => e.batch_no === batch_no);
-			if (existing_row.length) {
-				existing_row[0].qty += 1;
-			}
-		} else if (serial_no) {
-			existing_row = entries[key].filter((e) => e.serial_no === serial_no);
-			if (existing_row.length) {
-				frappe.throw(__("Serial No {0} has already scanned.", [serial_no]));
-			}
-		}
-
-		if (!existing_row.length) {
-			entries[key].push({
-				"serial_no": serial_no,
-				"batch_no": batch_no,
-				"qty": 1
-			});
-		}
-
-		localStorage.setItem(docname, JSON.stringify(entries));
-
-		// Auto remove from localstorage after 1 hour
-		setTimeout(() => {
-			localStorage.removeItem(docname);
-		}, 3600000)
-	}
-
-	remove_item_from_localstorage() {
-		let docname = this.frm.doc.name;
-		if (localStorage[docname]) {
-			localStorage.removeItem(docname);
-		}
-	}
-
-	async sync_bundle_data() {
-		let docname = this.frm.doc.name;
-
-		if (localStorage[docname]) {
-			let entries = JSON.parse(localStorage[docname]);
-			if (entries) {
-				for (let entry in entries) {
-					let row = this.frm.doc.items.filter((item) => {
-						if (item.item_code === entry) {
-							return true;
-						}
-					})[0];
-
-					if (row) {
-						this.create_serial_and_batch_bundle(row, entries, entry)
-							.then(() => {
-								if (!entries) {
-									localStorage.removeItem(docname);
-								}
-							});
-					}
-				}
-			}
-		}
-	}
-
-	async create_serial_and_batch_bundle(row, entries, key) {
-		frappe.call({
-			method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.add_serial_batch_ledgers",
-			args: {
-				entries: entries[key],
-				child_row: row,
-				doc: this.frm.doc,
-				warehouse: row.warehouse,
-				do_not_save: 1
-			},
-			callback: function(r) {
-				row.serial_and_batch_bundle = r.message.name;
-				delete entries[key];
-			}
-		})
-	}
-
 	async set_barcode_uom(row, uom) {
 		if (uom && frappe.meta.has_field(row.doctype, this.uom_field)) {
 			await frappe.model.set_value(row.doctype, row.name, this.uom_field, uom);
 		}
 	}
 
+	async set_batch_no(row, batch_no) {
+		if (batch_no && frappe.meta.has_field(row.doctype, this.batch_no_field)) {
+			await frappe.model.set_value(row.doctype, row.name, this.batch_no_field, batch_no);
+		}
+	}
+
 	async set_barcode(row, barcode) {
 		if (barcode && frappe.meta.has_field(row.doctype, this.barcode_field)) {
 			await frappe.model.set_value(row.doctype, row.name, this.barcode_field, barcode);
@@ -490,58 +383,13 @@
 		}
 	}
 
-	async is_duplicate_serial_no(row, item_code, serial_no) {
-		let is_duplicate = false;
-		const promise = new Promise((resolve, reject) => {
-			if (this.frm.is_new() || !row.serial_and_batch_bundle) {
-				is_duplicate = this.check_duplicate_serial_no_in_localstorage(item_code, serial_no);
-				if (is_duplicate) {
-					this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
-				}
+	is_duplicate_serial_no(row, serial_no) {
+		const is_duplicate = row[this.serial_no_field]?.includes(serial_no);
 
-				resolve(is_duplicate);
-			} else if (row.serial_and_batch_bundle) {
-				this.check_duplicate_serial_no_in_db(row, serial_no, (r) => {
-					if (r.message) {
-						this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
-					}
-
-					is_duplicate = r.message;
-					resolve(is_duplicate);
-				})
-			}
-		});
-
-		return await promise;
-	}
-
-	check_duplicate_serial_no_in_db(row, serial_no, response) {
-		frappe.call({
-			method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.is_duplicate_serial_no",
-			args: {
-				serial_no: serial_no,
-				bundle_id: row.serial_and_batch_bundle
-			},
-			callback(r) {
-				response(r);
-			}
-		});
-	}
-
-	check_duplicate_serial_no_in_localstorage(item_code, serial_no) {
-		let docname = this.frm.doc.name
-		let entries = JSON.parse(localStorage.getItem(docname));
-
-		if (!entries) {
-			return false;
+		if (is_duplicate) {
+			this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
 		}
-
-		let existing_row = [];
-		if (entries[item_code]) {
-			existing_row = entries[item_code].filter((e) => e.serial_no === serial_no);
-		}
-
-		return existing_row.length;
+		return is_duplicate;
 	}
 
 	get_row_to_modify_on_scan(item_code, batch_no, uom, barcode) {
@@ -587,4 +435,4 @@
 	show_alert(msg, indicator, duration=3) {
 		frappe.show_alert({message: msg, indicator: indicator}, duration);
 	}
-};
+};
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 2f6775f..3744922 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -230,6 +230,7 @@
 
 		if self.flags.is_new_doc:
 			self.link_lead_address_and_contact()
+			self.copy_communication()
 
 		self.update_customer_groups()
 
@@ -291,6 +292,17 @@
 					linked_doc.append("links", dict(link_doctype="Customer", link_name=self.name))
 					linked_doc.save(ignore_permissions=self.flags.ignore_permissions)
 
+	def copy_communication(self):
+		if not self.lead_name or not frappe.db.get_single_value(
+			"CRM Settings", "carry_forward_communication_and_comments"
+		):
+			return
+
+		from erpnext.crm.utils import copy_comments, link_communications
+
+		copy_comments("Lead", self.lead_name, self)
+		link_communications("Lead", self.lead_name, self)
+
 	def validate_name_with_customer_group(self):
 		if frappe.db.exists("Customer Group", self.name):
 			frappe.throw(
@@ -560,15 +572,14 @@
 
 
 @frappe.whitelist()
-def send_emails(args):
-	args = json.loads(args)
-	subject = _("Credit limit reached for customer {0}").format(args.get("customer"))
+def send_emails(customer, customer_outstanding, credit_limit, credit_controller_users_list):
+	if isinstance(credit_controller_users_list, str):
+		credit_controller_users_list = json.loads(credit_controller_users_list)
+	subject = _("Credit limit reached for customer {0}").format(customer)
 	message = _("Credit limit has been crossed for customer {0} ({1}/{2})").format(
-		args.get("customer"), args.get("customer_outstanding"), args.get("credit_limit")
+		customer, customer_outstanding, credit_limit
 	)
-	frappe.sendmail(
-		recipients=args.get("credit_controller_users_list"), subject=subject, message=message
-	)
+	frappe.sendmail(recipients=credit_controller_users_list, subject=subject, message=message)
 
 
 def get_customer_outstanding(
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 79f24d1..9661bac 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -906,6 +906,7 @@
 		target.run_method("set_missing_values")
 		target.run_method("set_po_nos")
 		target.run_method("calculate_taxes_and_totals")
+		target.run_method("set_use_serial_batch_fields")
 
 		if source.company_address:
 			target.update({"company_address": source.company_address})
@@ -1026,6 +1027,7 @@
 		target.run_method("set_missing_values")
 		target.run_method("set_po_nos")
 		target.run_method("calculate_taxes_and_totals")
+		target.run_method("set_use_serial_batch_fields")
 
 		if source.company_address:
 			target.update({"company_address": source.company_address})
@@ -1608,7 +1610,11 @@
 		"Sales Order",
 		source_name,
 		{
-			"Sales Order": {"doctype": "Pick List", "validation": {"docstatus": ["=", 1]}},
+			"Sales Order": {
+				"doctype": "Pick List",
+				"field_map": {"set_warehouse": "parent_warehouse"},
+				"validation": {"docstatus": ["=", 1]},
+			},
 			"Sales Order Item": {
 				"doctype": "Pick List Item",
 				"field_map": {"parent": "sales_order", "name": "sales_order_item"},
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 5ae48ee..b5189b8 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -20,6 +20,7 @@
 from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
 from erpnext.selling.doctype.sales_order.sales_order import (
 	WarehouseRequired,
+	create_pick_list,
 	make_delivery_note,
 	make_material_request,
 	make_raw_material_request,
@@ -2023,6 +2024,83 @@
 			frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested"
 		)
 
+	def test_pick_list_without_rejected_materials(self):
+		serial_and_batch_item = make_item(
+			"_Test Serial and Batch Item for Rejected Materials",
+			properties={
+				"has_serial_no": 1,
+				"has_batch_no": 1,
+				"create_new_batch": 1,
+				"batch_number_series": "BAT-TSBIFRM-.#####",
+				"serial_no_series": "SN-TSBIFRM-.#####",
+			},
+		).name
+
+		serial_item = make_item(
+			"_Test Serial Item for Rejected Materials",
+			properties={
+				"has_serial_no": 1,
+				"serial_no_series": "SN-TSIFRM-.#####",
+			},
+		).name
+
+		batch_item = make_item(
+			"_Test Batch Item for Rejected Materials",
+			properties={
+				"has_batch_no": 1,
+				"create_new_batch": 1,
+				"batch_number_series": "BAT-TBIFRM-.#####",
+			},
+		).name
+
+		normal_item = make_item("_Test Normal Item for Rejected Materials").name
+
+		warehouse = "_Test Warehouse - _TC"
+		rejected_warehouse = "_Test Dummy Rejected Warehouse - _TC"
+
+		if not frappe.db.exists("Warehouse", rejected_warehouse):
+			frappe.get_doc(
+				{
+					"doctype": "Warehouse",
+					"warehouse_name": rejected_warehouse,
+					"company": "_Test Company",
+					"warehouse_group": "_Test Warehouse Group",
+					"is_rejected_warehouse": 1,
+				}
+			).insert()
+
+		se = make_stock_entry(item_code=normal_item, qty=1, to_warehouse=warehouse, do_not_submit=True)
+		for item in [serial_and_batch_item, serial_item, batch_item]:
+			se.append("items", {"item_code": item, "qty": 1, "t_warehouse": warehouse})
+
+		se.save()
+		se.submit()
+
+		se = make_stock_entry(
+			item_code=normal_item, qty=1, to_warehouse=rejected_warehouse, do_not_submit=True
+		)
+		for item in [serial_and_batch_item, serial_item, batch_item]:
+			se.append("items", {"item_code": item, "qty": 1, "t_warehouse": rejected_warehouse})
+
+		se.save()
+		se.submit()
+
+		so = make_sales_order(item_code=normal_item, qty=2, do_not_submit=True)
+
+		for item in [serial_and_batch_item, serial_item, batch_item]:
+			so.append("items", {"item_code": item, "qty": 2, "warehouse": warehouse})
+
+		so.save()
+		so.submit()
+
+		pick_list = create_pick_list(so.name)
+
+		pick_list.save()
+		for row in pick_list.locations:
+			self.assertEqual(row.qty, 1.0)
+			self.assertFalse(row.warehouse == rejected_warehouse)
+			self.assertTrue(row.warehouse == warehouse)
+
 
 def automatically_fetch_payment_terms(enable=1):
 	accounts_settings = frappe.get_doc("Accounts Settings")
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 6239864..527f742 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -2,14 +2,12 @@
 # License: GNU General Public License v3. See license.txt
 
 
-import click
 import frappe
 from frappe import _
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
 from frappe.utils import cint
 
-import erpnext
 from erpnext.setup.default_energy_point_rules import get_default_energy_point_rules
 from erpnext.setup.doctype.incoterm.incoterm import create_incoterms
 
@@ -42,25 +40,6 @@
 		frappe.throw(message)  # nosemgrep
 
 
-def check_frappe_version():
-	def major_version(v: str) -> str:
-		return v.split(".")[0]
-
-	frappe_version = major_version(frappe.__version__)
-	erpnext_version = major_version(erpnext.__version__)
-
-	if frappe_version == erpnext_version:
-		return
-
-	click.secho(
-		f"You're attempting to install ERPNext version {erpnext_version} with Frappe version {frappe_version}. "
-		"This is not supported and will result in broken install. Switch to correct branch before installing.",
-		fg="red",
-	)
-
-	raise SystemExit(1)
-
-
 def set_single_defaults():
 	for dt in (
 		"Accounts Settings",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 58990d4..4eacbc1 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -398,6 +398,8 @@
 			self.check_credit_limit()
 		elif self.issue_credit_note:
 			self.make_return_invoice()
+
+		self.make_bundle_using_old_serial_batch_fields()
 		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating reserved qty in bin depends upon updated delivered qty in SO
 		self.update_stock_ledger()
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 0f12f38..459e7e7 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -200,7 +200,6 @@
 			},
 		)
 
-		frappe.flags.ignore_serial_batch_bundle_validation = True
 		serial_nos = [
 			"OSN-1",
 			"OSN-2",
@@ -239,6 +238,8 @@
 		)
 
 		se_doc.items[0].serial_no = "\n".join(serial_nos)
+
+		frappe.flags.use_serial_and_batch_fields = True
 		se_doc.submit()
 
 		self.assertEqual(sorted(get_serial_nos(se_doc.items[0].serial_no)), sorted(serial_nos))
@@ -294,6 +295,8 @@
 			self.assertTrue(serial_no in serial_nos)
 			self.assertFalse(serial_no in returned_serial_nos1)
 
+		frappe.flags.use_serial_and_batch_fields = False
+
 	def test_sales_return_for_non_bundled_items_partial(self):
 		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
@@ -1563,7 +1566,7 @@
 	dn.return_against = args.return_against
 
 	bundle_id = None
-	if args.get("batch_no") or args.get("serial_no"):
+	if not args.use_serial_batch_fields and (args.get("batch_no") or args.get("serial_no")):
 		type_of_transaction = args.type_of_transaction or "Outward"
 
 		if dn.is_return:
@@ -1605,6 +1608,9 @@
 			"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
 			"cost_center": args.cost_center or "_Test Cost Center - _TC",
 			"target_warehouse": args.target_warehouse,
+			"use_serial_batch_fields": args.use_serial_batch_fields,
+			"serial_no": args.serial_no if args.use_serial_batch_fields else None,
+			"batch_no": args.batch_no if args.use_serial_batch_fields else None,
 		},
 	)
 
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index a44b9ac..247672f 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -80,8 +80,11 @@
   "section_break_40",
   "pick_serial_and_batch",
   "serial_and_batch_bundle",
+  "use_serial_batch_fields",
   "column_break_eaoe",
+  "section_break_qyjv",
   "serial_no",
+  "column_break_rxvc",
   "batch_no",
   "available_qty_section",
   "actual_batch_qty",
@@ -850,6 +853,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial and Batch Bundle",
@@ -859,6 +863,7 @@
    "search_index": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "pick_serial_and_batch",
    "fieldtype": "Button",
    "label": "Pick Serial / Batch No"
@@ -874,27 +879,40 @@
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
    "fieldtype": "Text",
-   "hidden": 1,
-   "label": "Serial No",
-   "read_only": 1
+   "label": "Serial No"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "batch_no",
    "fieldtype": "Link",
-   "hidden": 1,
    "label": "Batch No",
    "options": "Batch",
-   "read_only": 1,
    "search_index": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
+   "fieldname": "section_break_qyjv",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_rxvc",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-14 18:37:38.638144",
+ "modified": "2024-02-04 14:10:31.750340",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py
index c11c410..b76f742 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py
@@ -82,6 +82,7 @@
 		target_warehouse: DF.Link | None
 		total_weight: DF.Float
 		uom: DF.Link
+		use_serial_batch_fields: DF.Check
 		warehouse: DF.Link | None
 		weight_per_unit: DF.Float
 		weight_uom: DF.Link | None
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 b4f7708..dec7506 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -149,6 +149,13 @@
 				self.get("items")[item_count - 1].applicable_charges += diff
 
 	def validate_applicable_charges_for_item(self):
+		if self.distribute_charges_based_on == "Distribute Manually" and len(self.taxes) > 1:
+			frappe.throw(
+				_(
+					"Please keep one Applicable Charges, when 'Distribute Charges Based On' is 'Distribute Manually'. For more charges, please create another Landed Cost Voucher."
+				)
+			)
+
 		based_on = self.distribute_charges_based_on.lower()
 
 		if based_on != "distribute manually":
diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json
index 5dd8934..1daf679 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.json
+++ b/erpnext/stock/doctype/packed_item/packed_item.json
@@ -20,9 +20,12 @@
   "uom",
   "section_break_9",
   "pick_serial_and_batch",
-  "serial_and_batch_bundle",
-  "serial_no",
+  "use_serial_batch_fields",
   "column_break_11",
+  "serial_and_batch_bundle",
+  "section_break_bgys",
+  "serial_no",
+  "column_break_qlha",
   "batch_no",
   "actual_batch_qty",
   "section_break_13",
@@ -118,10 +121,10 @@
    "fieldtype": "Section Break"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
    "fieldtype": "Text",
-   "label": "Serial No",
-   "read_only": 1
+   "label": "Serial No"
   },
   {
    "fieldname": "column_break_11",
@@ -131,8 +134,7 @@
    "fieldname": "batch_no",
    "fieldtype": "Link",
    "label": "Batch No",
-   "options": "Batch",
-   "read_only": 1
+   "options": "Batch"
   },
   {
    "fieldname": "section_break_13",
@@ -259,6 +261,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial and Batch Bundle",
@@ -267,16 +270,32 @@
    "print_hide": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "pick_serial_and_batch",
    "fieldtype": "Button",
    "label": "Pick Serial / Batch No"
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
+   "fieldname": "section_break_bgys",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_qlha",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-04-28 13:16:38.460806",
+ "modified": "2024-02-04 16:30:44.263964",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Packed Item",
diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py
index ed667c2..c115e33 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -47,6 +47,7 @@
 		serial_no: DF.Text | None
 		target_warehouse: DF.Link | None
 		uom: DF.Link | None
+		use_serial_batch_fields: DF.Check
 		warehouse: DF.Link | None
 	# end: auto-generated types
 
diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js
index afd6ce8..aa0e125 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.js
+++ b/erpnext/stock/doctype/pick_list/pick_list.js
@@ -16,7 +16,6 @@
 		frm.set_query('parent_warehouse', () => {
 			return {
 				filters: {
-					'is_group': 1,
 					'company': frm.doc.company
 				}
 			};
diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json
index 7259dc0..0c47434 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.json
+++ b/erpnext/stock/doctype/pick_list/pick_list.json
@@ -16,6 +16,7 @@
   "for_qty",
   "column_break_4",
   "parent_warehouse",
+  "consider_rejected_warehouses",
   "get_item_locations",
   "section_break_6",
   "scan_barcode",
@@ -51,7 +52,7 @@
    "description": "Items under this warehouse will be suggested",
    "fieldname": "parent_warehouse",
    "fieldtype": "Link",
-   "label": "Parent Warehouse",
+   "label": "Warehouse",
    "options": "Warehouse"
   },
   {
@@ -184,11 +185,18 @@
    "report_hide": 1,
    "reqd": 1,
    "search_index": 1
+  },
+  {
+   "default": "0",
+   "description": "Enable it if users want to consider rejected materials to dispatch.",
+   "fieldname": "consider_rejected_warehouses",
+   "fieldtype": "Check",
+   "label": "Consider Rejected Warehouses"
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2023-01-24 10:33:43.244476",
+ "modified": "2024-02-02 16:17:44.877426",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Pick List",
@@ -260,4 +268,4 @@
  "sort_order": "DESC",
  "states": [],
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 758448a..98ed569 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -13,7 +13,7 @@
 from frappe.query_builder import Case
 from frappe.query_builder.custom import GROUP_CONCAT
 from frappe.query_builder.functions import Coalesce, Locate, Replace, Sum
-from frappe.utils import cint, floor, flt
+from frappe.utils import ceil, cint, floor, flt
 from frappe.utils.nestedset import get_descendants_of
 
 from erpnext.selling.doctype.sales_order.sales_order import (
@@ -122,11 +122,42 @@
 
 	def on_submit(self):
 		self.validate_serial_and_batch_bundle()
+		self.make_bundle_using_old_serial_batch_fields()
 		self.update_status()
 		self.update_bundle_picked_qty()
 		self.update_reference_qty()
 		self.update_sales_order_picking_status()
 
+	def make_bundle_using_old_serial_batch_fields(self):
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+		for row in self.locations:
+			if not row.serial_no and not row.batch_no:
+				continue
+
+			if not row.use_serial_batch_fields and (row.serial_no or row.batch_no):
+				frappe.throw(_("Please enable Use Old Serial / Batch Fields to make_bundle"))
+
+			if row.use_serial_batch_fields and (not row.serial_and_batch_bundle):
+				sn_doc = SerialBatchCreation(
+					{
+						"item_code": row.item_code,
+						"warehouse": row.warehouse,
+						"voucher_type": self.doctype,
+						"voucher_no": self.name,
+						"voucher_detail_no": row.name,
+						"qty": row.stock_qty,
+						"type_of_transaction": "Outward",
+						"company": self.company,
+						"serial_nos": get_serial_nos(row.serial_no) if row.serial_no else None,
+						"batches": frappe._dict({row.batch_no: row.stock_qty}) if row.batch_no else None,
+						"batch_no": row.batch_no,
+					}
+				).make_serial_and_batch_bundle()
+
+				row.serial_and_batch_bundle = sn_doc.name
+				row.db_set("serial_and_batch_bundle", sn_doc.name)
+
 	def on_update_after_submit(self) -> None:
 		if self.has_reserved_stock():
 			msg = _(
@@ -156,6 +187,7 @@
 					{"is_cancelled": 1, "voucher_no": ""},
 				)
 
+				frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle).cancel()
 				row.db_set("serial_and_batch_bundle", None)
 
 	def on_update(self):
@@ -324,7 +356,6 @@
 		locations_replica = self.get("locations")
 
 		# reset
-		self.remove_serial_and_batch_bundle()
 		self.delete_key("locations")
 		updated_locations = frappe._dict()
 		for item_doc in items:
@@ -338,6 +369,7 @@
 					self.item_count_map.get(item_code),
 					self.company,
 					picked_item_details=picked_items_details.get(item_code),
+					consider_rejected_warehouses=self.consider_rejected_warehouses,
 				),
 			)
 
@@ -639,13 +671,19 @@
 			if not stock_qty:
 				break
 
+		serial_nos = None
+		if item_location.serial_nos:
+			serial_nos = "\n".join(item_location.serial_nos[0 : cint(stock_qty)])
+
 		locations.append(
 			frappe._dict(
 				{
 					"qty": qty,
 					"stock_qty": stock_qty,
 					"warehouse": item_location.warehouse,
-					"serial_and_batch_bundle": item_location.serial_and_batch_bundle,
+					"serial_no": serial_nos,
+					"batch_no": item_location.batch_no,
+					"use_serial_batch_fields": 1,
 				}
 			)
 		)
@@ -673,6 +711,7 @@
 	company,
 	ignore_validation=False,
 	picked_item_details=None,
+	consider_rejected_warehouses=False,
 ):
 	locations = []
 	total_picked_qty = (
@@ -681,17 +720,41 @@
 	has_serial_no = frappe.get_cached_value("Item", item_code, "has_serial_no")
 	has_batch_no = frappe.get_cached_value("Item", item_code, "has_batch_no")
 
-	if has_serial_no:
+	if has_batch_no and has_serial_no:
+		locations = get_available_item_locations_for_serial_and_batched_item(
+			item_code,
+			from_warehouses,
+			required_qty,
+			company,
+			total_picked_qty,
+			consider_rejected_warehouses=consider_rejected_warehouses,
+		)
+	elif has_serial_no:
 		locations = get_available_item_locations_for_serialized_item(
-			item_code, from_warehouses, required_qty, company, total_picked_qty
+			item_code,
+			from_warehouses,
+			required_qty,
+			company,
+			total_picked_qty,
+			consider_rejected_warehouses=consider_rejected_warehouses,
 		)
 	elif has_batch_no:
 		locations = get_available_item_locations_for_batched_item(
-			item_code, from_warehouses, required_qty, company, total_picked_qty
+			item_code,
+			from_warehouses,
+			required_qty,
+			company,
+			total_picked_qty,
+			consider_rejected_warehouses=consider_rejected_warehouses,
 		)
 	else:
 		locations = get_available_item_locations_for_other_item(
-			item_code, from_warehouses, required_qty, company, total_picked_qty
+			item_code,
+			from_warehouses,
+			required_qty,
+			company,
+			total_picked_qty,
+			consider_rejected_warehouses=consider_rejected_warehouses,
 		)
 
 	total_qty_available = sum(location.get("qty") for location in locations)
@@ -724,8 +787,56 @@
 	return locations
 
 
+def get_available_item_locations_for_serial_and_batched_item(
+	item_code,
+	from_warehouses,
+	required_qty,
+	company,
+	total_picked_qty=0,
+	consider_rejected_warehouses=False,
+):
+	# Get batch nos by FIFO
+	locations = get_available_item_locations_for_batched_item(
+		item_code,
+		from_warehouses,
+		required_qty,
+		company,
+		consider_rejected_warehouses=consider_rejected_warehouses,
+	)
+
+	if locations:
+		sn = frappe.qb.DocType("Serial No")
+		conditions = (sn.item_code == item_code) & (sn.company == company)
+
+		for location in locations:
+			location.qty = (
+				required_qty if location.qty > required_qty else location.qty
+			)  # if extra qty in batch
+
+			serial_nos = (
+				frappe.qb.from_(sn)
+				.select(sn.name)
+				.where(
+					(conditions) & (sn.batch_no == location.batch_no) & (sn.warehouse == location.warehouse)
+				)
+				.orderby(sn.creation)
+				.limit(ceil(location.qty + total_picked_qty))
+			).run(as_dict=True)
+
+			serial_nos = [sn.name for sn in serial_nos]
+			location.serial_nos = serial_nos
+			location.qty = len(serial_nos)
+
+	return locations
+
+
 def get_available_item_locations_for_serialized_item(
-	item_code, from_warehouses, required_qty, company, total_picked_qty=0
+	item_code,
+	from_warehouses,
+	required_qty,
+	company,
+	total_picked_qty=0,
+	consider_rejected_warehouses=False,
 ):
 	picked_serial_nos = get_picked_serial_nos(item_code, from_warehouses)
 
@@ -742,6 +853,10 @@
 	else:
 		query = query.where(Coalesce(sn.warehouse, "") != "")
 
+	if not consider_rejected_warehouses:
+		if rejected_warehouses := get_rejected_warehouses():
+			query = query.where(sn.warehouse.notin(rejected_warehouses))
+
 	serial_nos = query.run(as_list=True)
 
 	warehouse_serial_nos_map = frappe._dict()
@@ -757,28 +872,16 @@
 		picked_qty -= 1
 
 	locations = []
+
 	for warehouse, serial_nos in warehouse_serial_nos_map.items():
 		qty = len(serial_nos)
 
-		bundle_doc = SerialBatchCreation(
-			{
-				"item_code": item_code,
-				"warehouse": warehouse,
-				"voucher_type": "Pick List",
-				"total_qty": qty * -1,
-				"serial_nos": serial_nos,
-				"type_of_transaction": "Outward",
-				"company": company,
-				"do_not_submit": True,
-			}
-		).make_serial_and_batch_bundle()
-
 		locations.append(
 			{
 				"qty": qty,
 				"warehouse": warehouse,
 				"item_code": item_code,
-				"serial_and_batch_bundle": bundle_doc.name,
+				"serial_nos": serial_nos,
 			}
 		)
 
@@ -786,7 +889,12 @@
 
 
 def get_available_item_locations_for_batched_item(
-	item_code, from_warehouses, required_qty, company, total_picked_qty=0
+	item_code,
+	from_warehouses,
+	required_qty,
+	company,
+	total_picked_qty=0,
+	consider_rejected_warehouses=False,
 ):
 	locations = []
 	data = get_auto_batch_nos(
@@ -801,42 +909,42 @@
 	)
 
 	warehouse_wise_batches = frappe._dict()
+	rejected_warehouses = get_rejected_warehouses()
+
 	for d in data:
+		if (
+			not consider_rejected_warehouses and rejected_warehouses and d.warehouse in rejected_warehouses
+		):
+			continue
+
 		if d.warehouse not in warehouse_wise_batches:
 			warehouse_wise_batches.setdefault(d.warehouse, defaultdict(float))
 
 		warehouse_wise_batches[d.warehouse][d.batch_no] += d.qty
 
 	for warehouse, batches in warehouse_wise_batches.items():
-		qty = sum(batches.values())
-
-		bundle_doc = SerialBatchCreation(
-			{
-				"item_code": item_code,
-				"warehouse": warehouse,
-				"voucher_type": "Pick List",
-				"total_qty": qty * -1,
-				"batches": batches,
-				"type_of_transaction": "Outward",
-				"company": company,
-				"do_not_submit": True,
-			}
-		).make_serial_and_batch_bundle()
-
-		locations.append(
-			{
-				"qty": qty,
-				"warehouse": warehouse,
-				"item_code": item_code,
-				"serial_and_batch_bundle": bundle_doc.name,
-			}
-		)
+		for batch_no, qty in batches.items():
+			locations.append(
+				frappe._dict(
+					{
+						"qty": qty,
+						"warehouse": warehouse,
+						"item_code": item_code,
+						"batch_no": batch_no,
+					}
+				)
+			)
 
 	return locations
 
 
 def get_available_item_locations_for_other_item(
-	item_code, from_warehouses, required_qty, company, total_picked_qty=0
+	item_code,
+	from_warehouses,
+	required_qty,
+	company,
+	total_picked_qty=0,
+	consider_rejected_warehouses=False,
 ):
 	bin = frappe.qb.DocType("Bin")
 	query = (
@@ -853,6 +961,10 @@
 		wh = frappe.qb.DocType("Warehouse")
 		query = query.from_(wh).where((bin.warehouse == wh.name) & (wh.company == company))
 
+	if not consider_rejected_warehouses:
+		if rejected_warehouses := get_rejected_warehouses():
+			query = query.where(bin.warehouse.notin(rejected_warehouses))
+
 	item_locations = query.run(as_dict=True)
 
 	return item_locations
@@ -1174,3 +1286,15 @@
 	item.serial_no = location.serial_no
 	item.batch_no = location.batch_no
 	item.material_request_item = location.material_request_item
+
+
+def get_rejected_warehouses():
+	if not hasattr(frappe.local, "rejected_warehouses"):
+		frappe.local.rejected_warehouses = []
+
+	if not frappe.local.rejected_warehouses:
+		frappe.local.rejected_warehouses = frappe.get_all(
+			"Warehouse", filters={"is_rejected_warehouse": 1}, pluck="name"
+		)
+
+	return frappe.local.rejected_warehouses
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index 322b0b4..cffd0d2 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -217,6 +217,8 @@
 		)
 
 		pick_list.save()
+		pick_list.submit()
+
 		self.assertEqual(pick_list.locations[0].item_code, "_Test Serialized Item")
 		self.assertEqual(pick_list.locations[0].warehouse, "_Test Warehouse - _TC")
 		self.assertEqual(pick_list.locations[0].qty, 5)
@@ -239,7 +241,7 @@
 		pr1 = make_purchase_receipt(item_code="Batched Item", qty=1, rate=100.0)
 
 		pr1.load_from_db()
-		oldest_batch_no = pr1.items[0].batch_no
+		oldest_batch_no = get_batch_from_bundle(pr1.items[0].serial_and_batch_bundle)
 
 		pr2 = make_purchase_receipt(item_code="Batched Item", qty=2, rate=100.0)
 
@@ -302,6 +304,8 @@
 			}
 		)
 		pick_list.set_item_locations()
+		pick_list.submit()
+		pick_list.reload()
 
 		self.assertEqual(
 			get_batch_from_bundle(pick_list.locations[0].serial_and_batch_bundle), oldest_batch_no
@@ -310,6 +314,7 @@
 			get_serial_nos_from_bundle(pick_list.locations[0].serial_and_batch_bundle), oldest_serial_nos
 		)
 
+		pick_list.cancel()
 		pr1.cancel()
 		pr2.cancel()
 
@@ -671,29 +676,22 @@
 
 		so = make_sales_order(item_code=item, qty=25.0, rate=100)
 		pl = create_pick_list(so.name)
+		pl.submit()
 		# pick half the qty
 		for loc in pl.locations:
 			self.assertEqual(loc.qty, 25.0)
 			self.assertTrue(loc.serial_and_batch_bundle)
 
-			data = frappe.get_all(
-				"Serial and Batch Entry",
-				fields=["qty", "batch_no"],
-				filters={"parent": loc.serial_and_batch_bundle},
-			)
-
-			for d in data:
-				self.assertEqual(d.batch_no, "PICKLT-000001")
-				self.assertEqual(d.qty, 25.0 * -1)
-
 		pl.save()
 		pl.submit()
 
 		so1 = make_sales_order(item_code=item, qty=10.0, rate=100)
-		pl = create_pick_list(so1.name)
+		pl1 = create_pick_list(so1.name)
+		pl1.submit()
+
 		# pick half the qty
-		for loc in pl.locations:
-			self.assertEqual(loc.qty, 10.0)
+		for loc in pl1.locations:
+			self.assertEqual(loc.qty, 5.0)
 			self.assertTrue(loc.serial_and_batch_bundle)
 
 			data = frappe.get_all(
@@ -709,8 +707,7 @@
 				elif d.batch_no == "PICKLT-000002":
 					self.assertEqual(d.qty, 5.0 * -1)
 
-		pl.save()
-		pl.submit()
+		pl1.cancel()
 		pl.cancel()
 
 	def test_picklist_for_serial_item(self):
@@ -723,6 +720,7 @@
 
 		so = make_sales_order(item_code=item, qty=25.0, rate=100)
 		pl = create_pick_list(so.name)
+		pl.submit()
 		picked_serial_nos = []
 		# pick half the qty
 		for loc in pl.locations:
@@ -736,13 +734,11 @@
 			picked_serial_nos = [d.serial_no for d in data]
 			self.assertEqual(len(picked_serial_nos), 25)
 
-		pl.save()
-		pl.submit()
-
 		so1 = make_sales_order(item_code=item, qty=10.0, rate=100)
-		pl = create_pick_list(so1.name)
+		pl1 = create_pick_list(so1.name)
+		pl1.submit()
 		# pick half the qty
-		for loc in pl.locations:
+		for loc in pl1.locations:
 			self.assertEqual(loc.qty, 10.0)
 			self.assertTrue(loc.serial_and_batch_bundle)
 
@@ -756,8 +752,7 @@
 			for d in data:
 				self.assertTrue(d.serial_no not in picked_serial_nos)
 
-		pl.save()
-		pl.submit()
+		pl1.cancel()
 		pl.cancel()
 
 	def test_picklist_with_bundles(self):
diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json
index e8e4afc..962fa9f 100644
--- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json
+++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json
@@ -24,8 +24,11 @@
   "serial_no_and_batch_section",
   "pick_serial_and_batch",
   "serial_and_batch_bundle",
-  "serial_no",
+  "use_serial_batch_fields",
   "column_break_20",
+  "section_break_ecxc",
+  "serial_no",
+  "column_break_belw",
   "batch_no",
   "column_break_15",
   "sales_order",
@@ -72,19 +75,17 @@
    "read_only": 1
   },
   {
-   "depends_on": "serial_no",
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
    "fieldtype": "Small Text",
-   "label": "Serial No",
-   "read_only": 1
+   "label": "Serial No"
   },
   {
-   "depends_on": "batch_no",
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "batch_no",
    "fieldtype": "Link",
    "label": "Batch No",
    "options": "Batch",
-   "read_only": 1,
    "search_index": 1
   },
   {
@@ -195,6 +196,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial and Batch Bundle",
@@ -204,6 +206,7 @@
    "search_index": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "pick_serial_and_batch",
    "fieldtype": "Button",
    "label": "Pick Serial / Batch No"
@@ -218,11 +221,26 @@
    "print_hide": 1,
    "read_only": 1,
    "report_hide": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
+   "fieldname": "section_break_ecxc",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_belw",
+   "fieldtype": "Column Break"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2023-07-26 12:54:15.785962",
+ "modified": "2024-02-04 16:12:16.257951",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Pick List Item",
diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.py b/erpnext/stock/doctype/pick_list_item/pick_list_item.py
index 6e5a94e..f3f6298 100644
--- a/erpnext/stock/doctype/pick_list_item/pick_list_item.py
+++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.py
@@ -37,6 +37,7 @@
 		stock_reserved_qty: DF.Float
 		stock_uom: DF.Link | None
 		uom: DF.Link | None
+		use_serial_batch_fields: DF.Check
 		warehouse: DF.Link | None
 	# end: auto-generated types
 
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index bf6080b..c9fe7d2 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -369,6 +369,7 @@
 		else:
 			self.db_set("status", "Completed")
 
+		self.make_bundle_using_old_serial_batch_fields()
 		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating ordered qty, reserved_qty_for_subcontract in bin
 		# depends upon updated ordered qty in PO
@@ -1360,16 +1361,16 @@
 	for lcv in landed_cost_vouchers:
 		landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent)
 
+		based_on_field = None
 		# Use amount field for total item cost for manually cost distributed LCVs
-		if landed_cost_voucher_doc.distribute_charges_based_on == "Distribute Manually":
-			based_on_field = "amount"
-		else:
+		if landed_cost_voucher_doc.distribute_charges_based_on != "Distribute Manually":
 			based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
 
 		total_item_cost = 0
 
-		for item in landed_cost_voucher_doc.items:
-			total_item_cost += item.get(based_on_field)
+		if based_on_field:
+			for item in landed_cost_voucher_doc.items:
+				total_item_cost += item.get(based_on_field)
 
 		for item in landed_cost_voucher_doc.items:
 			if item.receipt_document == purchase_document:
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 65c08c1..2d20922 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -2230,6 +2230,93 @@
 		pr_doc.reload()
 		self.assertFalse(pr_doc.items[0].from_warehouse)
 
+	def test_use_serial_batch_fields_for_serial_nos(self):
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+		from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
+			create_stock_reconciliation,
+		)
+
+		item_code = make_item(
+			"_Test Use Serial Fields Item Serial Item",
+			properties={"has_serial_no": 1, "serial_no_series": "SNU-TSFISI-.#####"},
+		).name
+
+		serial_nos = [
+			"SNU-TSFISI-000011",
+			"SNU-TSFISI-000012",
+			"SNU-TSFISI-000013",
+			"SNU-TSFISI-000014",
+			"SNU-TSFISI-000015",
+		]
+
+		pr = make_purchase_receipt(
+			item_code=item_code,
+			qty=5,
+			serial_no="\n".join(serial_nos),
+			use_serial_batch_fields=1,
+			rate=100,
+		)
+
+		self.assertEqual(pr.items[0].use_serial_batch_fields, 1)
+		self.assertFalse(pr.items[0].serial_no)
+		self.assertTrue(pr.items[0].serial_and_batch_bundle)
+
+		sbb_doc = frappe.get_doc("Serial and Batch Bundle", pr.items[0].serial_and_batch_bundle)
+
+		for row in sbb_doc.entries:
+			self.assertTrue(row.serial_no in serial_nos)
+
+		serial_nos.remove("SNU-TSFISI-000015")
+
+		sr = create_stock_reconciliation(
+			item_code=item_code,
+			serial_no="\n".join(serial_nos),
+			qty=4,
+			warehouse=pr.items[0].warehouse,
+			use_serial_batch_fields=1,
+			do_not_submit=True,
+		)
+		sr.reload()
+
+		serial_nos = get_serial_nos(sr.items[0].current_serial_no)
+		self.assertEqual(len(serial_nos), 5)
+		self.assertEqual(sr.items[0].current_qty, 5)
+
+		new_serial_nos = get_serial_nos(sr.items[0].serial_no)
+		self.assertEqual(len(new_serial_nos), 4)
+		self.assertEqual(sr.items[0].qty, 4)
+		self.assertEqual(sr.items[0].use_serial_batch_fields, 1)
+		self.assertFalse(sr.items[0].current_serial_and_batch_bundle)
+		self.assertFalse(sr.items[0].serial_and_batch_bundle)
+		self.assertTrue(sr.items[0].current_serial_no)
+		sr.submit()
+
+		sr.reload()
+		self.assertTrue(sr.items[0].current_serial_and_batch_bundle)
+		self.assertTrue(sr.items[0].serial_and_batch_bundle)
+
+		serial_no_status = frappe.db.get_value("Serial No", "SNU-TSFISI-000015", "status")
+
+		self.assertTrue(serial_no_status != "Active")
+
+		dn = create_delivery_note(
+			item_code=item_code,
+			qty=4,
+			serial_no="\n".join(new_serial_nos),
+			use_serial_batch_fields=1,
+		)
+
+		self.assertTrue(dn.items[0].serial_and_batch_bundle)
+		self.assertEqual(dn.items[0].qty, 4)
+		doc = frappe.get_doc("Serial and Batch Bundle", dn.items[0].serial_and_batch_bundle)
+		for row in doc.entries:
+			self.assertTrue(row.serial_no in new_serial_nos)
+
+		for sn in new_serial_nos:
+			serial_no_status = frappe.db.get_value("Serial No", sn, "status")
+			self.assertTrue(serial_no_status != "Active")
+
 
 def prepare_data_for_internal_transfer():
 	from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
@@ -2399,7 +2486,7 @@
 	uom = args.uom or frappe.db.get_value("Item", item_code, "stock_uom") or "_Test UOM"
 
 	bundle_id = None
-	if args.get("batch_no") or args.get("serial_no"):
+	if not args.use_serial_batch_fields and (args.get("batch_no") or args.get("serial_no")):
 		batches = {}
 		if args.get("batch_no"):
 			batches = frappe._dict({args.batch_no: qty})
@@ -2441,6 +2528,9 @@
 			"cost_center": args.cost_center
 			or frappe.get_cached_value("Company", pr.company, "cost_center"),
 			"asset_location": args.location or "Test Location",
+			"use_serial_batch_fields": args.use_serial_batch_fields or 0,
+			"serial_no": args.serial_no if args.use_serial_batch_fields else "",
+			"batch_no": args.batch_no if args.use_serial_batch_fields else "",
 		},
 	)
 
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 9bd692a..6b01047 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -94,6 +94,7 @@
   "section_break_45",
   "add_serial_batch_bundle",
   "serial_and_batch_bundle",
+  "use_serial_batch_fields",
   "col_break5",
   "add_serial_batch_for_rejected_qty",
   "rejected_serial_and_batch_bundle",
@@ -1003,6 +1004,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial and Batch Bundle",
@@ -1020,24 +1022,22 @@
   {
    "fieldname": "serial_no",
    "fieldtype": "Text",
-   "label": "Serial No",
-   "read_only": 1
+   "label": "Serial No"
   },
   {
    "fieldname": "rejected_serial_no",
    "fieldtype": "Text",
-   "label": "Rejected Serial No",
-   "read_only": 1
+   "label": "Rejected Serial No"
   },
   {
    "fieldname": "batch_no",
    "fieldtype": "Link",
    "label": "Batch No",
    "options": "Batch",
-   "read_only": 1,
    "search_index": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "rejected_serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Rejected Serial and Batch Bundle",
@@ -1045,11 +1045,13 @@
    "options": "Serial and Batch Bundle"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0",
    "fieldname": "add_serial_batch_for_rejected_qty",
    "fieldtype": "Button",
    "label": "Add Serial / Batch No (Rejected Qty)"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "section_break_3vxt",
    "fieldtype": "Section Break"
   },
@@ -1058,6 +1060,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0",
    "fieldname": "add_serial_batch_bundle",
    "fieldtype": "Button",
    "label": "Add Serial / Batch No"
@@ -1098,12 +1101,18 @@
    "read_only": 1,
    "report_hide": 1,
    "search_index": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-12-25 22:32:09.801965",
+ "modified": "2024-02-04 11:48:06.653771",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py
index aed8d21..3c6dcdc 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py
@@ -99,6 +99,7 @@
 		supplier_part_no: DF.Data | None
 		total_weight: DF.Float
 		uom: DF.Link
+		use_serial_batch_fields: DF.Check
 		valuation_rate: DF.Currency
 		warehouse: DF.Link | None
 		weight_per_unit: DF.Float
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 4f45210..31fc2ca 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -281,6 +281,7 @@
 		repost_gl_entries(doc)
 
 		doc.set_status("Completed")
+		remove_attached_file(doc.name)
 
 	except Exception as e:
 		if frappe.flags.in_test:
@@ -309,6 +310,13 @@
 			frappe.db.commit()
 
 
+def remove_attached_file(docname):
+	if file_name := frappe.db.get_value(
+		"File", {"attached_to_name": docname, "attached_to_doctype": "Repost Item Valuation"}, "name"
+	):
+		frappe.delete_doc("File", file_name, delete_permanently=True)
+
+
 def repost_sl_entries(doc):
 	if doc.based_on == "Transaction":
 		repost_future_sle(
diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
index 5b76e44..f96a612 100644
--- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
@@ -420,3 +420,38 @@
 
 		self.assertRaises(frappe.ValidationError, riv.save)
 		doc.cancel()
+
+	def test_remove_attached_file(self):
+		item_code = make_item("_Test Remove Attached File Item", properties={"is_stock_item": 1})
+
+		make_purchase_receipt(
+			item_code=item_code,
+			qty=1,
+			rate=100,
+		)
+
+		pr1 = make_purchase_receipt(
+			item_code=item_code,
+			qty=1,
+			rate=100,
+			posting_date=add_days(today(), days=-1),
+		)
+
+		if docname := frappe.db.exists("Repost Item Valuation", {"voucher_no": pr1.name}):
+			self.assertFalse(
+				frappe.db.get_value(
+					"File",
+					{"attached_to_doctype": "Repost Item Valuation", "attached_to_name": docname},
+					"name",
+				)
+			)
+		else:
+			repost_entries = create_item_wise_repost_entries(pr1.doctype, pr1.name)
+			for entry in repost_entries:
+				self.assertFalse(
+					frappe.db.get_value(
+						"File",
+						{"attached_to_doctype": "Repost Item Valuation", "attached_to_name": entry.name},
+						"name",
+					)
+				)
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 9cad8f6..eb4df29 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
@@ -1117,7 +1117,7 @@
 	if isinstance(data, list):
 		return data
 
-	return [s.strip() for s in cstr(data).strip().upper().replace(",", "\n").split("\n") if s.strip()]
+	return [s.strip() for s in cstr(data).strip().replace(",", "\n").split("\n") if s.strip()]
 
 
 @frappe.whitelist()
@@ -1256,7 +1256,7 @@
 
 
 def get_type_of_transaction(parent_doc, child_row):
-	type_of_transaction = child_row.type_of_transaction
+	type_of_transaction = child_row.get("type_of_transaction")
 	if parent_doc.get("doctype") == "Stock Entry":
 		type_of_transaction = "Outward" if child_row.s_warehouse else "Inward"
 
@@ -1384,6 +1384,8 @@
 
 	filters = {"item_code": kwargs.item_code}
 
+	# ignore_warehouse is used for backdated stock transactions
+	# There might be chances that the serial no not exists in the warehouse during backdated stock transactions
 	if not kwargs.get("ignore_warehouse"):
 		filters["warehouse"] = ("is", "set")
 		if kwargs.warehouse:
@@ -1677,7 +1679,10 @@
 			query = query.where(sb_entry.batch_no == kwargs.batch_no)
 
 	if kwargs.warehouse:
-		query = query.where(sre.warehouse == kwargs.warehouse)
+		if isinstance(kwargs.warehouse, list):
+			query = query.where(sre.warehouse.isin(kwargs.warehouse))
+		else:
+			query = query.where(sre.warehouse == kwargs.warehouse)
 
 	if kwargs.ignore_voucher_nos:
 		query = query.where(sre.name.notin(kwargs.ignore_voucher_nos))
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
index 0d453fb..f430943 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
@@ -136,6 +136,7 @@
 
 	def test_old_batch_valuation(self):
 		frappe.flags.ignore_serial_batch_bundle_validation = True
+		frappe.flags.use_serial_and_batch_fields = True
 		batch_item_code = "Old Batch Item Valuation 1"
 		make_item(
 			batch_item_code,
@@ -240,6 +241,7 @@
 		bundle_doc.submit()
 
 		frappe.flags.ignore_serial_batch_bundle_validation = False
+		frappe.flags.use_serial_and_batch_fields = False
 
 	def test_old_serial_no_valuation(self):
 		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
@@ -259,6 +261,7 @@
 		)
 
 		frappe.flags.ignore_serial_batch_bundle_validation = True
+		frappe.flags.use_serial_and_batch_fields = True
 
 		serial_no_id = "Old Serial No 1"
 		if not frappe.db.exists("Serial No", serial_no_id):
@@ -320,6 +323,9 @@
 		for row in bundle_doc.entries:
 			self.assertEqual(flt(row.stock_value_difference, 2), -100.00)
 
+		frappe.flags.ignore_serial_batch_bundle_validation = False
+		frappe.flags.use_serial_and_batch_fields = False
+
 	def test_batch_not_belong_to_serial_no(self):
 		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
 
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 122664c..5f4f393 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -151,9 +151,7 @@
 	if isinstance(serial_no, list):
 		return serial_no
 
-	return [
-		s.strip() for s in cstr(serial_no).strip().upper().replace(",", "\n").split("\n") if s.strip()
-	]
+	return [s.strip() for s in cstr(serial_no).strip().replace(",", "\n").split("\n") if s.strip()]
 
 
 def clean_serial_no_string(serial_no: str) -> str:
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index faccfa3..10e3522 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -274,6 +274,7 @@
 
 	def on_submit(self):
 		self.validate_closed_subcontracting_order()
+		self.make_bundle_using_old_serial_batch_fields()
 		self.update_stock_ledger()
 		self.update_work_order()
 		self.validate_subcontract_order()
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index 83bfaa0..0f67e47 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -92,6 +92,9 @@
 		else:
 			args.qty = cint(args.qty)
 
+	if args.serial_no or args.batch_no:
+		args.use_serial_batch_fields = True
+
 	# purpose
 	if not args.purpose:
 		if args.source and args.target:
@@ -162,6 +165,7 @@
 		)
 
 	args.serial_no = serial_number
+
 	s.append(
 		"items",
 		{
@@ -177,6 +181,7 @@
 			"batch_no": args.batch_no,
 			"cost_center": args.cost_center,
 			"expense_account": args.expense_account,
+			"use_serial_batch_fields": args.use_serial_batch_fields,
 		},
 	)
 
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 23dacc8..af91536 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -680,6 +680,7 @@
 	def test_serial_move(self):
 		se = make_serialized_item()
 		serial_no = get_serial_nos_from_bundle(se.get("items")[0].serial_and_batch_bundle)[0]
+		frappe.flags.use_serial_and_batch_fields = True
 
 		se = frappe.copy_doc(test_records[0])
 		se.purpose = "Material Transfer"
@@ -700,6 +701,7 @@
 		self.assertTrue(
 			frappe.db.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse - _TC"
 		)
+		frappe.flags.use_serial_and_batch_fields = False
 
 	def test_serial_cancel(self):
 		se, serial_nos = self.test_serial_by_series()
@@ -999,6 +1001,8 @@
 			do_not_save=True,
 		)
 
+		frappe.flags.use_serial_and_batch_fields = True
+
 		cls_obj = SerialBatchCreation(
 			{
 				"type_of_transaction": "Inward",
@@ -1035,84 +1039,7 @@
 
 		s2.submit()
 		s2.cancel()
-
-	# def test_retain_sample(self):
-	# 	from erpnext.stock.doctype.batch.batch import get_batch_qty
-	# 	from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
-
-	# 	create_warehouse("Test Warehouse for Sample Retention")
-	# 	frappe.db.set_value(
-	# 		"Stock Settings",
-	# 		None,
-	# 		"sample_retention_warehouse",
-	# 		"Test Warehouse for Sample Retention - _TC",
-	# 	)
-
-	# 	test_item_code = "Retain Sample Item"
-	# 	if not frappe.db.exists("Item", test_item_code):
-	# 		item = frappe.new_doc("Item")
-	# 		item.item_code = test_item_code
-	# 		item.item_name = "Retain Sample Item"
-	# 		item.description = "Retain Sample Item"
-	# 		item.item_group = "All Item Groups"
-	# 		item.is_stock_item = 1
-	# 		item.has_batch_no = 1
-	# 		item.create_new_batch = 1
-	# 		item.retain_sample = 1
-	# 		item.sample_quantity = 4
-	# 		item.save()
-
-	# 	receipt_entry = frappe.new_doc("Stock Entry")
-	# 	receipt_entry.company = "_Test Company"
-	# 	receipt_entry.purpose = "Material Receipt"
-	# 	receipt_entry.append(
-	# 		"items",
-	# 		{
-	# 			"item_code": test_item_code,
-	# 			"t_warehouse": "_Test Warehouse - _TC",
-	# 			"qty": 40,
-	# 			"basic_rate": 12,
-	# 			"cost_center": "_Test Cost Center - _TC",
-	# 			"sample_quantity": 4,
-	# 		},
-	# 	)
-	# 	receipt_entry.set_stock_entry_type()
-	# 	receipt_entry.insert()
-	# 	receipt_entry.submit()
-
-	# 	retention_data = move_sample_to_retention_warehouse(
-	# 		receipt_entry.company, receipt_entry.get("items")
-	# 	)
-	# 	retention_entry = frappe.new_doc("Stock Entry")
-	# 	retention_entry.company = retention_data.company
-	# 	retention_entry.purpose = retention_data.purpose
-	# 	retention_entry.append(
-	# 		"items",
-	# 		{
-	# 			"item_code": test_item_code,
-	# 			"t_warehouse": "Test Warehouse for Sample Retention - _TC",
-	# 			"s_warehouse": "_Test Warehouse - _TC",
-	# 			"qty": 4,
-	# 			"basic_rate": 12,
-	# 			"cost_center": "_Test Cost Center - _TC",
-	# 			"batch_no": get_batch_from_bundle(receipt_entry.get("items")[0].serial_and_batch_bundle),
-	# 		},
-	# 	)
-	# 	retention_entry.set_stock_entry_type()
-	# 	retention_entry.insert()
-	# 	retention_entry.submit()
-
-	# 	qty_in_usable_warehouse = get_batch_qty(
-	# 		get_batch_from_bundle(receipt_entry.get("items")[0].serial_and_batch_bundle), "_Test Warehouse - _TC", "_Test Item"
-	# 	)
-	# 	qty_in_retention_warehouse = get_batch_qty(
-	# 		get_batch_from_bundle(receipt_entry.get("items")[0].serial_and_batch_bundle),
-	# 		"Test Warehouse for Sample Retention - _TC",
-	# 		"_Test Item",
-	# 	)
-
-	# 	self.assertEqual(qty_in_usable_warehouse, 36)
-	# 	self.assertEqual(qty_in_retention_warehouse, 4)
+		frappe.flags.use_serial_and_batch_fields = False
 
 	def test_quality_check(self):
 		item_code = "_Test Item For QC"
@@ -1785,6 +1712,48 @@
 
 		self.assertRaises(frappe.ValidationError, se1.cancel)
 
+	def test_auto_reorder_level(self):
+		from erpnext.stock.reorder_item import reorder_item
+
+		item_doc = make_item(
+			"Test Auto Reorder Item - 001",
+			properties={"stock_uom": "Kg", "purchase_uom": "Nos", "is_stock_item": 1},
+			uoms=[{"uom": "Nos", "conversion_factor": 5}],
+		)
+
+		if not frappe.db.exists("Item Reorder", {"parent": item_doc.name}):
+			item_doc.append(
+				"reorder_levels",
+				{
+					"warehouse_reorder_level": 0,
+					"warehouse_reorder_qty": 10,
+					"warehouse": "_Test Warehouse - _TC",
+					"material_request_type": "Purchase",
+				},
+			)
+
+		item_doc.save(ignore_permissions=True)
+
+		frappe.db.set_single_value("Stock Settings", "auto_indent", 1)
+
+		mr_list = reorder_item()
+
+		frappe.db.set_single_value("Stock Settings", "auto_indent", 0)
+		mrs = frappe.get_all(
+			"Material Request Item",
+			fields=["qty", "stock_uom", "stock_qty"],
+			filters={"item_code": item_doc.name, "uom": "Nos"},
+		)
+
+		for mri in mrs:
+			self.assertEqual(mri.stock_uom, "Kg")
+			self.assertEqual(mri.stock_qty, 10)
+			self.assertEqual(mri.qty, 2)
+
+		for mr in mr_list:
+			mr.cancel()
+			mr.delete()
+
 
 def make_serialized_item(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index bd84a2b..c7b3daa 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -47,9 +47,12 @@
   "amount",
   "serial_no_batch",
   "add_serial_batch_bundle",
-  "serial_and_batch_bundle",
+  "use_serial_batch_fields",
   "col_break4",
+  "serial_and_batch_bundle",
+  "section_break_rdtg",
   "serial_no",
+  "column_break_prps",
   "batch_no",
   "accounting",
   "expense_account",
@@ -289,27 +292,27 @@
    "no_copy": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
    "fieldtype": "Small Text",
    "label": "Serial No",
    "no_copy": 1,
    "oldfieldname": "serial_no",
-   "oldfieldtype": "Text",
-   "read_only": 1
+   "oldfieldtype": "Text"
   },
   {
    "fieldname": "col_break4",
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "batch_no",
    "fieldtype": "Link",
    "label": "Batch No",
    "no_copy": 1,
    "oldfieldname": "batch_no",
    "oldfieldtype": "Link",
-   "options": "Batch",
-   "read_only": 1
+   "options": "Batch"
   },
   {
    "depends_on": "eval:parent.inspection_required && doc.t_warehouse",
@@ -573,24 +576,41 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0",
    "fieldname": "add_serial_batch_bundle",
    "fieldtype": "Button",
    "label": "Add Serial / Batch No"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial and Batch Bundle",
    "no_copy": 1,
    "options": "Serial and Batch Bundle",
    "print_hide": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
+   "fieldname": "section_break_rdtg",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_prps",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2024-01-12 11:56:04.626103",
+ "modified": "2024-02-04 16:16:47.606270",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
index a6dd0fa..47c443c 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
@@ -63,6 +63,7 @@
 		transfer_qty: DF.Float
 		transferred_qty: DF.Float
 		uom: DF.Link
+		use_serial_batch_fields: DF.Check
 		valuation_rate: DF.Currency
 	# end: auto-generated types
 
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 277ca01..04441f0 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -93,6 +93,9 @@
 		self.validate_inventory_dimension_negative_stock()
 
 	def validate_inventory_dimension_negative_stock(self):
+		if self.is_cancelled:
+			return
+
 		extra_cond = ""
 		kwargs = {}
 
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index d8a3f2e..c099953 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -482,6 +482,8 @@
 			(item, warehouses[0], batches[1], 1, 200),
 			(item, warehouses[0], batches[0], 1, 200),
 		]
+
+		frappe.flags.use_serial_and_batch_fields = True
 		dns = create_delivery_note_entries_for_batchwise_item_valuation_test(dn_entry_list)
 		sle_details = fetch_sle_details_for_doc_list(dns, ["stock_value_difference"])
 		svd_list = [-1 * d["stock_value_difference"] for d in sle_details]
@@ -494,6 +496,8 @@
 				"Incorrect 'Incoming Rate' values fetched for DN items",
 			)
 
+		frappe.flags.use_serial_and_batch_fields = False
+
 	def test_batchwise_item_valuation_stock_reco(self):
 		item, warehouses, batches = setup_item_valuation_test()
 		state = {"stock_value": 0.0, "qty": 0.0}
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 8e9dcb0..ba7f9c5 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -198,6 +198,7 @@
 					frappe.model.set_value(cdt, cdn, "current_amount", r.message.rate * r.message.qty);
 					frappe.model.set_value(cdt, cdn, "amount", row.qty * row.valuation_rate);
 					frappe.model.set_value(cdt, cdn, "current_serial_no", r.message.serial_nos);
+					frappe.model.set_value(cdt, cdn, "use_serial_batch_fields", r.message.use_serial_batch_fields);
 
 					if (frm.doc.purpose == "Stock Reconciliation" && !frm.doc.scan_mode) {
 						frappe.model.set_value(cdt, cdn, "serial_no", r.message.serial_nos);
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 788ae0d..ce08615 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -99,6 +99,8 @@
 					)
 
 	def on_submit(self):
+		self.make_bundle_for_current_qty()
+		self.make_bundle_using_old_serial_batch_fields()
 		self.update_stock_ledger()
 		self.make_gl_entries()
 		self.repost_future_sle_and_gle()
@@ -116,9 +118,52 @@
 		self.repost_future_sle_and_gle()
 		self.delete_auto_created_batches()
 
+	def make_bundle_for_current_qty(self):
+		from erpnext.stock.serial_batch_bundle import SerialBatchCreation
+
+		for row in self.items:
+			if not row.use_serial_batch_fields:
+				continue
+
+			if row.current_serial_and_batch_bundle:
+				continue
+
+			if row.current_qty and (row.current_serial_no or row.batch_no):
+				sn_doc = SerialBatchCreation(
+					{
+						"item_code": row.item_code,
+						"warehouse": row.warehouse,
+						"posting_date": self.posting_date,
+						"posting_time": self.posting_time,
+						"voucher_type": self.doctype,
+						"voucher_no": self.name,
+						"voucher_detail_no": row.name,
+						"qty": row.qty,
+						"type_of_transaction": "Outward",
+						"company": self.company,
+						"is_rejected": 0,
+						"serial_nos": get_serial_nos(row.current_serial_no) if row.current_serial_no else None,
+						"batches": frappe._dict({row.batch_no: row.qty}) if row.batch_no else None,
+						"batch_no": row.batch_no,
+						"do_not_submit": True,
+					}
+				).make_serial_and_batch_bundle()
+
+				row.current_serial_and_batch_bundle = sn_doc.name
+				row.db_set(
+					{
+						"current_serial_and_batch_bundle": sn_doc.name,
+						"current_serial_no": "",
+						"batch_no": "",
+					}
+				)
+
 	def set_current_serial_and_batch_bundle(self, voucher_detail_no=None, save=False) -> None:
 		"""Set Serial and Batch Bundle for each item"""
 		for item in self.items:
+			if not save and item.use_serial_batch_fields:
+				continue
+
 			if voucher_detail_no and voucher_detail_no != item.name:
 				continue
 
@@ -229,6 +274,9 @@
 
 	def set_new_serial_and_batch_bundle(self):
 		for item in self.items:
+			if item.use_serial_batch_fields:
+				continue
+
 			if not item.qty:
 				continue
 
@@ -291,8 +339,10 @@
 				inventory_dimensions_dict=inventory_dimensions_dict,
 			)
 
-			if (item.qty is None or item.qty == item_dict.get("qty")) and (
-				item.valuation_rate is None or item.valuation_rate == item_dict.get("rate")
+			if (
+				(item.qty is None or item.qty == item_dict.get("qty"))
+				and (item.valuation_rate is None or item.valuation_rate == item_dict.get("rate"))
+				and (not item.serial_no or (item.serial_no == item_dict.get("serial_nos")))
 			):
 				return False
 			else:
@@ -303,6 +353,11 @@
 				if item.valuation_rate is None:
 					item.valuation_rate = item_dict.get("rate")
 
+				if item_dict.get("serial_nos"):
+					item.current_serial_no = item_dict.get("serial_nos")
+					if self.purpose == "Stock Reconciliation" and not item.serial_no and item.qty:
+						item.serial_no = item.current_serial_no
+
 				item.current_qty = item_dict.get("qty")
 				item.current_valuation_rate = item_dict.get("rate")
 				self.calculate_difference_amount(item, item_dict)
@@ -1135,9 +1190,16 @@
 	has_serial_no = bool(item_dict.get("has_serial_no"))
 	has_batch_no = bool(item_dict.get("has_batch_no"))
 
+	use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields")
+
 	if not batch_no and has_batch_no:
 		# Not enough information to fetch data
-		return {"qty": 0, "rate": 0, "serial_nos": None}
+		return {
+			"qty": 0,
+			"rate": 0,
+			"serial_nos": None,
+			"use_serial_batch_fields": use_serial_batch_fields,
+		}
 
 	# TODO: fetch only selected batch's values
 	data = get_stock_balance(
@@ -1160,7 +1222,12 @@
 			get_batch_qty(batch_no, warehouse, posting_date=posting_date, posting_time=posting_time) or 0
 		)
 
-	return {"qty": qty, "rate": rate, "serial_nos": serial_nos}
+	return {
+		"qty": qty,
+		"rate": rate,
+		"serial_nos": serial_nos,
+		"use_serial_batch_fields": use_serial_batch_fields,
+	}
 
 
 @frappe.whitelist()
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 0bbfed4..479a74a 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -1094,7 +1094,7 @@
 	)
 
 	bundle_id = None
-	if args.batch_no or args.serial_no:
+	if not args.use_serial_batch_fields and (args.batch_no or args.serial_no):
 		batches = frappe._dict({})
 		if args.batch_no:
 			batches[args.batch_no] = args.qty
@@ -1125,7 +1125,10 @@
 			"warehouse": args.warehouse or "_Test Warehouse - _TC",
 			"qty": args.qty,
 			"valuation_rate": args.rate,
+			"serial_no": args.serial_no if args.use_serial_batch_fields else None,
+			"batch_no": args.batch_no if args.use_serial_batch_fields else None,
 			"serial_and_batch_bundle": bundle_id,
+			"use_serial_batch_fields": args.use_serial_batch_fields,
 		},
 	)
 
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
index fc4ae6a..7342259 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -19,11 +19,14 @@
   "allow_zero_valuation_rate",
   "serial_no_and_batch_section",
   "add_serial_batch_bundle",
-  "serial_and_batch_bundle",
-  "batch_no",
+  "use_serial_batch_fields",
   "column_break_11",
+  "serial_and_batch_bundle",
   "current_serial_and_batch_bundle",
+  "section_break_lypk",
   "serial_no",
+  "column_break_eefq",
+  "batch_no",
   "section_break_3",
   "current_qty",
   "current_amount",
@@ -103,10 +106,10 @@
    "label": "Serial No and Batch"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
    "fieldtype": "Long Text",
-   "label": "Serial No",
-   "read_only": 1
+   "label": "Serial No"
   },
   {
    "fieldname": "column_break_11",
@@ -171,11 +174,11 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "batch_no",
    "fieldtype": "Link",
    "label": "Batch No",
    "options": "Batch",
-   "read_only": 1,
    "search_index": 1
   },
   {
@@ -195,6 +198,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial / Batch Bundle",
@@ -204,6 +208,7 @@
    "search_index": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "current_serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Current Serial / Batch Bundle",
@@ -212,6 +217,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "add_serial_batch_bundle",
    "fieldtype": "Button",
    "label": "Add Serial / Batch No"
@@ -222,11 +228,26 @@
    "fieldtype": "Link",
    "label": "Item Group",
    "options": "Item Group"
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
+   "fieldname": "section_break_lypk",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_eefq",
+   "fieldtype": "Column Break"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2024-01-14 10:04:23.599951",
+ "modified": "2024-02-04 16:19:44.576022",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Reconciliation Item",
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py
index c82cdf5..1938fec 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py
@@ -26,6 +26,7 @@
 		current_valuation_rate: DF.Currency
 		has_item_scanned: DF.Data | None
 		item_code: DF.Link
+		item_group: DF.Link | None
 		item_name: DF.Data | None
 		parent: DF.Data
 		parentfield: DF.Data
@@ -34,6 +35,7 @@
 		quantity_difference: DF.ReadOnly | None
 		serial_and_batch_bundle: DF.Link | None
 		serial_no: DF.LongText | None
+		use_serial_batch_fields: DF.Check
 		valuation_rate: DF.Currency
 		warehouse: DF.Link
 	# end: auto-generated types
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
index 76cedd4..bf5ea74 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
@@ -315,7 +315,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-10-19 16:41:16.545416",
+ "modified": "2024-02-07 16:05:17.772098",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Reservation Entry",
@@ -335,6 +335,90 @@
    "share": 1,
    "submit": 1,
    "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Sales Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Purchase Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Sales User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Purchase User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
   }
  ],
  "sort_field": "modified",
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index dc27974..c698283 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -50,6 +50,7 @@
   "disable_serial_no_and_batch_selector",
   "use_naming_series",
   "naming_series_prefix",
+  "use_serial_batch_fields",
   "stock_planning_tab",
   "auto_material_request",
   "auto_indent",
@@ -420,6 +421,12 @@
    "fieldname": "auto_reserve_stock_for_sales_order_on_purchase",
    "fieldtype": "Check",
    "label": "Auto Reserve Stock for Sales Order on Purchase"
+  },
+  {
+   "default": "1",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial / Batch Fields"
   }
  ],
  "icon": "icon-cog",
@@ -427,7 +434,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2024-01-30 14:03:52.143457",
+ "modified": "2024-02-04 12:01:31.931864",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Settings",
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index 088c7cd..c4960aa 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -57,6 +57,7 @@
 		stock_uom: DF.Link | None
 		update_existing_price_list_rate: DF.Check
 		use_naming_series: DF.Check
+		use_serial_batch_fields: DF.Check
 		valuation_method: DF.Literal["FIFO", "Moving Average", "LIFO"]
 	# end: auto-generated types
 
@@ -68,6 +69,7 @@
 			"allow_negative_stock",
 			"default_warehouse",
 			"set_qty_in_transactions_based_on_serial_no_input",
+			"use_serial_batch_fields",
 		]:
 			frappe.db.set_default(key, self.get(key, ""))
 
diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json
index 43b2ad2..7b0cade 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.json
+++ b/erpnext/stock/doctype/warehouse/warehouse.json
@@ -13,6 +13,7 @@
   "column_break_3",
   "is_group",
   "parent_warehouse",
+  "is_rejected_warehouse",
   "column_break_4",
   "account",
   "company",
@@ -249,13 +250,20 @@
   {
    "fieldname": "column_break_qajx",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "description": "If yes, then this warehouse will be used to store rejected materials",
+   "fieldname": "is_rejected_warehouse",
+   "fieldtype": "Check",
+   "label": "Is Rejected Warehouse"
   }
  ],
  "icon": "fa fa-building",
  "idx": 1,
  "is_tree": 1,
  "links": [],
- "modified": "2023-05-29 13:10:43.333160",
+ "modified": "2024-01-24 16:27:28.299520",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Warehouse",
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index ebcdd11..1cb1057 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -86,7 +86,8 @@
 
 	get_party_item_code(args, item, out)
 
-	set_valuation_rate(out, args)
+	if args.get("doctype") in ["Sales Order", "Quotation"]:
+		set_valuation_rate(out, args)
 
 	update_party_blanket_order(args, out)
 
@@ -269,7 +270,9 @@
 	if not item:
 		item = frappe.get_doc("Item", args.get("item_code"))
 
-	if item.variant_of and not item.taxes:
+	if (
+		item.variant_of and not item.taxes and frappe.db.exists("Item Tax", {"parent": item.variant_of})
+	):
 		item.update_template_tables()
 
 	item_defaults = get_item_defaults(item.name, args.company)
@@ -543,7 +546,7 @@
 		args = {
 			"company": company,
 			"tax_category": tax_category,
-			"net_rate": item_rates.get(item_code[1]),
+			"base_net_rate": item_rates.get(item_code[1]),
 		}
 
 		if item_tax_templates:
@@ -635,7 +638,7 @@
 	if not flt(tax.maximum_net_rate):
 		# No range specified, just ignore
 		return True
-	elif flt(tax.minimum_net_rate) <= flt(args.get("net_rate")) <= flt(tax.maximum_net_rate):
+	elif flt(tax.minimum_net_rate) <= flt(args.get("base_net_rate")) <= flt(tax.maximum_net_rate):
 		return True
 
 	return False
diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py
index 276531a..59f8b20 100644
--- a/erpnext/stock/reorder_item.py
+++ b/erpnext/stock/reorder_item.py
@@ -34,73 +34,157 @@
 		erpnext.get_default_company() or frappe.db.sql("""select name from tabCompany limit 1""")[0][0]
 	)
 
-	items_to_consider = frappe.db.sql_list(
-		"""select name from `tabItem` item
-		where is_stock_item=1 and has_variants=0
-			and disabled=0
-			and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s)
-			and (exists (select name from `tabItem Reorder` ir where ir.parent=item.name)
-				or (variant_of is not null and variant_of != ''
-				and exists (select name from `tabItem Reorder` ir where ir.parent=item.variant_of))
-			)""",
-		{"today": nowdate()},
-	)
+	items_to_consider = get_items_for_reorder()
 
 	if not items_to_consider:
 		return
 
 	item_warehouse_projected_qty = get_item_warehouse_projected_qty(items_to_consider)
 
-	def add_to_material_request(
-		item_code, warehouse, reorder_level, reorder_qty, material_request_type, warehouse_group=None
-	):
-		if warehouse not in warehouse_company:
+	def add_to_material_request(**kwargs):
+		if isinstance(kwargs, dict):
+			kwargs = frappe._dict(kwargs)
+
+		if kwargs.warehouse not in warehouse_company:
 			# a disabled warehouse
 			return
 
-		reorder_level = flt(reorder_level)
-		reorder_qty = flt(reorder_qty)
+		reorder_level = flt(kwargs.reorder_level)
+		reorder_qty = flt(kwargs.reorder_qty)
 
 		# projected_qty will be 0 if Bin does not exist
-		if warehouse_group:
-			projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse_group))
+		if kwargs.warehouse_group:
+			projected_qty = flt(
+				item_warehouse_projected_qty.get(kwargs.item_code, {}).get(kwargs.warehouse_group)
+			)
 		else:
-			projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse))
+			projected_qty = flt(
+				item_warehouse_projected_qty.get(kwargs.item_code, {}).get(kwargs.warehouse)
+			)
 
 		if (reorder_level or reorder_qty) and projected_qty <= reorder_level:
 			deficiency = reorder_level - projected_qty
 			if deficiency > reorder_qty:
 				reorder_qty = deficiency
 
-			company = warehouse_company.get(warehouse) or default_company
+			company = warehouse_company.get(kwargs.warehouse) or default_company
 
-			material_requests[material_request_type].setdefault(company, []).append(
-				{"item_code": item_code, "warehouse": warehouse, "reorder_qty": reorder_qty}
+			material_requests[kwargs.material_request_type].setdefault(company, []).append(
+				{
+					"item_code": kwargs.item_code,
+					"warehouse": kwargs.warehouse,
+					"reorder_qty": reorder_qty,
+					"item_details": kwargs.item_details,
+				}
 			)
 
-	for item_code in items_to_consider:
-		item = frappe.get_doc("Item", item_code)
+	for item_code, reorder_levels in items_to_consider.items():
+		for d in reorder_levels:
+			if d.has_variants:
+				continue
 
-		if item.variant_of and not item.get("reorder_levels"):
-			item.update_template_tables()
-
-		if item.get("reorder_levels"):
-			for d in item.get("reorder_levels"):
-				add_to_material_request(
-					item_code,
-					d.warehouse,
-					d.warehouse_reorder_level,
-					d.warehouse_reorder_qty,
-					d.material_request_type,
-					warehouse_group=d.warehouse_group,
-				)
+			add_to_material_request(
+				item_code=item_code,
+				warehouse=d.warehouse,
+				reorder_level=d.warehouse_reorder_level,
+				reorder_qty=d.warehouse_reorder_qty,
+				material_request_type=d.material_request_type,
+				warehouse_group=d.warehouse_group,
+				item_details=frappe._dict(
+					{
+						"item_code": item_code,
+						"name": item_code,
+						"item_name": d.item_name,
+						"item_group": d.item_group,
+						"brand": d.brand,
+						"description": d.description,
+						"stock_uom": d.stock_uom,
+						"purchase_uom": d.purchase_uom,
+					}
+				),
+			)
 
 	if material_requests:
 		return create_material_request(material_requests)
 
 
+def get_items_for_reorder() -> dict[str, list]:
+	reorder_table = frappe.qb.DocType("Item Reorder")
+	item_table = frappe.qb.DocType("Item")
+
+	query = (
+		frappe.qb.from_(reorder_table)
+		.inner_join(item_table)
+		.on(reorder_table.parent == item_table.name)
+		.select(
+			reorder_table.warehouse,
+			reorder_table.warehouse_group,
+			reorder_table.material_request_type,
+			reorder_table.warehouse_reorder_level,
+			reorder_table.warehouse_reorder_qty,
+			item_table.name,
+			item_table.stock_uom,
+			item_table.purchase_uom,
+			item_table.description,
+			item_table.item_name,
+			item_table.item_group,
+			item_table.brand,
+			item_table.variant_of,
+			item_table.has_variants,
+		)
+		.where(
+			(item_table.disabled == 0)
+			& (item_table.is_stock_item == 1)
+			& (
+				(item_table.end_of_life.isnull())
+				| (item_table.end_of_life > nowdate())
+				| (item_table.end_of_life == "0000-00-00")
+			)
+		)
+	)
+
+	data = query.run(as_dict=True)
+	itemwise_reorder = frappe._dict({})
+	for d in data:
+		itemwise_reorder.setdefault(d.name, []).append(d)
+
+	itemwise_reorder = get_reorder_levels_for_variants(itemwise_reorder)
+
+	return itemwise_reorder
+
+
+def get_reorder_levels_for_variants(itemwise_reorder):
+	item_table = frappe.qb.DocType("Item")
+
+	query = (
+		frappe.qb.from_(item_table)
+		.select(
+			item_table.name,
+			item_table.variant_of,
+		)
+		.where(
+			(item_table.disabled == 0)
+			& (item_table.is_stock_item == 1)
+			& (
+				(item_table.end_of_life.isnull())
+				| (item_table.end_of_life > nowdate())
+				| (item_table.end_of_life == "0000-00-00")
+			)
+			& (item_table.variant_of.notnull())
+		)
+	)
+
+	variants_item = query.run(as_dict=True)
+	for row in variants_item:
+		if not itemwise_reorder.get(row.name) and itemwise_reorder.get(row.variant_of):
+			itemwise_reorder.setdefault(row.name, []).extend(itemwise_reorder.get(row.variant_of, []))
+
+	return itemwise_reorder
+
+
 def get_item_warehouse_projected_qty(items_to_consider):
 	item_warehouse_projected_qty = {}
+	items_to_consider = list(items_to_consider.keys())
 
 	for item_code, warehouse, projected_qty in frappe.db.sql(
 		"""select item_code, warehouse, projected_qty
@@ -164,7 +248,7 @@
 
 				for d in items:
 					d = frappe._dict(d)
-					item = frappe.get_doc("Item", d.item_code)
+					item = d.get("item_details")
 					uom = item.stock_uom
 					conversion_factor = 1.0
 
@@ -190,6 +274,7 @@
 							"item_code": d.item_code,
 							"schedule_date": add_days(nowdate(), cint(item.lead_time_days)),
 							"qty": qty,
+							"conversion_factor": conversion_factor,
 							"uom": uom,
 							"stock_uom": item.stock_uom,
 							"warehouse": d.warehouse,
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index ed84a5c..2693238 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -90,8 +90,7 @@
 				self.opening_data.setdefault(group_by_key, entry)
 
 	def prepare_new_data(self):
-		if not self.sle_entries:
-			return
+		self.item_warehouse_map = self.get_item_warehouse_map()
 
 		if self.filters.get("show_stock_ageing_data"):
 			self.filters["show_warehouse_wise_stock"] = True
@@ -99,7 +98,8 @@
 
 		_func = itemgetter(1)
 
-		self.item_warehouse_map = self.get_item_warehouse_map()
+		del self.sle_entries
+
 		sre_details = self.get_sre_reserved_qty_details()
 
 		variant_values = {}
@@ -143,15 +143,22 @@
 		item_warehouse_map = {}
 		self.opening_vouchers = self.get_opening_vouchers()
 
-		for entry in self.sle_entries:
-			group_by_key = self.get_group_by_key(entry)
-			if group_by_key not in item_warehouse_map:
-				self.initialize_data(item_warehouse_map, group_by_key, entry)
+		if self.filters.get("show_stock_ageing_data"):
+			self.sle_entries = self.sle_query.run(as_dict=True)
 
-			self.prepare_item_warehouse_map(item_warehouse_map, entry, group_by_key)
+		with frappe.db.unbuffered_cursor():
+			if not self.filters.get("show_stock_ageing_data"):
+				self.sle_entries = self.sle_query.run(as_dict=True, as_iterator=True)
 
-			if self.opening_data.get(group_by_key):
-				del self.opening_data[group_by_key]
+			for entry in self.sle_entries:
+				group_by_key = self.get_group_by_key(entry)
+				if group_by_key not in item_warehouse_map:
+					self.initialize_data(item_warehouse_map, group_by_key, entry)
+
+				self.prepare_item_warehouse_map(item_warehouse_map, entry, group_by_key)
+
+				if self.opening_data.get(group_by_key):
+					del self.opening_data[group_by_key]
 
 		for group_by_key, entry in self.opening_data.items():
 			if group_by_key not in item_warehouse_map:
@@ -252,7 +259,8 @@
 			.where(
 				(table.docstatus == 1)
 				& (table.company == self.filters.company)
-				& ((table.to_date <= self.from_date))
+				& (table.to_date <= self.from_date)
+				& (table.status == "Completed")
 			)
 			.orderby(table.to_date, order=Order.desc)
 			.limit(1)
@@ -305,7 +313,7 @@
 		if self.filters.get("company"):
 			query = query.where(sle.company == self.filters.get("company"))
 
-		self.sle_entries = query.run(as_dict=True)
+		self.sle_query = query
 
 	def apply_inventory_dimensions_filters(self, query, sle) -> str:
 		inventory_dimension_fields = self.get_inventory_dimension_fields()
diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py
index 4cfe5d8..d8b5b34 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()
 
@@ -793,6 +794,9 @@
 			setattr(self, "actual_qty", qty)
 			self.__dict__["actual_qty"] = self.actual_qty
 
+		if not hasattr(self, "use_serial_batch_fields"):
+			setattr(self, "use_serial_batch_fields", 0)
+
 	def duplicate_package(self):
 		if not self.serial_and_batch_bundle:
 			return
@@ -901,9 +905,14 @@
 			self.batches = get_available_batches(kwargs)
 
 	def set_auto_serial_batch_entries_for_inward(self):
+		print(self.get("serial_nos"))
+
 		if (self.get("batches") and self.has_batch_no) or (
 			self.get("serial_nos") and self.has_serial_no
 		):
+			if self.use_serial_batch_fields and self.get("serial_nos"):
+				self.make_serial_no_if_not_exists()
+
 			return
 
 		self.batch_no = None
@@ -915,6 +924,59 @@
 		else:
 			self.batches = frappe._dict({self.batch_no: abs(self.actual_qty)})
 
+	def make_serial_no_if_not_exists(self):
+		non_exists_serial_nos = []
+		for row in self.serial_nos:
+			if not frappe.db.exists("Serial No", row):
+				non_exists_serial_nos.append(row)
+
+		if non_exists_serial_nos:
+			self.make_serial_nos(non_exists_serial_nos)
+
+	def make_serial_nos(self, serial_nos):
+		serial_nos_details = []
+		batch_no = None
+		if self.batches:
+			batch_no = list(self.batches.keys())[0]
+
+		for serial_no in serial_nos:
+			serial_nos_details.append(
+				(
+					serial_no,
+					serial_no,
+					now(),
+					now(),
+					frappe.session.user,
+					frappe.session.user,
+					self.warehouse,
+					self.company,
+					self.item_code,
+					self.item_name,
+					self.description,
+					"Active",
+					batch_no,
+				)
+			)
+
+		if serial_nos_details:
+			fields = [
+				"name",
+				"serial_no",
+				"creation",
+				"modified",
+				"owner",
+				"modified_by",
+				"warehouse",
+				"company",
+				"item_code",
+				"item_name",
+				"description",
+				"status",
+				"batch_no",
+			]
+
+			frappe.db.bulk_insert("Serial No", fields=fields, values=set(serial_nos_details))
+
 	def set_serial_batch_entries(self, doc):
 		if self.get("serial_nos"):
 			serial_no_wise_batch = frappe._dict({})
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 45764f3..e88b192 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -897,9 +897,12 @@
 
 		self.wh_data.stock_value = round_off_if_near_zero(self.wh_data.stock_value + doc.total_amount)
 
-		self.wh_data.qty_after_transaction += doc.total_qty
+		precision = doc.precision("total_qty")
+		self.wh_data.qty_after_transaction += flt(doc.total_qty, precision)
 		if self.wh_data.qty_after_transaction:
-			self.wh_data.valuation_rate = self.wh_data.stock_value / self.wh_data.qty_after_transaction
+			self.wh_data.valuation_rate = flt(self.wh_data.stock_value, precision) / flt(
+				self.wh_data.qty_after_transaction, precision
+			)
 
 	def validate_negative_stock(self, sle):
 		"""
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 76af5d7..9eac172 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -11,6 +11,9 @@
 from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime
 
 import erpnext
+from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
+	get_available_serial_nos,
+)
 from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
 from erpnext.stock.serial_batch_bundle import BatchNoValuation, SerialNoValuation
 from erpnext.stock.valuation import FIFOValuation, LIFOValuation
@@ -125,7 +128,21 @@
 
 	if with_valuation_rate:
 		if with_serial_no:
-			serial_nos = get_serial_nos_data_after_transactions(args)
+			serial_no_details = get_available_serial_nos(
+				frappe._dict(
+					{
+						"item_code": item_code,
+						"warehouse": warehouse,
+						"posting_date": posting_date,
+						"posting_time": posting_time,
+						"ignore_warehouse": 1,
+					}
+				)
+			)
+
+			serial_nos = ""
+			if serial_no_details:
+				serial_nos = "\n".join(d.serial_no for d in serial_no_details)
 
 			return (
 				(last_entry.qty_after_transaction, last_entry.valuation_rate, serial_nos)
@@ -140,38 +157,6 @@
 		return last_entry.qty_after_transaction if last_entry else 0.0
 
 
-def get_serial_nos_data_after_transactions(args):
-
-	serial_nos = set()
-	args = frappe._dict(args)
-	sle = frappe.qb.DocType("Stock Ledger Entry")
-
-	stock_ledger_entries = (
-		frappe.qb.from_(sle)
-		.select("serial_no", "actual_qty")
-		.where(
-			(sle.item_code == args.item_code)
-			& (sle.warehouse == args.warehouse)
-			& (
-				CombineDatetime(sle.posting_date, sle.posting_time)
-				< CombineDatetime(args.posting_date, args.posting_time)
-			)
-			& (sle.is_cancelled == 0)
-		)
-		.orderby(sle.posting_date, sle.posting_time, sle.creation)
-		.run(as_dict=1)
-	)
-
-	for stock_ledger_entry in stock_ledger_entries:
-		changed_serial_no = get_serial_nos_data(stock_ledger_entry.serial_no)
-		if stock_ledger_entry.actual_qty > 0:
-			serial_nos.update(changed_serial_no)
-		else:
-			serial_nos.difference_update(changed_serial_no)
-
-	return "\n".join(serial_nos)
-
-
 def get_serial_nos_data(serial_nos):
 	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
index 7c2a1f1..3467b82 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
@@ -149,6 +149,7 @@
 		self.update_prevdoc_status()
 		self.set_subcontracting_order_status()
 		self.set_consumed_qty_in_subcontract_order()
+		self.make_bundle_using_old_serial_batch_fields()
 		self.update_stock_ledger()
 		self.make_gl_entries()
 		self.repost_future_sle_and_gle()
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
index 9bfc2fd..f9e0a0b 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
@@ -48,11 +48,14 @@
   "reference_name",
   "section_break_45",
   "serial_and_batch_bundle",
-  "serial_no",
+  "use_serial_batch_fields",
   "col_break5",
   "rejected_serial_and_batch_bundle",
-  "batch_no",
+  "section_break_jshh",
+  "serial_no",
   "rejected_serial_no",
+  "column_break_henr",
+  "batch_no",
   "manufacture_details",
   "manufacturer",
   "column_break_16",
@@ -311,22 +314,20 @@
    "label": "Serial and Batch Details"
   },
   {
-   "depends_on": "eval:!doc.is_fixed_asset",
+   "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
    "fieldtype": "Small Text",
    "label": "Serial No",
-   "no_copy": 1,
-   "read_only": 1
+   "no_copy": 1
   },
   {
-   "depends_on": "eval:!doc.is_fixed_asset",
+   "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1",
    "fieldname": "batch_no",
    "fieldtype": "Link",
    "label": "Batch No",
    "no_copy": 1,
    "options": "Batch",
-   "print_hide": 1,
-   "read_only": 1
+   "print_hide": 1
   },
   {
    "depends_on": "eval: !parent.is_return",
@@ -478,6 +479,7 @@
    "label": "Accounting Details"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Serial and Batch Bundle",
@@ -486,6 +488,7 @@
    "print_hide": 1
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "rejected_serial_and_batch_bundle",
    "fieldtype": "Link",
    "label": "Rejected Serial and Batch Bundle",
@@ -546,12 +549,27 @@
    "fieldtype": "Check",
    "label": "Include Exploded Items",
    "print_hide": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
+   "fieldname": "section_break_jshh",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_henr",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-30 12:05:51.920705",
+ "modified": "2024-02-04 16:23:30.374865",
  "modified_by": "Administrator",
  "module": "Subcontracting",
  "name": "Subcontracting Receipt Item",
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py
index d02160e..1a4ce5b 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py
@@ -58,6 +58,7 @@
 		subcontracting_order: DF.Link | None
 		subcontracting_order_item: DF.Data | None
 		subcontracting_receipt_item: DF.Data | None
+		use_serial_batch_fields: DF.Check
 		warehouse: DF.Link | None
 	# end: auto-generated types
 
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json
index 90bcf4e..957b6a2 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json
@@ -26,10 +26,13 @@
   "current_stock",
   "secbreak_3",
   "serial_and_batch_bundle",
-  "batch_no",
+  "use_serial_batch_fields",
   "col_break4",
+  "subcontracting_order",
+  "section_break_zwnh",
   "serial_no",
-  "subcontracting_order"
+  "column_break_qibi",
+  "batch_no"
  ],
  "fields": [
   {
@@ -60,19 +63,19 @@
    "width": "300px"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "batch_no",
    "fieldtype": "Link",
    "label": "Batch No",
    "no_copy": 1,
-   "options": "Batch",
-   "read_only": 1
+   "options": "Batch"
   },
   {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
    "fieldtype": "Text",
    "label": "Serial No",
-   "no_copy": 1,
-   "read_only": 1
+   "no_copy": 1
   },
   {
    "fieldname": "col_break1",
@@ -198,6 +201,7 @@
   },
   {
    "columns": 2,
+   "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
    "fieldname": "serial_and_batch_bundle",
    "fieldtype": "Link",
    "in_list_view": 1,
@@ -205,12 +209,27 @@
    "no_copy": 1,
    "options": "Serial and Batch Bundle",
    "print_hide": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "use_serial_batch_fields",
+   "fieldtype": "Check",
+   "label": "Use Serial No / Batch Fields"
+  },
+  {
+   "depends_on": "eval:doc.use_serial_batch_fields === 1",
+   "fieldname": "section_break_zwnh",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_qibi",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-03-15 13:55:08.132626",
+ "modified": "2024-02-04 16:32:17.534162",
  "modified_by": "Administrator",
  "module": "Subcontracting",
  "name": "Subcontracting Receipt Supplied Item",
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py
index 2ee5551..8f09197a 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py
@@ -35,6 +35,7 @@
 		serial_no: DF.Text | None
 		stock_uom: DF.Link | None
 		subcontracting_order: DF.Link | None
+		use_serial_batch_fields: DF.Check
 	# end: auto-generated types
 
 	pass
diff --git a/pyproject.toml b/pyproject.toml
index 604aa44..8a0f12c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -39,3 +39,6 @@
 use_parentheses = true
 ensure_newline_before_comments = true
 indent = "\t"
+
+[tool.bench.frappe-dependencies]
+frappe = ">=16.0.0-dev,<17.0.0"