Merge branch 'develop' into Provision-to-send-Accounts-Receivable-Reports
diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml
index d5f0052..e2d8957 100644
--- a/.github/workflows/patch.yml
+++ b/.github/workflows/patch.yml
@@ -43,9 +43,11 @@
           fi
 
       - name: Setup Python
-        uses: "gabrielfalcao/pyenv-action@v9"
+        uses: "actions/setup-python@v4"
         with:
-          versions: 3.10:latest, 3.7:latest
+          python-version: |
+              3.7
+              3.10
 
       - name: Setup Node
         uses: actions/setup-node@v2
@@ -92,7 +94,6 @@
       - name: Install
         run: |
           pip install frappe-bench
-          pyenv global $(pyenv versions | grep '3.10')
           bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
         env:
           DB: mariadb
@@ -107,7 +108,6 @@
           git -C "apps/frappe" remote set-url upstream https://github.com/frappe/frappe.git
           git -C "apps/erpnext" remote set-url upstream https://github.com/frappe/erpnext.git
 
-          pyenv global $(pyenv versions | grep '3.7')
           for version in $(seq 12 13)
           do
               echo "Updating to v$version"
@@ -120,7 +120,7 @@
               git -C "apps/erpnext" checkout -q -f $branch_name
 
               rm -rf ~/frappe-bench/env
-              bench setup env
+              bench setup env --python python3.7
               bench pip install -e ./apps/payments
               bench pip install -e ./apps/erpnext
 
@@ -132,9 +132,8 @@
           git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
           git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
 
-          pyenv global $(pyenv versions | grep '3.10')
           rm -rf ~/frappe-bench/env
-          bench -v setup env
+          bench -v setup env --python python3.10
           bench pip install -e ./apps/payments
           bench pip install -e ./apps/erpnext
 
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 09482d7..7cd498d 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -61,7 +61,10 @@
   "column_break_25",
   "frozen_accounts_modifier",
   "tab_break_dpet",
-  "show_balance_in_coa"
+  "show_balance_in_coa",
+  "banking_tab",
+  "enable_party_matching",
+  "enable_fuzzy_matching"
  ],
  "fields": [
   {
@@ -383,6 +386,26 @@
    "fieldname": "show_taxes_as_table_in_print",
    "fieldtype": "Check",
    "label": "Show Taxes as Table in Print"
+  },
+  {
+   "fieldname": "banking_tab",
+   "fieldtype": "Tab Break",
+   "label": "Banking"
+  },
+  {
+   "default": "0",
+   "description": "Auto match and set the Party in Bank Transactions",
+   "fieldname": "enable_party_matching",
+   "fieldtype": "Check",
+   "label": "Enable Automatic Party Matching"
+  },
+  {
+   "default": "0",
+   "depends_on": "enable_party_matching",
+   "description": "Approximately match the description/party name against parties",
+   "fieldname": "enable_fuzzy_matching",
+   "fieldtype": "Check",
+   "label": "Enable Fuzzy Matching"
   }
  ],
  "icon": "icon-cog",
@@ -390,7 +413,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-06-13 18:47:46.430291",
+ "modified": "2023-06-15 16:35:45.123456",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py
index b91f0f9..363a277 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account.py
@@ -70,7 +70,6 @@
 	return doc
 
 
-@frappe.whitelist()
 def get_party_bank_account(party_type, party):
 	return frappe.db.get_value(party_type, party, "default_bank_account")
 
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
index c4a23a6..0eef3e9 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -10,6 +10,7 @@
 from frappe.query_builder.custom import ConstantColumn
 from frappe.utils import cint, flt
 
+from erpnext import get_default_cost_center
 from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount
 from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
 	get_amounts_not_reflected_in_system,
@@ -140,6 +141,9 @@
 					second_account
 				)
 			)
+
+	company = frappe.get_value("Account", company_account, "company")
+
 	accounts = []
 	# Multi Currency?
 	accounts.append(
@@ -149,6 +153,7 @@
 			"debit_in_account_currency": bank_transaction.withdrawal,
 			"party_type": party_type,
 			"party": party,
+			"cost_center": get_default_cost_center(company),
 		}
 	)
 
@@ -158,11 +163,10 @@
 			"bank_account": bank_transaction.bank_account,
 			"credit_in_account_currency": bank_transaction.withdrawal,
 			"debit_in_account_currency": bank_transaction.deposit,
+			"cost_center": get_default_cost_center(company),
 		}
 	)
 
-	company = frappe.get_value("Account", company_account, "company")
-
 	journal_entry_dict = {
 		"voucher_type": entry_type,
 		"company": company,
diff --git a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py
new file mode 100644
index 0000000..5d94a08
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py
@@ -0,0 +1,178 @@
+from typing import Tuple, Union
+
+import frappe
+from frappe.utils import flt
+from rapidfuzz import fuzz, process
+
+
+class AutoMatchParty:
+	"""
+	Matches by Account/IBAN and then by Party Name/Description sequentially.
+	Returns when a result is obtained.
+
+	Result (if present) is of the form: (Party Type, Party,)
+	"""
+
+	def __init__(self, **kwargs) -> None:
+		self.__dict__.update(kwargs)
+
+	def get(self, key):
+		return self.__dict__.get(key, None)
+
+	def match(self) -> Union[Tuple, None]:
+		result = None
+		result = AutoMatchbyAccountIBAN(
+			bank_party_account_number=self.bank_party_account_number,
+			bank_party_iban=self.bank_party_iban,
+			deposit=self.deposit,
+		).match()
+
+		fuzzy_matching_enabled = frappe.db.get_single_value("Accounts Settings", "enable_fuzzy_matching")
+		if not result and fuzzy_matching_enabled:
+			result = AutoMatchbyPartyNameDescription(
+				bank_party_name=self.bank_party_name, description=self.description, deposit=self.deposit
+			).match()
+
+		return result
+
+
+class AutoMatchbyAccountIBAN:
+	def __init__(self, **kwargs) -> None:
+		self.__dict__.update(kwargs)
+
+	def get(self, key):
+		return self.__dict__.get(key, None)
+
+	def match(self):
+		if not (self.bank_party_account_number or self.bank_party_iban):
+			return None
+
+		result = self.match_account_in_party()
+		return result
+
+	def match_account_in_party(self) -> Union[Tuple, None]:
+		"""Check if there is a IBAN/Account No. match in Customer/Supplier/Employee"""
+		result = None
+		parties = get_parties_in_order(self.deposit)
+		or_filters = self.get_or_filters()
+
+		for party in parties:
+			party_result = frappe.db.get_all(
+				"Bank Account", or_filters=or_filters, pluck="party", limit_page_length=1
+			)
+
+			if party == "Employee" and not party_result:
+				# Search in Bank Accounts first for Employee, and then Employee record
+				if "bank_account_no" in or_filters:
+					or_filters["bank_ac_no"] = or_filters.pop("bank_account_no")
+
+				party_result = frappe.db.get_all(
+					party, or_filters=or_filters, pluck="name", limit_page_length=1
+				)
+
+			if party_result:
+				result = (
+					party,
+					party_result[0],
+				)
+				break
+
+		return result
+
+	def get_or_filters(self) -> dict:
+		or_filters = {}
+		if self.bank_party_account_number:
+			or_filters["bank_account_no"] = self.bank_party_account_number
+
+		if self.bank_party_iban:
+			or_filters["iban"] = self.bank_party_iban
+
+		return or_filters
+
+
+class AutoMatchbyPartyNameDescription:
+	def __init__(self, **kwargs) -> None:
+		self.__dict__.update(kwargs)
+
+	def get(self, key):
+		return self.__dict__.get(key, None)
+
+	def match(self) -> Union[Tuple, None]:
+		# fuzzy search by customer/supplier & employee
+		if not (self.bank_party_name or self.description):
+			return None
+
+		result = self.match_party_name_desc_in_party()
+		return result
+
+	def match_party_name_desc_in_party(self) -> Union[Tuple, None]:
+		"""Fuzzy search party name and/or description against parties in the system"""
+		result = None
+		parties = get_parties_in_order(self.deposit)
+
+		for party in parties:
+			filters = {"status": "Active"} if party == "Employee" else {"disabled": 0}
+			names = frappe.get_all(party, filters=filters, pluck=party.lower() + "_name")
+
+			for field in ["bank_party_name", "description"]:
+				if not self.get(field):
+					continue
+
+				result, skip = self.fuzzy_search_and_return_result(party, names, field)
+				if result or skip:
+					break
+
+			if result or skip:
+				# Skip If: It was hard to distinguish between close matches and so match is None
+				# OR if the right match was found
+				break
+
+		return result
+
+	def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]:
+		skip = False
+		result = process.extract(query=self.get(field), choices=names, scorer=fuzz.token_set_ratio)
+		party_name, skip = self.process_fuzzy_result(result)
+
+		if not party_name:
+			return None, skip
+
+		return (
+			party,
+			party_name,
+		), skip
+
+	def process_fuzzy_result(self, result: Union[list, None]):
+		"""
+		If there are multiple valid close matches return None as result may be faulty.
+		Return the result only if one accurate match stands out.
+
+		Returns: Result, Skip (whether or not to discontinue matching)
+		"""
+		PARTY, SCORE, CUTOFF = 0, 1, 80
+
+		if not result or not len(result):
+			return None, False
+
+		first_result = result[0]
+		if len(result) == 1:
+			return (first_result[PARTY] if first_result[SCORE] > CUTOFF else None), True
+
+		second_result = result[1]
+		if first_result[SCORE] > CUTOFF:
+			# If multiple matches with the same score, return None but discontinue matching
+			# Matches were found but were too close to distinguish between
+			if first_result[SCORE] == second_result[SCORE]:
+				return None, True
+
+			return first_result[PARTY], True
+		else:
+			return None, False
+
+
+def get_parties_in_order(deposit: float) -> list:
+	parties = ["Supplier", "Employee", "Customer"]  # most -> least likely to receive
+	if flt(deposit) > 0:
+		parties = ["Customer", "Supplier", "Employee"]  # most -> least likely to pay
+
+	return parties
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
index 768d2f0..b32022e 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
@@ -33,7 +33,11 @@
   "unallocated_amount",
   "party_section",
   "party_type",
-  "party"
+  "party",
+  "column_break_3czf",
+  "bank_party_name",
+  "bank_party_account_number",
+  "bank_party_iban"
  ],
  "fields": [
   {
@@ -63,7 +67,7 @@
    "fieldtype": "Select",
    "in_standard_filter": 1,
    "label": "Status",
-   "options": "\nPending\nSettled\nUnreconciled\nReconciled"
+   "options": "\nPending\nSettled\nUnreconciled\nReconciled\nCancelled"
   },
   {
    "fieldname": "bank_account",
@@ -202,11 +206,30 @@
    "fieldtype": "Data",
    "label": "Transaction Type",
    "length": 50
+  },
+  {
+   "fieldname": "column_break_3czf",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "bank_party_name",
+   "fieldtype": "Data",
+   "label": "Party Name/Account Holder (Bank Statement)"
+  },
+  {
+   "fieldname": "bank_party_iban",
+   "fieldtype": "Data",
+   "label": "Party IBAN (Bank Statement)"
+  },
+  {
+   "fieldname": "bank_party_account_number",
+   "fieldtype": "Data",
+   "label": "Party Account No. (Bank Statement)"
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2022-05-29 18:36:50.475964",
+ "modified": "2023-06-06 13:58:12.821411",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Bank Transaction",
@@ -260,4 +283,4 @@
  "states": [],
  "title_field": "bank_account",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index b441af9..f82337f 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -15,6 +15,9 @@
 		self.clear_linked_payment_entries()
 		self.set_status()
 
+		if frappe.db.get_single_value("Accounts Settings", "enable_party_matching"):
+			self.auto_set_party()
+
 	_saving_flag = False
 
 	# nosemgrep: frappe-semgrep-rules.rules.frappe-modifying-but-not-comitting
@@ -146,6 +149,26 @@
 			payment_entry.payment_document, payment_entry.payment_entry, clearance_date, self
 		)
 
+	def auto_set_party(self):
+		from erpnext.accounts.doctype.bank_transaction.auto_match_party import AutoMatchParty
+
+		if self.party_type and self.party:
+			return
+
+		result = AutoMatchParty(
+			bank_party_account_number=self.bank_party_account_number,
+			bank_party_iban=self.bank_party_iban,
+			bank_party_name=self.bank_party_name,
+			description=self.description,
+			deposit=self.deposit,
+		).match()
+
+		if result:
+			party_type, party = result
+			frappe.db.set_value(
+				"Bank Transaction", self.name, field={"party_type": party_type, "party": party}
+			)
+
 
 @frappe.whitelist()
 def get_doctypes_for_bank_reconciliation():
diff --git a/erpnext/accounts/doctype/bank_transaction/test_auto_match_party.py b/erpnext/accounts/doctype/bank_transaction/test_auto_match_party.py
new file mode 100644
index 0000000..36ef1fc
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_transaction/test_auto_match_party.py
@@ -0,0 +1,151 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import nowdate
+
+from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import create_bank_account
+
+
+class TestAutoMatchParty(FrappeTestCase):
+	@classmethod
+	def setUpClass(cls):
+		create_bank_account()
+		frappe.db.set_single_value("Accounts Settings", "enable_party_matching", 1)
+		frappe.db.set_single_value("Accounts Settings", "enable_fuzzy_matching", 1)
+		return super().setUpClass()
+
+	@classmethod
+	def tearDownClass(cls):
+		frappe.db.set_single_value("Accounts Settings", "enable_party_matching", 0)
+		frappe.db.set_single_value("Accounts Settings", "enable_fuzzy_matching", 0)
+
+	def test_match_by_account_number(self):
+		create_supplier_for_match(account_no="000000003716541159")
+		doc = create_bank_transaction(
+			withdrawal=1200,
+			transaction_id="562213b0ca1bf838dab8f2c6a39bbc3b",
+			account_no="000000003716541159",
+			iban="DE02000000003716541159",
+		)
+
+		self.assertEqual(doc.party_type, "Supplier")
+		self.assertEqual(doc.party, "John Doe & Co.")
+
+	def test_match_by_iban(self):
+		create_supplier_for_match(iban="DE02000000003716541159")
+		doc = create_bank_transaction(
+			withdrawal=1200,
+			transaction_id="c5455a224602afaa51592a9d9250600d",
+			account_no="000000003716541159",
+			iban="DE02000000003716541159",
+		)
+
+		self.assertEqual(doc.party_type, "Supplier")
+		self.assertEqual(doc.party, "John Doe & Co.")
+
+	def test_match_by_party_name(self):
+		create_supplier_for_match(supplier_name="Jackson Ella W.")
+		doc = create_bank_transaction(
+			withdrawal=1200,
+			transaction_id="1f6f661f347ff7b1ea588665f473adb1",
+			party_name="Ella Jackson",
+			iban="DE04000000003716545346",
+		)
+		self.assertEqual(doc.party_type, "Supplier")
+		self.assertEqual(doc.party, "Jackson Ella W.")
+
+	def test_match_by_description(self):
+		create_supplier_for_match(supplier_name="Microsoft")
+		doc = create_bank_transaction(
+			description="Auftraggeber: microsoft payments Buchungstext: msft ..e3006b5hdy. ref. j375979555927627/5536",
+			withdrawal=1200,
+			transaction_id="8df880a2d09c3bed3fea358ca5168c5a",
+			party_name="",
+		)
+		self.assertEqual(doc.party_type, "Supplier")
+		self.assertEqual(doc.party, "Microsoft")
+
+	def test_skip_match_if_multiple_close_results(self):
+		create_supplier_for_match(supplier_name="Adithya Medical & General Stores")
+		create_supplier_for_match(supplier_name="Adithya Medical And General Stores")
+
+		doc = create_bank_transaction(
+			description="Paracetamol Consignment, SINV-0009",
+			withdrawal=24.85,
+			transaction_id="3a1da4ee2dc5a980138d56ef3460cbd9",
+			party_name="Adithya Medical & General",
+		)
+
+		# Mapping is skipped as both Supplier names have the same match score
+		self.assertEqual(doc.party_type, None)
+		self.assertEqual(doc.party, None)
+
+
+def create_supplier_for_match(supplier_name="John Doe & Co.", iban=None, account_no=None):
+	if frappe.db.exists("Supplier", {"supplier_name": supplier_name}):
+		# Update related Bank Account details
+		if not (iban or account_no):
+			return
+
+		frappe.db.set_value(
+			dt="Bank Account",
+			dn={"party": supplier_name},
+			field={"iban": iban, "bank_account_no": account_no},
+		)
+		return
+
+	# Create Supplier and Bank Account for the same
+	supplier = frappe.new_doc("Supplier")
+	supplier.supplier_name = supplier_name
+	supplier.supplier_group = "Services"
+	supplier.supplier_type = "Company"
+	supplier.insert()
+
+	if not frappe.db.exists("Bank", "TestBank"):
+		bank = frappe.new_doc("Bank")
+		bank.bank_name = "TestBank"
+		bank.insert(ignore_if_duplicate=True)
+
+	if not frappe.db.exists("Bank Account", supplier.name + " - " + "TestBank"):
+		bank_account = frappe.new_doc("Bank Account")
+		bank_account.account_name = supplier.name
+		bank_account.bank = "TestBank"
+		bank_account.iban = iban
+		bank_account.bank_account_no = account_no
+		bank_account.party_type = "Supplier"
+		bank_account.party = supplier.name
+		bank_account.insert()
+
+
+def create_bank_transaction(
+	description=None,
+	withdrawal=0,
+	deposit=0,
+	transaction_id=None,
+	party_name=None,
+	account_no=None,
+	iban=None,
+):
+	doc = frappe.new_doc("Bank Transaction")
+	doc.update(
+		{
+			"doctype": "Bank Transaction",
+			"description": description or "1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
+			"date": nowdate(),
+			"withdrawal": withdrawal,
+			"deposit": deposit,
+			"currency": "INR",
+			"bank_account": "Checking Account - Citi Bank",
+			"transaction_id": transaction_id,
+			"bank_party_name": party_name,
+			"bank_party_account_number": account_no,
+			"bank_party_iban": iban,
+		}
+	)
+	doc.insert()
+	doc.submit()
+	doc.reload()
+
+	return doc
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js
index f51b90d..1ef5c83 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js
@@ -37,7 +37,7 @@
 
 	validate_rounding_loss: function(frm) {
 		let allowance = frm.doc.rounding_loss_allowance;
-		if (!(allowance > 0 && allowance < 1)) {
+		if (!(allowance >= 0 && allowance < 1)) {
 			frappe.throw(__("Rounding Loss Allowance should be between 0 and 1"));
 		}
 	},
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.json b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.json
index 2310d12..79428d5 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.json
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.json
@@ -100,15 +100,16 @@
   },
   {
    "default": "0.05",
-   "description": "Only values between 0 and 1 are allowed. \nEx: If allowance is set at 0.07, accounts that have balance of 0.07 in either of the currencies will be considered as zero balance account",
+   "description": "Only values between [0,1) are allowed. Like {0.00, 0.04, 0.09, ...}\nEx: If allowance is set at 0.07, accounts that have balance of 0.07 in either of the currencies will be considered as zero balance account",
    "fieldname": "rounding_loss_allowance",
    "fieldtype": "Float",
-   "label": "Rounding Loss Allowance"
+   "label": "Rounding Loss Allowance",
+   "precision": "9"
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2023-06-12 21:02:09.818208",
+ "modified": "2023-06-20 07:29:06.972434",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Exchange Rate Revaluation",
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
index 5d239c9..598db64 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -22,7 +22,7 @@
 		self.set_total_gain_loss()
 
 	def validate_rounding_loss_allowance(self):
-		if not (self.rounding_loss_allowance > 0 and self.rounding_loss_allowance < 1):
+		if not (self.rounding_loss_allowance >= 0 and self.rounding_loss_allowance < 1):
 			frappe.throw(_("Rounding Loss Allowance should be between 0 and 1"))
 
 	def set_total_gain_loss(self):
@@ -373,6 +373,24 @@
 						"credit": 0,
 					}
 				)
+
+				journal_entry_accounts.append(journal_account)
+
+				journal_entry_accounts.append(
+					{
+						"account": unrealized_exchange_gain_loss_account,
+						"balance": get_balance_on(unrealized_exchange_gain_loss_account),
+						"debit": 0,
+						"credit": 0,
+						"debit_in_account_currency": abs(d.gain_loss) if d.gain_loss < 0 else 0,
+						"credit_in_account_currency": abs(d.gain_loss) if d.gain_loss > 0 else 0,
+						"cost_center": erpnext.get_default_cost_center(self.company),
+						"exchange_rate": 1,
+						"reference_type": "Exchange Rate Revaluation",
+						"reference_name": self.name,
+					}
+				)
+
 			elif d.get("balance_in_base_currency") and not d.get("new_balance_in_base_currency"):
 				# Base currency has balance
 				dr_or_cr = "credit" if d.get("balance_in_base_currency") > 0 else "debit"
@@ -388,22 +406,22 @@
 					}
 				)
 
-			journal_entry_accounts.append(journal_account)
+				journal_entry_accounts.append(journal_account)
 
-		journal_entry_accounts.append(
-			{
-				"account": unrealized_exchange_gain_loss_account,
-				"balance": get_balance_on(unrealized_exchange_gain_loss_account),
-				"debit": abs(self.gain_loss_booked) if self.gain_loss_booked < 0 else 0,
-				"credit": abs(self.gain_loss_booked) if self.gain_loss_booked > 0 else 0,
-				"debit_in_account_currency": abs(self.gain_loss_booked) if self.gain_loss_booked < 0 else 0,
-				"credit_in_account_currency": self.gain_loss_booked if self.gain_loss_booked > 0 else 0,
-				"cost_center": erpnext.get_default_cost_center(self.company),
-				"exchange_rate": 1,
-				"reference_type": "Exchange Rate Revaluation",
-				"reference_name": self.name,
-			}
-		)
+				journal_entry_accounts.append(
+					{
+						"account": unrealized_exchange_gain_loss_account,
+						"balance": get_balance_on(unrealized_exchange_gain_loss_account),
+						"debit": abs(d.gain_loss) if d.gain_loss < 0 else 0,
+						"credit": abs(d.gain_loss) if d.gain_loss > 0 else 0,
+						"debit_in_account_currency": 0,
+						"credit_in_account_currency": 0,
+						"cost_center": erpnext.get_default_cost_center(self.company),
+						"exchange_rate": 1,
+						"reference_type": "Exchange Rate Revaluation",
+						"reference_name": self.name,
+					}
+				)
 
 		journal_entry.set("accounts", journal_entry_accounts)
 		journal_entry.set_total_debit_credit()
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json b/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json
index 2968359..fd2d931 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json
@@ -73,6 +73,7 @@
    "fieldname": "current_exchange_rate",
    "fieldtype": "Float",
    "label": "Current Exchange Rate",
+   "precision": "9",
    "read_only": 1
   },
   {
@@ -92,6 +93,7 @@
    "fieldtype": "Float",
    "in_list_view": 1,
    "label": "New Exchange Rate",
+   "precision": "9",
    "reqd": 1
   },
   {
@@ -147,7 +149,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2022-12-29 19:38:52.915295",
+ "modified": "2023-06-22 12:39:56.446722",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Exchange Rate Revaluation Account",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 6d9e320..a51e38e 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -8,7 +8,7 @@
 frappe.ui.form.on("Journal Entry", {
 	setup: function(frm) {
 		frm.add_fetch("bank_account", "account", "account");
-		frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset Depreciation Schedule'];
+		frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', 'Asset Depreciation Schedule'];
 	},
 
 	refresh: function(frm) {
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 74fd559..83312db 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -326,12 +326,10 @@
 				d.db_update()
 
 	def unlink_asset_reference(self):
-		if self.voucher_type != "Depreciation Entry":
-			return
-
 		for d in self.get("accounts"):
 			if (
-				d.reference_type == "Asset"
+				self.voucher_type == "Depreciation Entry"
+				and d.reference_type == "Asset"
 				and d.reference_name
 				and d.account_type == "Depreciation"
 				and d.debit
@@ -370,6 +368,15 @@
 				else:
 					asset.db_set("value_after_depreciation", asset.value_after_depreciation + d.debit)
 				asset.set_status()
+			elif self.voucher_type == "Journal Entry" and d.reference_type == "Asset" and d.reference_name:
+				journal_entry_for_scrap = frappe.db.get_value(
+					"Asset", d.reference_name, "journal_entry_for_scrap"
+				)
+
+				if journal_entry_for_scrap == self.name:
+					frappe.throw(
+						_("Journal Entry for Asset scrapping cannot be cancelled. Please restore the Asset.")
+					)
 
 	def unlink_inter_company_jv(self):
 		if (
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 9f55ba1..bac84db 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -155,6 +155,7 @@
 		frm.events.hide_unhide_fields(frm);
 		frm.events.set_dynamic_labels(frm);
 		frm.events.show_general_ledger(frm);
+		erpnext.accounts.ledger_preview.show_accounting_ledger_preview(frm);
 	},
 
 	validate_company: (frm) => {
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index 2283677..89fa151 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -85,25 +85,29 @@
 
 		// check for any running reconciliation jobs
 		if (this.frm.doc.receivable_payable_account) {
-			frappe.db.get_single_value("Accounts Settings", "auto_reconcile_payments").then((enabled) => {
- 				if(enabled) {
-					this.frm.call({
-						'method': "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.is_any_doc_running",
-						"args": {
-							for_filter: {
-								company: this.frm.doc.company,
-								party_type: this.frm.doc.party_type,
-								party: this.frm.doc.party,
-								receivable_payable_account: this.frm.doc.receivable_payable_account
+			this.frm.call({
+				doc: this.frm.doc,
+				method: 'is_auto_process_enabled',
+				callback: (r) => {
+					if (r.message) {
+						this.frm.call({
+							'method': "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.is_any_doc_running",
+							"args": {
+								for_filter: {
+									company: this.frm.doc.company,
+									party_type: this.frm.doc.party_type,
+									party: this.frm.doc.party,
+									receivable_payable_account: this.frm.doc.receivable_payable_account
+								}
 							}
-						}
-					}).then(r => {
-						if (r.message) {
-							let doc_link = frappe.utils.get_form_link("Process Payment Reconciliation", r.message, true);
-							let msg = __("Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.", [doc_link]);
-							this.frm.dashboard.add_comment(msg, "yellow");
-						}
-					});
+						}).then(r => {
+							if (r.message) {
+								let doc_link = frappe.utils.get_form_link("Process Payment Reconciliation", r.message, true);
+								let msg = __("Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.", [doc_link]);
+								this.frm.dashboard.add_comment(msg, "yellow");
+							}
+						});
+					}
 				}
 			});
 		}
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 2c8faec..2e4e3b0 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -253,6 +253,10 @@
 		return difference_amount
 
 	@frappe.whitelist()
+	def is_auto_process_enabled(self):
+		return frappe.db.get_single_value("Accounts Settings", "auto_reconcile_payments")
+
+	@frappe.whitelist()
 	def calculate_difference_on_allocation_change(self, payment_entry, invoice, allocated_amount):
 		invoice_exchange_map = self.get_invoice_exchange_map(invoice, payment_entry)
 		invoice[0]["exchange_rate"] = invoice_exchange_map.get(invoice[0].get("invoice_number"))
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index ab7884d..6a558ca 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -54,9 +54,11 @@
 		hide_fields(this.frm.doc);
 		// Show / Hide button
 		this.show_general_ledger();
+		erpnext.accounts.ledger_preview.show_accounting_ledger_preview(this.frm);
 
-		if(doc.update_stock==1 && doc.docstatus==1) {
+		if(doc.update_stock==1) {
 			this.show_stock_ledger();
+			erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
 		}
 
 		if(!doc.is_return && doc.docstatus == 1 && doc.outstanding_amount != 0){
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 8cb2950..68407e0 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -88,8 +88,12 @@
 		}
 
 		this.show_general_ledger();
+		erpnext.accounts.ledger_preview.show_accounting_ledger_preview(this.frm);
 
-		if(doc.update_stock) this.show_stock_ledger();
+		if(doc.update_stock){
+			this.show_stock_ledger();
+			erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
+		}
 
 		if (doc.docstatus == 1 && doc.outstanding_amount!=0
 			&& !(cint(doc.is_return) && doc.return_against)) {
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 7b68dd4..f0d3f72 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -320,6 +320,7 @@
   },
   {
    "default": "0",
+   "depends_on": "eval: !doc.is_debit_note",
    "fieldname": "is_return",
    "fieldtype": "Check",
    "hide_days": 1,
@@ -1960,6 +1961,7 @@
   },
   {
    "default": "0",
+   "depends_on": "eval: !doc.is_return",
    "description": "Issue a debit note with 0 qty against an existing Sales Invoice",
    "fieldname": "is_debit_note",
    "fieldtype": "Check",
@@ -2155,7 +2157,7 @@
    "link_fieldname": "consolidated_invoice"
   }
  ],
- "modified": "2023-06-03 16:22:16.219333",
+ "modified": "2023-06-21 16:02:18.988799",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.json b/erpnext/accounts/doctype/shareholder/shareholder.json
index e94aea9..e80b057 100644
--- a/erpnext/accounts/doctype/shareholder/shareholder.json
+++ b/erpnext/accounts/doctype/shareholder/shareholder.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "naming_series:",
  "creation": "2017-12-25 16:50:53.878430",
  "doctype": "DocType",
@@ -111,11 +112,12 @@
    "read_only": 1
   }
  ],
- "modified": "2019-11-17 23:24:11.395882",
+ "links": [],
+ "modified": "2023-04-10 22:02:20.406087",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Shareholder",
- "name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
@@ -158,6 +160,7 @@
  "search_fields": "folio_no",
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "title",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index d34c213..924c14b 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -87,7 +87,7 @@
 				"project": d.project,
 				"company": d.company,
 				"purchase_order": d.purchase_order,
-				"purchase_receipt": d.purchase_receipt,
+				"purchase_receipt": purchase_receipt,
 				"expense_account": expense_account,
 				"stock_qty": d.stock_qty,
 				"stock_uom": d.stock_uom,
@@ -241,7 +241,7 @@
 		},
 		{
 			"label": _("Purchase Receipt"),
-			"fieldname": "Purchase Receipt",
+			"fieldname": "purchase_receipt",
 			"fieldtype": "Link",
 			"options": "Purchase Receipt",
 			"width": 100,
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 0ee06e8..a5cb324 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -237,11 +237,6 @@
 		if not (frappe.flags.ignore_account_permission or ignore_account_permission):
 			acc.check_permission("read")
 
-		if report_type == "Profit and Loss":
-			# for pl accounts, get balance within a fiscal year
-			cond.append(
-				"posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" % year_start_date
-			)
 		# different filter for group and ledger - improved performance
 		if acc.is_group:
 			cond.append(
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index bfef57e..259568a 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -159,15 +159,15 @@
 			je.flags.ignore_permissions = True
 			je.flags.planned_depr_entry = True
 			je.save()
-			if not je.meta.get_workflow():
-				je.submit()
 
 			d.db_set("journal_entry", je.name)
 
-			idx = cint(asset_depr_schedule_doc.finance_book_id)
-			row = asset.get("finance_books")[idx - 1]
-			row.value_after_depreciation -= d.depreciation_amount
-			row.db_update()
+			if not je.meta.get_workflow():
+				je.submit()
+				idx = cint(asset_depr_schedule_doc.finance_book_id)
+				row = asset.get("finance_books")[idx - 1]
+				row.value_after_depreciation -= d.depreciation_amount
+				row.db_update()
 
 	asset.db_set("depr_entry_posting_status", "Successful")
 
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
index 01fcb11..6d55d77 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
@@ -15,7 +15,6 @@
 	}
 
 	refresh() {
-		erpnext.hide_company();
 		this.show_general_ledger();
 		if ((this.frm.doc.stock_items && this.frm.doc.stock_items.length) || !this.frm.doc.target_is_fixed_asset) {
 			this.show_stock_ledger();
@@ -129,10 +128,6 @@
 		return this.get_target_item_details();
 	}
 
-	target_asset() {
-		return this.get_target_asset_details();
-	}
-
 	item_code(doc, cdt, cdn) {
 		var row = frappe.get_doc(cdt, cdn);
 		if (cdt === "Asset Capitalization Stock Item") {
@@ -247,26 +242,6 @@
 		}
 	}
 
-	get_target_asset_details() {
-		var me = this;
-
-		if (me.frm.doc.target_asset) {
-			return me.frm.call({
-				method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_target_asset_details",
-				child: me.frm.doc,
-				args: {
-					asset: me.frm.doc.target_asset,
-					company: me.frm.doc.company,
-				},
-				callback: function (r) {
-					if (!r.exc) {
-						me.frm.refresh_fields();
-					}
-				}
-			});
-		}
-	}
-
 	get_consumed_stock_item_details(row) {
 		var me = this;
 
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
index 01b35f6..04b0c4e 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
@@ -11,13 +11,14 @@
   "naming_series",
   "entry_type",
   "target_item_code",
+  "target_asset",
   "target_item_name",
   "target_is_fixed_asset",
   "target_has_batch_no",
   "target_has_serial_no",
   "column_break_9",
-  "target_asset",
   "target_asset_name",
+  "target_asset_location",
   "target_warehouse",
   "target_qty",
   "target_stock_uom",
@@ -85,14 +86,13 @@
    "fieldtype": "Column Break"
   },
   {
-   "depends_on": "eval:doc.entry_type=='Capitalization'",
    "fieldname": "target_asset",
    "fieldtype": "Link",
    "in_standard_filter": 1,
    "label": "Target Asset",
-   "mandatory_depends_on": "eval:doc.entry_type=='Capitalization'",
    "no_copy": 1,
-   "options": "Asset"
+   "options": "Asset",
+   "read_only": 1
   },
   {
    "depends_on": "eval:doc.entry_type=='Capitalization'",
@@ -108,11 +108,11 @@
    "fieldtype": "Column Break"
   },
   {
-   "fetch_from": "asset.company",
    "fieldname": "company",
    "fieldtype": "Link",
    "label": "Company",
    "options": "Company",
+   "remember_last_selected_value": 1,
    "reqd": 1
   },
   {
@@ -158,7 +158,7 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval:doc.docstatus == 0 || (doc.stock_items && doc.stock_items.length)",
+   "depends_on": "eval:doc.entry_type=='Capitalization' && (doc.docstatus == 0 || (doc.stock_items && doc.stock_items.length))",
    "fieldname": "section_break_16",
    "fieldtype": "Section Break",
    "label": "Consumed Stock Items"
@@ -189,7 +189,7 @@
    "fieldname": "target_qty",
    "fieldtype": "Float",
    "label": "Target Qty",
-   "read_only_depends_on": "target_is_fixed_asset"
+   "read_only_depends_on": "eval:doc.entry_type=='Capitalization'"
   },
   {
    "fetch_from": "target_item_code.stock_uom",
@@ -227,7 +227,7 @@
    "depends_on": "eval:doc.docstatus == 0 || (doc.asset_items && doc.asset_items.length)",
    "fieldname": "section_break_26",
    "fieldtype": "Section Break",
-   "label": "Consumed Asset Items"
+   "label": "Consumed Assets"
   },
   {
    "fieldname": "asset_items",
@@ -266,7 +266,7 @@
    "options": "Finance Book"
   },
   {
-   "depends_on": "eval:doc.docstatus == 0 || (doc.service_items && doc.service_items.length)",
+   "depends_on": "eval:doc.entry_type=='Capitalization' && (doc.docstatus == 0 || (doc.service_items && doc.service_items.length))",
    "fieldname": "service_expenses_section",
    "fieldtype": "Section Break",
    "label": "Service Expenses"
@@ -329,12 +329,20 @@
    "label": "Target Fixed Asset Account",
    "options": "Account",
    "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.entry_type=='Capitalization'",
+   "fieldname": "target_asset_location",
+   "fieldtype": "Link",
+   "label": "Target Asset Location",
+   "mandatory_depends_on": "eval:doc.entry_type=='Capitalization'",
+   "options": "Location"
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-10-12 15:09:40.771332",
+ "modified": "2023-06-22 14:17:07.995120",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Capitalization",
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index 6841c56..a883bec 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -19,9 +19,6 @@
 	reverse_depreciation_entry_made_after_disposal,
 )
 from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
-from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
-	make_new_active_asset_depr_schedules_and_cancel_current_ones,
-)
 from erpnext.controllers.stock_controller import StockController
 from erpnext.setup.doctype.brand.brand import get_brand_defaults
 from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
@@ -45,7 +42,6 @@
 	"target_has_batch_no",
 	"target_stock_uom",
 	"stock_uom",
-	"target_fixed_asset_account",
 	"fixed_asset_account",
 	"valuation_rate",
 ]
@@ -56,7 +52,6 @@
 		self.validate_posting_time()
 		self.set_missing_values(for_validate=True)
 		self.validate_target_item()
-		self.validate_target_asset()
 		self.validate_consumed_stock_item()
 		self.validate_consumed_asset_item()
 		self.validate_service_item()
@@ -71,11 +66,12 @@
 
 	def before_submit(self):
 		self.validate_source_mandatory()
+		if self.entry_type == "Capitalization":
+			self.create_target_asset()
 
 	def on_submit(self):
 		self.update_stock_ledger()
 		self.make_gl_entries()
-		self.update_target_asset()
 
 	def on_cancel(self):
 		self.ignore_linked_doctypes = (
@@ -86,7 +82,7 @@
 		)
 		self.update_stock_ledger()
 		self.make_gl_entries()
-		self.update_target_asset()
+		self.restore_consumed_asset_items()
 
 	def set_title(self):
 		self.title = self.target_asset_name or self.target_item_name or self.target_item_code
@@ -97,15 +93,6 @@
 			if self.meta.has_field(k) and (not self.get(k) or k in force_fields):
 				self.set(k, v)
 
-		# Remove asset if item not a fixed asset
-		if not self.target_is_fixed_asset:
-			self.target_asset = None
-
-		target_asset_details = get_target_asset_details(self.target_asset, self.company)
-		for k, v in target_asset_details.items():
-			if self.meta.has_field(k) and (not self.get(k) or k in force_fields):
-				self.set(k, v)
-
 		for d in self.stock_items:
 			args = self.as_dict()
 			args.update(d.as_dict())
@@ -157,9 +144,6 @@
 
 		if not target_item.is_stock_item:
 			self.target_warehouse = None
-		if not target_item.is_fixed_asset:
-			self.target_asset = None
-			self.target_fixed_asset_account = None
 		if not target_item.has_batch_no:
 			self.target_batch_no = None
 		if not target_item.has_serial_no:
@@ -170,17 +154,6 @@
 
 		self.validate_item(target_item)
 
-	def validate_target_asset(self):
-		if self.target_asset:
-			target_asset = self.get_asset_for_validation(self.target_asset)
-
-			if target_asset.item_code != self.target_item_code:
-				frappe.throw(
-					_("Asset {0} does not belong to Item {1}").format(self.target_asset, self.target_item_code)
-				)
-
-			self.validate_asset(target_asset)
-
 	def validate_consumed_stock_item(self):
 		for d in self.stock_items:
 			if d.item_code:
@@ -386,7 +359,11 @@
 			gl_entries, target_account, target_against, precision
 		)
 
+		if not self.stock_items and not self.service_items and self.are_all_asset_items_non_depreciable:
+			return []
+
 		self.get_gl_entries_for_target_item(gl_entries, target_against, precision)
+
 		return gl_entries
 
 	def get_target_account(self):
@@ -429,11 +406,14 @@
 	def get_gl_entries_for_consumed_asset_items(
 		self, gl_entries, target_account, target_against, precision
 	):
+		self.are_all_asset_items_non_depreciable = True
+
 		# Consumed Assets
 		for item in self.asset_items:
-			asset = self.get_asset(item)
+			asset = frappe.get_doc("Asset", item.asset)
 
 			if asset.calculate_depreciation:
+				self.are_all_asset_items_non_depreciable = False
 				notes = _(
 					"This schedule was created when Asset {0} was consumed through Asset Capitalization {1}."
 				).format(
@@ -519,40 +499,46 @@
 					)
 				)
 
-	def update_target_asset(self):
+	def create_target_asset(self):
 		total_target_asset_value = flt(self.total_value, self.precision("total_value"))
-		if self.docstatus == 1 and self.entry_type == "Capitalization":
-			asset_doc = frappe.get_doc("Asset", self.target_asset)
-			asset_doc.purchase_date = self.posting_date
-			asset_doc.gross_purchase_amount = total_target_asset_value
-			asset_doc.purchase_receipt_amount = total_target_asset_value
-			notes = _(
-				"This schedule was created when target Asset {0} was updated through Asset Capitalization {1}."
-			).format(
-				get_link_to_form(asset_doc.doctype, asset_doc.name), get_link_to_form(self.doctype, self.name)
-			)
-			make_new_active_asset_depr_schedules_and_cancel_current_ones(asset_doc, notes)
-			asset_doc.flags.ignore_validate_update_after_submit = True
-			asset_doc.save()
-		elif self.docstatus == 2:
-			for item in self.asset_items:
-				asset = self.get_asset(item)
-				asset.db_set("disposal_date", None)
-				self.set_consumed_asset_status(asset)
+		asset_doc = frappe.new_doc("Asset")
+		asset_doc.company = self.company
+		asset_doc.item_code = self.target_item_code
+		asset_doc.is_existing_asset = 1
+		asset_doc.location = self.target_asset_location
+		asset_doc.available_for_use_date = self.posting_date
+		asset_doc.purchase_date = self.posting_date
+		asset_doc.gross_purchase_amount = total_target_asset_value
+		asset_doc.purchase_receipt_amount = total_target_asset_value
+		asset_doc.flags.ignore_validate = True
+		asset_doc.insert()
 
-				if asset.calculate_depreciation:
-					reverse_depreciation_entry_made_after_disposal(asset, self.posting_date)
-					notes = _(
-						"This schedule was created when Asset {0} was restored on Asset Capitalization {1}'s cancellation."
-					).format(
-						get_link_to_form(asset.doctype, asset.name), get_link_to_form(self.doctype, self.name)
-					)
-					reset_depreciation_schedule(asset, self.posting_date, notes)
+		self.target_asset = asset_doc.name
 
-	def get_asset(self, item):
-		asset = frappe.get_doc("Asset", item.asset)
-		self.check_finance_books(item, asset)
-		return asset
+		self.target_fixed_asset_account = get_asset_category_account(
+			"fixed_asset_account", item=self.target_item_code, company=asset_doc.company
+		)
+
+		frappe.msgprint(
+			_(
+				"Asset {0} has been created. Please set the depreciation details if any and submit it."
+			).format(get_link_to_form("Asset", asset_doc.name))
+		)
+
+	def restore_consumed_asset_items(self):
+		for item in self.asset_items:
+			asset = frappe.get_doc("Asset", item.asset)
+			asset.db_set("disposal_date", None)
+			self.set_consumed_asset_status(asset)
+
+			if asset.calculate_depreciation:
+				reverse_depreciation_entry_made_after_disposal(asset, self.posting_date)
+				notes = _(
+					"This schedule was created when Asset {0} was restored on Asset Capitalization {1}'s cancellation."
+				).format(
+					get_link_to_form(asset.doctype, asset.name), get_link_to_form(self.doctype, self.name)
+				)
+				reset_depreciation_schedule(asset, self.posting_date, notes)
 
 	def set_consumed_asset_status(self, asset):
 		if self.docstatus == 1:
@@ -603,33 +589,6 @@
 
 
 @frappe.whitelist()
-def get_target_asset_details(asset=None, company=None):
-	out = frappe._dict()
-
-	# Get Asset Details
-	asset_details = frappe._dict()
-	if asset:
-		asset_details = frappe.db.get_value("Asset", asset, ["asset_name", "item_code"], as_dict=1)
-		if not asset_details:
-			frappe.throw(_("Asset {0} does not exist").format(asset))
-
-		# Re-set item code from Asset
-		out.target_item_code = asset_details.item_code
-
-	# Set Asset Details
-	out.asset_name = asset_details.asset_name
-
-	if asset_details.item_code:
-		out.target_fixed_asset_account = get_asset_category_account(
-			"fixed_asset_account", item=asset_details.item_code, company=company
-		)
-	else:
-		out.target_fixed_asset_account = None
-
-	return out
-
-
-@frappe.whitelist()
 def get_consumed_stock_item_details(args):
 	if isinstance(args, str):
 		args = json.loads(args)
diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
index 5345d0e..6e0a685 100644
--- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
@@ -47,13 +47,6 @@
 
 		total_amount = 103000
 
-		# Create assets
-		target_asset = create_asset(
-			asset_name="Asset Capitalization Target Asset",
-			submit=1,
-			warehouse="Stores - TCP1",
-			company=company,
-		)
 		consumed_asset = create_asset(
 			asset_name="Asset Capitalization Consumable Asset",
 			asset_value=consumed_asset_value,
@@ -65,7 +58,8 @@
 		# Create and submit Asset Captitalization
 		asset_capitalization = create_asset_capitalization(
 			entry_type="Capitalization",
-			target_asset=target_asset.name,
+			target_item_code="Macbook Pro",
+			target_asset_location="Test Location",
 			stock_qty=stock_qty,
 			stock_rate=stock_rate,
 			consumed_asset=consumed_asset.name,
@@ -94,7 +88,7 @@
 		self.assertEqual(asset_capitalization.target_incoming_rate, total_amount)
 
 		# Test Target Asset values
-		target_asset.reload()
+		target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
 		self.assertEqual(target_asset.gross_purchase_amount, total_amount)
 		self.assertEqual(target_asset.purchase_receipt_amount, total_amount)
 
@@ -142,13 +136,6 @@
 
 		total_amount = 103000
 
-		# Create assets
-		target_asset = create_asset(
-			asset_name="Asset Capitalization Target Asset",
-			submit=1,
-			warehouse="Stores - _TC",
-			company=company,
-		)
 		consumed_asset = create_asset(
 			asset_name="Asset Capitalization Consumable Asset",
 			asset_value=consumed_asset_value,
@@ -160,7 +147,8 @@
 		# Create and submit Asset Captitalization
 		asset_capitalization = create_asset_capitalization(
 			entry_type="Capitalization",
-			target_asset=target_asset.name,
+			target_item_code="Macbook Pro",
+			target_asset_location="Test Location",
 			stock_qty=stock_qty,
 			stock_rate=stock_rate,
 			consumed_asset=consumed_asset.name,
@@ -189,7 +177,7 @@
 		self.assertEqual(asset_capitalization.target_incoming_rate, total_amount)
 
 		# Test Target Asset values
-		target_asset.reload()
+		target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
 		self.assertEqual(target_asset.gross_purchase_amount, total_amount)
 		self.assertEqual(target_asset.purchase_receipt_amount, total_amount)
 
@@ -364,6 +352,7 @@
 			"posting_time": args.posting_time or now.strftime("%H:%M:%S.%f"),
 			"target_item_code": target_item_code,
 			"target_asset": target_asset.name,
+			"target_asset_location": "Test Location",
 			"target_warehouse": target_warehouse,
 			"target_qty": flt(args.target_qty) or 1,
 			"target_batch_no": args.target_batch_no,
diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
index 8303141..641d35f 100644
--- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
@@ -42,7 +42,6 @@
 				maintenance_log.db_set("maintenance_status", "Cancelled")
 
 
-@frappe.whitelist()
 def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date):
 	team_member = frappe.db.get_value("User", assign_to_member, "email")
 	args = {
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
index 4f7b836..b788a32 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
@@ -20,56 +20,6 @@
 			default: 'In Location'
 		},
 		{
-			"fieldname":"filter_based_on",
-			"label": __("Period Based On"),
-			"fieldtype": "Select",
-			"options": ["Fiscal Year", "Date Range"],
-			"default": "Fiscal Year",
-			"reqd": 1
-		},
-		{
-			"fieldname":"from_date",
-			"label": __("Start Date"),
-			"fieldtype": "Date",
-			"default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12),
-			"depends_on": "eval: doc.filter_based_on == 'Date Range'",
-			"reqd": 1
-		},
-		{
-			"fieldname":"to_date",
-			"label": __("End Date"),
-			"fieldtype": "Date",
-			"default": frappe.datetime.nowdate(),
-			"depends_on": "eval: doc.filter_based_on == 'Date Range'",
-			"reqd": 1
-		},
-		{
-			"fieldname":"from_fiscal_year",
-			"label": __("Start Year"),
-			"fieldtype": "Link",
-			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
-			"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
-			"reqd": 1
-		},
-		{
-			"fieldname":"to_fiscal_year",
-			"label": __("End Year"),
-			"fieldtype": "Link",
-			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
-			"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
-			"reqd": 1
-		},
-		{
-			"fieldname":"date_based_on",
-			"label": __("Date Based On"),
-			"fieldtype": "Select",
-			"options": ["Purchase Date", "Available For Use Date"],
-			"default": "Purchase Date",
-			"reqd": 1
-		},
-		{
 			fieldname:"asset_category",
 			label: __("Asset Category"),
 			fieldtype: "Link",
@@ -90,21 +40,66 @@
 			reqd: 1
 		},
 		{
+			fieldname:"only_existing_assets",
+			label: __("Only existing assets"),
+			fieldtype: "Check"
+		},
+		{
 			fieldname:"finance_book",
 			label: __("Finance Book"),
 			fieldtype: "Link",
 			options: "Finance Book",
-			depends_on: "eval: doc.filter_by_finance_book == 1",
 		},
 		{
-			fieldname:"filter_by_finance_book",
-			label: __("Filter by Finance Book"),
-			fieldtype: "Check"
+			"fieldname": "include_default_book_assets",
+			"label": __("Include Default Book Assets"),
+			"fieldtype": "Check",
+			"default": 1
 		},
 		{
-			fieldname:"only_existing_assets",
-			label: __("Only existing assets"),
-			fieldtype: "Check"
+			"fieldname":"filter_based_on",
+			"label": __("Period Based On"),
+			"fieldtype": "Select",
+			"options": ["--Select a period--", "Fiscal Year", "Date Range"],
+			"default": "--Select a period--",
+		},
+		{
+			"fieldname":"from_date",
+			"label": __("Start Date"),
+			"fieldtype": "Date",
+			"default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12),
+			"depends_on": "eval: doc.filter_based_on == 'Date Range'",
+		},
+		{
+			"fieldname":"to_date",
+			"label": __("End Date"),
+			"fieldtype": "Date",
+			"default": frappe.datetime.nowdate(),
+			"depends_on": "eval: doc.filter_based_on == 'Date Range'",
+		},
+		{
+			"fieldname":"from_fiscal_year",
+			"label": __("Start Year"),
+			"fieldtype": "Link",
+			"options": "Fiscal Year",
+			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
+		},
+		{
+			"fieldname":"to_fiscal_year",
+			"label": __("End Year"),
+			"fieldtype": "Link",
+			"options": "Fiscal Year",
+			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
+		},
+		{
+			"fieldname":"date_based_on",
+			"label": __("Date Based On"),
+			"fieldtype": "Select",
+			"options": ["Purchase Date", "Available For Use Date"],
+			"default": "Purchase Date",
+			"depends_on": "eval: doc.filter_based_on == 'Date Range' || doc.filter_based_on == 'Fiscal Year'",
 		},
 	]
 };
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
index 984b3fd..6911f94 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -2,9 +2,11 @@
 # For license information, please see license.txt
 
 
+from itertools import chain
+
 import frappe
 from frappe import _
-from frappe.query_builder.functions import Sum
+from frappe.query_builder.functions import IfNull, Sum
 from frappe.utils import cstr, flt, formatdate, getdate
 
 from erpnext.accounts.report.financial_statements import (
@@ -13,7 +15,6 @@
 	validate_fiscal_year,
 )
 from erpnext.assets.doctype.asset.asset import get_asset_value_after_depreciation
-from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts
 
 
 def execute(filters=None):
@@ -64,11 +65,9 @@
 
 
 def get_data(filters):
-
 	data = []
 
 	conditions = get_conditions(filters)
-	depreciation_amount_map = get_finance_book_value_map(filters)
 	pr_supplier_map = get_purchase_receipt_supplier_map()
 	pi_supplier_map = get_purchase_invoice_supplier_map()
 
@@ -102,20 +101,31 @@
 		]
 		assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
 
-	assets_linked_to_fb = None
+	assets_linked_to_fb = get_assets_linked_to_fb(filters)
 
-	if filters.filter_by_finance_book:
-		assets_linked_to_fb = frappe.db.get_all(
-			doctype="Asset Finance Book",
-			filters={"finance_book": filters.finance_book or ("is", "not set")},
-			pluck="parent",
-		)
+	company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
+
+	if filters.include_default_book_assets and company_fb:
+		finance_book = company_fb
+	elif filters.finance_book:
+		finance_book = filters.finance_book
+	else:
+		finance_book = None
+
+	depreciation_amount_map = get_asset_depreciation_amount_map(filters, finance_book)
 
 	for asset in assets_record:
-		if assets_linked_to_fb and asset.asset_id not in assets_linked_to_fb:
+		if (
+			assets_linked_to_fb
+			and asset.calculate_depreciation
+			and asset.asset_id not in assets_linked_to_fb
+		):
 			continue
 
-		asset_value = get_asset_value_after_depreciation(asset.asset_id, filters.finance_book)
+		asset_value = get_asset_value_after_depreciation(
+			asset.asset_id, finance_book
+		) or get_asset_value_after_depreciation(asset.asset_id)
+
 		row = {
 			"asset_id": asset.asset_id,
 			"asset_name": asset.asset_name,
@@ -126,7 +136,7 @@
 			or pi_supplier_map.get(asset.purchase_invoice),
 			"gross_purchase_amount": asset.gross_purchase_amount,
 			"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
-			"depreciated_amount": get_depreciation_amount_of_asset(asset, depreciation_amount_map, filters),
+			"depreciated_amount": get_depreciation_amount_of_asset(asset, depreciation_amount_map),
 			"available_for_use_date": asset.available_for_use_date,
 			"location": asset.location,
 			"asset_category": asset.asset_category,
@@ -140,14 +150,23 @@
 
 def prepare_chart_data(data, filters):
 	labels_values_map = {}
-	date_field = frappe.scrub(filters.date_based_on)
+	if filters.filter_based_on not in ("Date Range", "Fiscal Year"):
+		filters_filter_based_on = "Date Range"
+		date_field = "purchase_date"
+		filters_from_date = min(data, key=lambda a: a.get(date_field)).get(date_field)
+		filters_to_date = max(data, key=lambda a: a.get(date_field)).get(date_field)
+	else:
+		filters_filter_based_on = filters.filter_based_on
+		date_field = frappe.scrub(filters.date_based_on)
+		filters_from_date = filters.from_date
+		filters_to_date = filters.to_date
 
 	period_list = get_period_list(
 		filters.from_fiscal_year,
 		filters.to_fiscal_year,
-		filters.from_date,
-		filters.to_date,
-		filters.filter_based_on,
+		filters_from_date,
+		filters_to_date,
+		filters_filter_based_on,
 		"Monthly",
 		company=filters.company,
 		ignore_fiscal_year=True,
@@ -184,59 +203,76 @@
 	}
 
 
-def get_depreciation_amount_of_asset(asset, depreciation_amount_map, filters):
-	if asset.calculate_depreciation:
-		depr_amount = depreciation_amount_map.get(asset.asset_id) or 0.0
-	else:
-		depr_amount = get_manual_depreciation_amount_of_asset(asset, filters)
+def get_assets_linked_to_fb(filters):
+	afb = frappe.qb.DocType("Asset Finance Book")
 
-	return flt(depr_amount, 2)
-
-
-def get_finance_book_value_map(filters):
-	date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
-
-	return frappe._dict(
-		frappe.db.sql(
-			""" Select
-		ads.asset, SUM(depreciation_amount)
-		FROM `tabAsset Depreciation Schedule` ads, `tabDepreciation Schedule` ds
-		WHERE
-			ds.parent = ads.name
-			AND ifnull(ads.finance_book, '')=%s
-			AND ads.docstatus=1
-			AND ds.parentfield='depreciation_schedule'
-			AND ds.schedule_date<=%s
-			AND ds.journal_entry IS NOT NULL
-		GROUP BY ads.asset""",
-			(cstr(filters.finance_book or ""), date),
-		)
+	query = frappe.qb.from_(afb).select(
+		afb.parent,
 	)
 
+	if filters.include_default_book_assets:
+		company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
 
-def get_manual_depreciation_amount_of_asset(asset, filters):
+		if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb):
+			frappe.throw(_("To use a different finance book, please uncheck 'Include Default Book Assets'"))
+
+		query = query.where(
+			(afb.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""]))
+			| (afb.finance_book.isnull())
+		)
+	else:
+		query = query.where(
+			(afb.finance_book.isin([cstr(filters.finance_book), ""])) | (afb.finance_book.isnull())
+		)
+
+	assets_linked_to_fb = list(chain(*query.run(as_list=1)))
+
+	return assets_linked_to_fb
+
+
+def get_depreciation_amount_of_asset(asset, depreciation_amount_map):
+	return depreciation_amount_map.get(asset.asset_id) or 0.0
+
+
+def get_asset_depreciation_amount_map(filters, finance_book):
 	date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
 
-	(_, _, depreciation_expense_account) = get_depreciation_accounts(asset)
-
+	asset = frappe.qb.DocType("Asset")
 	gle = frappe.qb.DocType("GL Entry")
+	aca = frappe.qb.DocType("Asset Category Account")
+	company = frappe.qb.DocType("Company")
 
-	result = (
+	query = (
 		frappe.qb.from_(gle)
-		.select(Sum(gle.debit))
-		.where(gle.against_voucher == asset.asset_id)
-		.where(gle.account == depreciation_expense_account)
+		.join(asset)
+		.on(gle.against_voucher == asset.name)
+		.join(aca)
+		.on((aca.parent == asset.asset_category) & (aca.company_name == asset.company))
+		.join(company)
+		.on(company.name == asset.company)
+		.select(asset.name.as_("asset"), Sum(gle.debit).as_("depreciation_amount"))
+		.where(
+			gle.account == IfNull(aca.depreciation_expense_account, company.depreciation_expense_account)
+		)
 		.where(gle.debit != 0)
 		.where(gle.is_cancelled == 0)
-		.where(gle.posting_date <= date)
-	).run()
+		.where(asset.docstatus == 1)
+		.groupby(asset.name)
+	)
 
-	if result and result[0] and result[0][0]:
-		depr_amount = result[0][0]
+	if finance_book:
+		query = query.where(
+			(gle.finance_book.isin([cstr(finance_book), ""])) | (gle.finance_book.isnull())
+		)
 	else:
-		depr_amount = 0
+		query = query.where((gle.finance_book.isin([""])) | (gle.finance_book.isnull()))
 
-	return depr_amount
+	if filters.filter_based_on in ("Date Range", "Fiscal Year"):
+		query = query.where(gle.posting_date <= date)
+
+	asset_depr_amount_map = query.run()
+
+	return dict(asset_depr_amount_map)
 
 
 def get_purchase_receipt_supplier_map():
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 1bf7f58..f009789 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -457,7 +457,7 @@
    "link_fieldname": "party"
   }
  ],
- "modified": "2023-02-18 11:05:50.592270",
+ "modified": "2023-05-09 15:34:13.408932",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier",
diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
index a8b76db..1967df2 100644
--- a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
+++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
@@ -99,7 +99,6 @@
 	return mod
 
 
-@frappe.whitelist()
 def make_supplier_scorecard(source_name, target_doc=None):
 	def update_criteria_fields(obj, target, source_parent):
 		target.max_score, target.formula = frappe.db.get_value(
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 818c789..9546680 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -320,7 +320,9 @@
 	return data[0]
 
 
-def make_return_doc(doctype: str, source_name: str, target_doc=None):
+def make_return_doc(
+	doctype: str, source_name: str, target_doc=None, return_against_rejected_qty=False
+):
 	from frappe.model.mapper import get_mapped_doc
 
 	company = frappe.db.get_value("Delivery Note", source_name, "company")
@@ -471,7 +473,7 @@
 
 			target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get("qty") or 0))
 
-			if hasattr(target_doc, "stock_qty"):
+			if hasattr(target_doc, "stock_qty") and not return_against_rejected_qty:
 				target_doc.stock_qty = -1 * flt(
 					source_doc.stock_qty - (returned_qty_map.get("stock_qty") or 0)
 				)
@@ -490,6 +492,13 @@
 				target_doc.rejected_warehouse = source_doc.rejected_warehouse
 				target_doc.purchase_receipt_item = source_doc.name
 
+			if doctype == "Purchase Receipt" and return_against_rejected_qty:
+				target_doc.qty = -1 * flt(source_doc.rejected_qty - (returned_qty_map.get("qty") or 0))
+				target_doc.rejected_qty = 0.0
+				target_doc.rejected_warehouse = ""
+				target_doc.warehouse = source_doc.rejected_warehouse
+				target_doc.received_qty = target_doc.qty
+
 		elif doctype == "Purchase Invoice":
 			returned_qty_map = get_returned_qty_map_for_row(
 				source_parent.name, source_parent.supplier, source_doc.name, doctype
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index cdbf6c7..5137e03 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -845,6 +845,149 @@
 		gl_entries.append(self.get_gl_dict(gl_entry, item=item))
 
 
+@frappe.whitelist()
+def show_accounting_ledger_preview(company, doctype, docname):
+	filters = {"company": company, "include_dimensions": 1}
+	doc = frappe.get_doc(doctype, docname)
+
+	gl_columns, gl_data = get_accounting_ledger_preview(doc, filters)
+
+	frappe.db.rollback()
+
+	return {"gl_columns": gl_columns, "gl_data": gl_data}
+
+
+@frappe.whitelist()
+def show_stock_ledger_preview(company, doctype, docname):
+	filters = {"company": company}
+	doc = frappe.get_doc(doctype, docname)
+
+	sl_columns, sl_data = get_stock_ledger_preview(doc, filters)
+
+	frappe.db.rollback()
+
+	return {
+		"sl_columns": sl_columns,
+		"sl_data": sl_data,
+	}
+
+
+def get_accounting_ledger_preview(doc, filters):
+	from erpnext.accounts.report.general_ledger.general_ledger import get_columns as get_gl_columns
+
+	gl_columns, gl_data = [], []
+	fields = [
+		"posting_date",
+		"account",
+		"debit",
+		"credit",
+		"against",
+		"party",
+		"party_type",
+		"cost_center",
+		"against_voucher_type",
+		"against_voucher",
+	]
+
+	doc.docstatus = 1
+
+	if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"):
+		doc.update_stock_ledger()
+
+	doc.make_gl_entries()
+	columns = get_gl_columns(filters)
+	gl_entries = get_gl_entries_for_preview(doc.doctype, doc.name, fields)
+
+	gl_columns = get_columns(columns, fields)
+	gl_data = get_data(fields, gl_entries)
+
+	return gl_columns, gl_data
+
+
+def get_stock_ledger_preview(doc, filters):
+	from erpnext.stock.report.stock_ledger.stock_ledger import get_columns as get_sl_columns
+
+	sl_columns, sl_data = [], []
+	fields = [
+		"item_code",
+		"stock_uom",
+		"actual_qty",
+		"qty_after_transaction",
+		"warehouse",
+		"incoming_rate",
+		"valuation_rate",
+		"stock_value",
+		"stock_value_difference",
+	]
+	columns_fields = [
+		"item_code",
+		"stock_uom",
+		"in_qty",
+		"out_qty",
+		"qty_after_transaction",
+		"warehouse",
+		"incoming_rate",
+		"in_out_rate",
+		"stock_value",
+		"stock_value_difference",
+	]
+
+	if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"):
+		doc.docstatus = 1
+		doc.update_stock_ledger()
+		columns = get_sl_columns(filters)
+		sl_entries = get_sl_entries_for_preview(doc.doctype, doc.name, fields)
+
+		sl_columns = get_columns(columns, columns_fields)
+		sl_data = get_data(columns_fields, sl_entries)
+
+	return sl_columns, sl_data
+
+
+def get_sl_entries_for_preview(doctype, docname, fields):
+	sl_entries = frappe.get_all(
+		"Stock Ledger Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields
+	)
+
+	for entry in sl_entries:
+		if entry.actual_qty > 0:
+			entry["in_qty"] = entry.actual_qty
+			entry["out_qty"] = 0
+		else:
+			entry["out_qty"] = abs(entry.actual_qty)
+			entry["in_qty"] = 0
+
+		entry["in_out_rate"] = entry["valuation_rate"]
+
+	return sl_entries
+
+
+def get_gl_entries_for_preview(doctype, docname, fields):
+	return frappe.get_all(
+		"GL Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields
+	)
+
+
+def get_columns(raw_columns, fields):
+	return [
+		{"name": d.get("label"), "editable": False, "width": 110}
+		for d in raw_columns
+		if not d.get("hidden") and d.get("fieldname") in fields
+	]
+
+
+def get_data(raw_columns, raw_data):
+	datatable_data = []
+	for row in raw_data:
+		data_row = []
+		for column in raw_columns:
+			data_row.append(row.get(column) or "")
+
+		datatable_data.append(data_row)
+
+	return datatable_data
+
+
 def repost_required_for_queue(doc: StockController) -> bool:
 	"""check if stock document contains repeated item-warehouse with queue based valuation.
 
diff --git a/erpnext/e_commerce/variant_selector/utils.py b/erpnext/e_commerce/variant_selector/utils.py
index 1a3e737..4466c45 100644
--- a/erpnext/e_commerce/variant_selector/utils.py
+++ b/erpnext/e_commerce/variant_selector/utils.py
@@ -162,6 +162,7 @@
 		product_info = get_item_variant_price_dict(exact_match[0], cart_settings)
 
 		if product_info:
+			product_info["is_stock_item"] = frappe.get_cached_value("Item", exact_match[0], "is_stock_item")
 			product_info["allow_items_not_in_stock"] = cint(cart_settings.allow_items_not_in_stock)
 	else:
 		product_info = None
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index e57a30a..61d2ace 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -161,7 +161,6 @@
 		frappe.throw(frappe.get_traceback())
 
 
-@frappe.whitelist()
 def sync_transactions(bank, bank_account):
 	"""Sync transactions based on the last integration date as the start date, after sync is completed
 	add the transaction date of the oldest transaction as the last integration date."""
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 45a59cf..4898691 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -99,7 +99,7 @@
 					}, __('Create'));
 				}
 
-				if (frm.doc.mr_items && !in_list(['Material Requested', 'Closed'], frm.doc.status)) {
+				if (frm.doc.mr_items && frm.doc.mr_items.length && !in_list(['Material Requested', 'Closed'], frm.doc.status)) {
 					frm.add_custom_button(__("Material Request"), ()=> {
 						frm.trigger("make_material_request");
 					}, __('Create'));
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 0800bdd..6dc1ff6 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -515,6 +515,9 @@
 		self.show_list_created_message("Work Order", wo_list)
 		self.show_list_created_message("Purchase Order", po_list)
 
+		if not wo_list:
+			frappe.msgprint(_("No Work Orders were created"))
+
 	def make_work_order_for_finished_goods(self, wo_list, default_warehouses):
 		items_data = self.get_production_items()
 
@@ -618,6 +621,9 @@
 	def create_work_order(self, item):
 		from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError
 
+		if item.get("qty") <= 0:
+			return
+
 		wo = frappe.new_doc("Work Order")
 		wo.update(item)
 		wo.planned_start_date = item.get("planned_start_date") or item.get("schedule_date")
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 75b43ec..fcfba7f 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -76,6 +76,13 @@
 			"Work Order", fields=["name"], filters={"production_plan": pln.name}, as_list=1
 		)
 
+		pln.make_work_order()
+		nwork_orders = frappe.get_all(
+			"Work Order", fields=["name"], filters={"production_plan": pln.name}, as_list=1
+		)
+
+		self.assertTrue(len(work_orders), len(nwork_orders))
+
 		self.assertTrue(len(work_orders), len(pln.po_items))
 
 		for name in material_requests:
diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js
index d346357..720423b 100644
--- a/erpnext/public/js/controllers/stock_controller.js
+++ b/erpnext/public/js/controllers/stock_controller.js
@@ -66,7 +66,7 @@
 	}
 
 	show_general_ledger() {
-		var me = this;
+		let me = this;
 		if(this.frm.doc.docstatus > 0) {
 			cur_frm.add_custom_button(__('Accounting Ledger'), function() {
 				frappe.route_options = {
diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js
index cc020fc..4e028e4 100644
--- a/erpnext/public/js/erpnext.bundle.js
+++ b/erpnext/public/js/erpnext.bundle.js
@@ -17,6 +17,7 @@
 import "./utils/supplier_quick_entry";
 import "./call_popup/call_popup";
 import "./utils/dimension_tree_filter";
+import "./utils/ledger_preview.js"
 import "./utils/barcode_scanner";
 import "./telephony";
 import "./templates/call_link.html";
diff --git a/erpnext/public/js/utils/ledger_preview.js b/erpnext/public/js/utils/ledger_preview.js
new file mode 100644
index 0000000..85d4a7d
--- /dev/null
+++ b/erpnext/public/js/utils/ledger_preview.js
@@ -0,0 +1,78 @@
+frappe.provide('erpnext.accounts');
+
+erpnext.accounts.ledger_preview = {
+	show_accounting_ledger_preview(frm) {
+		let me = this;
+		if(!frm.is_new() && frm.doc.docstatus == 0) {
+			frm.add_custom_button(__('Accounting Ledger'), function() {
+				frappe.call({
+					"type": "GET",
+					"method": "erpnext.controllers.stock_controller.show_accounting_ledger_preview",
+					"args": {
+						"company": frm.doc.company,
+						"doctype": frm.doc.doctype,
+						"docname": frm.doc.name
+					},
+					"callback": function(response) {
+						me.make_dialog("Accounting Ledger Preview", "accounting_ledger_preview_html", response.message.gl_columns, response.message.gl_data);
+					}
+				})
+			}, __("Preview"));
+		}
+	},
+
+	show_stock_ledger_preview(frm) {
+		let me = this
+		if(!frm.is_new() && frm.doc.docstatus == 0) {
+			frm.add_custom_button(__('Stock Ledger'), function() {
+				frappe.call({
+					"type": "GET",
+					"method": "erpnext.controllers.stock_controller.show_stock_ledger_preview",
+					"args": {
+						"company": frm.doc.company,
+						"doctype": frm.doc.doctype,
+						"docname": frm.doc.name
+					},
+					"callback": function(response) {
+						me.make_dialog("Stock Ledger Preview", "stock_ledger_preview_html", response.message.sl_columns, response.message.sl_data);
+					}
+				})
+			}, __("Preview"));
+		}
+	},
+
+	make_dialog(label, fieldname, columns, data) {
+		let me = this;
+		let dialog = new frappe.ui.Dialog({
+			"size": "extra-large",
+			"title": __(label),
+			"fields": [
+				{
+					"fieldtype": "HTML",
+					"fieldname": fieldname,
+				},
+			]
+		});
+
+		setTimeout(function() {
+			me.get_datatable(columns, data, dialog.get_field(fieldname).wrapper);
+		}, 200);
+
+		dialog.show();
+	},
+
+	get_datatable(columns, data, wrapper) {
+		const datatable_options = {
+			columns: columns,
+			data: data,
+			dynamicRowHeight: true,
+			checkboxColumn: false,
+			inlineFilters: true,
+		};
+
+		new frappe.DataTable(
+			wrapper,
+			datatable_options
+		);
+	}
+}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index c133cd3..72a1594 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -568,7 +568,7 @@
    "link_fieldname": "party"
   }
  ],
- "modified": "2023-02-18 11:04:46.343527",
+ "modified": "2023-05-09 15:38:40.255193",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Customer",
diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json
index 99693d9..6cb4292 100644
--- a/erpnext/setup/doctype/employee/employee.json
+++ b/erpnext/setup/doctype/employee/employee.json
@@ -78,7 +78,9 @@
   "salary_mode",
   "bank_details_section",
   "bank_name",
+  "column_break_heye",
   "bank_ac_no",
+  "iban",
   "personal_details",
   "marital_status",
   "family_background",
@@ -804,17 +806,26 @@
   {
    "fieldname": "column_break_104",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "column_break_heye",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:doc.salary_mode == 'Bank'",
+   "fieldname": "iban",
+   "fieldtype": "Data",
+   "label": "IBAN"
   }
  ],
  "icon": "fa fa-user",
  "idx": 24,
  "image_field": "image",
  "links": [],
- "modified": "2022-09-13 10:27:14.579197",
+ "modified": "2023-03-30 15:57:05.174592",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Employee",
- "name_case": "Title Case",
  "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 77545e0..a648195 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -200,6 +200,9 @@
 			}
 		}
 
+		erpnext.accounts.ledger_preview.show_accounting_ledger_preview(this.frm);
+		erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
+
 		if (doc.docstatus > 0) {
 			this.show_stock_ledger();
 			if (erpnext.is_perpetual_inventory_enabled(doc.company)) {
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 312c166..35aad78 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -121,6 +121,10 @@
 	refresh() {
 		var me = this;
 		super.refresh();
+
+		erpnext.accounts.ledger_preview.show_accounting_ledger_preview(this.frm);
+		erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
+
 		if(this.frm.doc.docstatus > 0) {
 			this.show_stock_ledger();
 			//removed for temporary
@@ -209,10 +213,43 @@
 	}
 
 	make_purchase_return() {
-		frappe.model.open_mapped_doc({
-			method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_return",
-			frm: cur_frm
+		let me = this;
+
+		let has_rejected_items = cur_frm.doc.items.filter((item) => {
+			if (item.rejected_qty > 0) {
+				return true;
+			}
 		})
+
+		if (has_rejected_items && has_rejected_items.length > 0) {
+			frappe.prompt([
+				{
+					label: __("Return Qty from Rejected Warehouse"),
+					fieldtype: "Check",
+					fieldname: "return_for_rejected_warehouse",
+					default: 1
+				},
+			], function(values){
+				if (values.return_for_rejected_warehouse) {
+					frappe.call({
+						method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_return_against_rejected_warehouse",
+						args: {
+							source_name: cur_frm.doc.name
+						},
+						callback: function(r) {
+							if(r.message) {
+								frappe.model.sync(r.message);
+								frappe.set_route("Form", r.message.doctype, r.message.name);
+							}
+						}
+					})
+				} else {
+					cur_frm.cscript._make_purchase_return();
+				}
+			}, __("Return Qty"), __("Make Return Entry"));
+		} else {
+			cur_frm.cscript._make_purchase_return();
+		}
 	}
 
 	close_purchase_receipt() {
@@ -322,6 +359,13 @@
 	},
 });
 
+cur_frm.cscript._make_purchase_return = function() {
+	frappe.model.open_mapped_doc({
+		method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_return",
+		frm: cur_frm
+	});
+}
+
 cur_frm.cscript['Make Stock Entry'] = function() {
 	frappe.model.open_mapped_doc({
 		method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_stock_entry",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 387f031..0b5dc05 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -1137,6 +1137,13 @@
 
 
 @frappe.whitelist()
+def make_purchase_return_against_rejected_warehouse(source_name):
+	from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
+	return make_return_doc("Purchase Receipt", source_name, return_against_rejected_qty=True)
+
+
+@frappe.whitelist()
 def make_purchase_return(source_name, target_doc=None):
 	from erpnext.controllers.sales_and_purchase_return import make_return_doc
 
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index ddc0556..1986722 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -1827,6 +1827,33 @@
 
 		self.assertEqual(abs(data["stock_value_difference"]), 400.00)
 
+	def test_return_from_rejected_warehouse(self):
+		from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
+			make_purchase_return_against_rejected_warehouse,
+		)
+
+		item_code = "_Test Item Return from Rejected Warehouse"
+		create_item(item_code)
+
+		warehouse = create_warehouse("_Test Warehouse Return Qty Warehouse")
+		rejected_warehouse = create_warehouse("_Test Rejected Warehouse Return Qty Warehouse")
+
+		# Step 1: Create Purchase Receipt with valuation rate 100
+		pr = make_purchase_receipt(
+			item_code=item_code,
+			warehouse=warehouse,
+			qty=10,
+			rate=100,
+			rejected_qty=2,
+			rejected_warehouse=rejected_warehouse,
+		)
+
+		pr_return = make_purchase_return_against_rejected_warehouse(pr.name)
+		self.assertEqual(pr_return.items[0].warehouse, rejected_warehouse)
+		self.assertEqual(pr_return.items[0].qty, 2.0 * -1)
+		self.assertEqual(pr_return.items[0].rejected_qty, 0.0)
+		self.assertEqual(pr_return.items[0].rejected_warehouse, "")
+
 
 def prepare_data_for_internal_transfer():
 	from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
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 d5fc710..27066b8 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -13,6 +13,7 @@
 from rq.timeouts import JobTimeoutException
 
 import erpnext
+from erpnext.accounts.general_ledger import validate_accounting_period
 from erpnext.accounts.utils import get_future_stock_vouchers, repost_gle_for_stock_vouchers
 from erpnext.stock.stock_ledger import (
 	get_affected_transactions,
@@ -44,11 +45,49 @@
 		self.validate_accounts_freeze()
 
 	def validate_period_closing_voucher(self):
+		# Period Closing Voucher
 		year_end_date = self.get_max_year_end_date(self.company)
 		if year_end_date and getdate(self.posting_date) <= getdate(year_end_date):
-			msg = f"Due to period closing, you cannot repost item valuation before {year_end_date}"
+			date = frappe.format(year_end_date, "Date")
+			msg = f"Due to period closing, you cannot repost item valuation before {date}"
 			frappe.throw(_(msg))
 
+		# Accounting Period
+		if self.voucher_type:
+			validate_accounting_period(
+				[
+					frappe._dict(
+						{
+							"posting_date": self.posting_date,
+							"company": self.company,
+							"voucher_type": self.voucher_type,
+						}
+					)
+				]
+			)
+
+		# Closing Stock Balance
+		closing_stock = self.get_closing_stock_balance()
+		if closing_stock and closing_stock[0].name:
+			name = get_link_to_form("Closing Stock Balance", closing_stock[0].name)
+			to_date = frappe.format(closing_stock[0].to_date, "Date")
+			msg = f"Due to closing stock balance {name}, you cannot repost item valuation before {to_date}"
+			frappe.throw(_(msg))
+
+	def get_closing_stock_balance(self):
+		filters = {
+			"company": self.company,
+			"status": "Completed",
+			"docstatus": 1,
+			"to_date": (">=", self.posting_date),
+		}
+
+		for field in ["warehouse", "item_code"]:
+			if self.get(field):
+				filters.update({field: ("in", ["", self.get(field)])})
+
+		return frappe.get_all("Closing Stock Balance", fields=["name", "to_date"], filters=filters)
+
 	@staticmethod
 	def get_max_year_end_date(company):
 		data = frappe.get_all(
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 9c4d997..1853f45 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
@@ -392,3 +392,33 @@
 		pr.cancel()
 		self.assertTrue(pr.docstatus == 2)
 		self.assertTrue(frappe.db.exists("Repost Item Valuation", {"voucher_no": pr.name}))
+
+	def test_repost_item_valuation_for_closing_stock_balance(self):
+		from erpnext.stock.doctype.closing_stock_balance.closing_stock_balance import (
+			prepare_closing_stock_balance,
+		)
+
+		doc = frappe.new_doc("Closing Stock Balance")
+		doc.company = "_Test Company"
+		doc.from_date = today()
+		doc.to_date = today()
+		doc.submit()
+
+		prepare_closing_stock_balance(doc.name)
+		doc.load_from_db()
+		self.assertEqual(doc.docstatus, 1)
+		self.assertEqual(doc.status, "Completed")
+
+		riv = frappe.new_doc("Repost Item Valuation")
+		riv.update(
+			{
+				"item_code": "_Test Item",
+				"warehouse": "_Test Warehouse - _TC",
+				"based_on": "Item and Warehouse",
+				"posting_date": today(),
+				"posting_time": "00:01:00",
+			}
+		)
+
+		self.assertRaises(frappe.ValidationError, riv.save)
+		doc.cancel()
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 64650bc..4f85ac0 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -191,7 +191,6 @@
 	return args
 
 
-@frappe.whitelist()
 def get_item_code(barcode=None, serial_no=None):
 	if barcode:
 		item_code = frappe.db.get_value("Item Barcode", {"barcode": barcode}, fieldname=["parent"])
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 402f998..0244406 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -475,7 +475,7 @@
 
 	for row_idx, row in enumerate(result):
 		for convertible_col, data in convertible_column_map.items():
-			conversion_factor = conversion_factors[row.get("item_code")] or 1
+			conversion_factor = conversion_factors.get(row.get("item_code")) or 1.0
 			for_type = data.for_type
 			value_before_conversion = row.get(convertible_col)
 			if for_type == "rate":
diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js
index 613c967..9beba3f 100644
--- a/erpnext/templates/generators/item/item_configure.js
+++ b/erpnext/templates/generators/item/item_configure.js
@@ -219,7 +219,8 @@
 						: ''
 					}
 
-					${available_qty === 0 ? '<span class="text-danger">(' + __('Out of Stock') + ')</span>' : ''}
+					${available_qty === 0 && product_info && product_info?.is_stock_item
+						? '<span class="text-danger">(' + __('Out of Stock') + ')</span>' : ''}
 
 				</div></div>
 				<a href data-action="btn_clear_values" data-item-code="${one_item}">
@@ -236,7 +237,8 @@
 			</div>`;
 		/* eslint-disable indent */
 
-		if (!product_info?.allow_items_not_in_stock && available_qty === 0) {
+		if (!product_info?.allow_items_not_in_stock && available_qty === 0
+			&& product_info && product_info?.is_stock_item) {
 			item_add_to_cart = '';
 		}
 
diff --git a/pyproject.toml b/pyproject.toml
index c119ada..012ffb1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,6 +12,7 @@
     "pycountry~=22.3.5",
     "Unidecode~=1.3.6",
     "barcodenumber~=0.5.0",
+    "rapidfuzz~=2.15.0",
 
     # integration dependencies
     "gocardless-pro~=1.22.0",