Merge branch 'develop' into fix-translation-files
diff --git a/CODEOWNERS b/CODEOWNERS
index 7f8c4d1..540680c 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -22,4 +22,4 @@
 erpnext/patches/                @deepeshgarg007 
 
 .github/                        @deepeshgarg007
-pyproject.toml                  @ankush
+pyproject.toml                  @phot0n
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index e0f0c98..c9c9c9c 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -1,3 +1,4 @@
+import functools
 import inspect
 
 import frappe
@@ -120,12 +121,14 @@
 
 	You can also set global company flag in `frappe.flags.company`
 	"""
-	if company or frappe.flags.company:
-		return frappe.get_cached_value("Company", company or frappe.flags.company, "country")
-	elif frappe.flags.country:
-		return frappe.flags.country
-	else:
-		return frappe.get_system_settings("country")
+
+	if not company:
+		company = frappe.local.flags.company
+
+	if company:
+		return frappe.get_cached_value("Company", company, "country")
+
+	return frappe.flags.country or frappe.get_system_settings("country")
 
 
 def allow_regional(fn):
@@ -136,6 +139,7 @@
 	def myfunction():
 	  pass"""
 
+	@functools.wraps(fn)
 	def caller(*args, **kwargs):
 		overrides = frappe.get_hooks("regional_overrides", {}).get(get_region())
 		function_path = f"{inspect.getmodule(fn).__name__}.{fn.__name__}"
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index f9c9173..3a360c4 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -297,7 +297,7 @@
 		# fixed asset depreciation
 		["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
 		["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
-		["_Test Depreciations", "Expenses", 0, None, None],
+		["_Test Depreciations", "Expenses", 0, "Depreciation", None],
 		["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
 		# Receivable / Payable Account
 		["_Test Receivable", "Current Assets", 0, "Receivable", None],
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 5cecddd..2996836 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -19,6 +19,8 @@
   "column_break_17",
   "enable_common_party_accounting",
   "allow_multi_currency_invoices_against_single_party_account",
+  "journals_section",
+  "merge_similar_account_heads",
   "report_setting_section",
   "use_custom_cash_flow",
   "deferred_accounting_settings_section",
@@ -38,6 +40,8 @@
   "show_payment_schedule_in_print",
   "currency_exchange_section",
   "allow_stale",
+  "section_break_jpd0",
+  "auto_reconcile_payments",
   "stale_days",
   "invoicing_settings_tab",
   "accounts_transactions_settings_section",
@@ -57,7 +61,6 @@
   "acc_frozen_upto",
   "column_break_25",
   "frozen_accounts_modifier",
-  "report_settings_sb",
   "tab_break_dpet",
   "show_balance_in_coa"
  ],
@@ -171,11 +174,6 @@
    "label": "Stale Days"
   },
   {
-   "fieldname": "report_settings_sb",
-   "fieldtype": "Section Break",
-   "label": "Report Settings"
-  },
-  {
    "default": "0",
    "description": "Only select this if you have set up the Cash Flow Mapper documents",
    "fieldname": "use_custom_cash_flow",
@@ -369,6 +367,29 @@
    "fieldname": "book_tax_discount_loss",
    "fieldtype": "Check",
    "label": "Book Tax Loss on Early Payment Discount"
+  },
+  {
+   "fieldname": "journals_section",
+   "fieldtype": "Section Break",
+   "label": "Journals"
+  },
+  {
+   "default": "0",
+   "description": "Rows with Same Account heads will be merged on Ledger",
+   "fieldname": "merge_similar_account_heads",
+   "fieldtype": "Check",
+   "label": "Merge Similar Account Heads"
+  },
+  {
+   "fieldname": "section_break_jpd0",
+   "fieldtype": "Section Break",
+   "label": "Payment Reconciliations"
+  },
+  {
+   "default": "0",
+   "fieldname": "auto_reconcile_payments",
+   "fieldtype": "Check",
+   "label": "Auto Reconcile Payments"
   }
  ],
  "icon": "icon-cog",
@@ -376,7 +397,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-04-14 17:22:03.680886",
+ "modified": "2023-04-21 13:11:37.130743",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
index 0817187..8ad0bd1 100644
--- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
@@ -56,7 +56,7 @@
 			select
 				"Payment Entry" as payment_document, name as payment_entry,
 				reference_no as cheque_number, reference_date as cheque_date,
-				if(paid_from=%(account)s, paid_amount, 0) as credit,
+				if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit,
 				if(paid_from=%(account)s, 0, received_amount) as debit,
 				posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
 				if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index fcbaf32..b441af9 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -281,10 +281,13 @@
 		)
 
 	elif payment_entry.payment_document == "Journal Entry":
-		return frappe.db.get_value(
-			"Journal Entry Account",
-			{"parent": payment_entry.payment_entry, "account": gl_bank_account},
-			"sum(credit_in_account_currency)",
+		return abs(
+			frappe.db.get_value(
+				"Journal Entry Account",
+				{"parent": payment_entry.payment_entry, "account": gl_bank_account},
+				"sum(debit_in_account_currency-credit_in_account_currency)",
+			)
+			or 0
 		)
 
 	elif payment_entry.payment_document == "Expense Claim":
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 089f20b..b31cc32 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"];
+		frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", '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 68364be..34a753f 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -69,6 +69,7 @@
 		self.validate_empty_accounts_table()
 		self.set_account_and_party_balance()
 		self.validate_inter_company_accounts()
+		self.validate_depr_entry_voucher_type()
 
 		if self.docstatus == 0:
 			self.apply_tax_withholding()
@@ -130,6 +131,13 @@
 				if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit:
 					frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry"))
 
+	def validate_depr_entry_voucher_type(self):
+		if (
+			any(d.account_type == "Depreciation" for d in self.get("accounts"))
+			and self.voucher_type != "Depreciation Entry"
+		):
+			frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation"))
+
 	def validate_stock_accounts(self):
 		stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
 		for account in stock_accounts:
@@ -233,25 +241,30 @@
 			self.remove(d)
 
 	def update_asset_value(self):
-		if self.voucher_type != "Depreciation Entry":
+		if self.flags.planned_depr_entry or self.voucher_type != "Depreciation Entry":
 			return
 
-		processed_assets = []
-
 		for d in self.get("accounts"):
 			if (
-				d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets
+				d.reference_type == "Asset"
+				and d.reference_name
+				and d.account_type == "Depreciation"
+				and d.debit
 			):
-				processed_assets.append(d.reference_name)
-
 				asset = frappe.get_doc("Asset", d.reference_name)
 
 				if asset.calculate_depreciation:
-					continue
-
-				depr_value = d.debit or d.credit
-
-				asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value)
+					fb_idx = 1
+					if self.finance_book:
+						for fb_row in asset.get("finance_books"):
+							if fb_row.finance_book == self.finance_book:
+								fb_idx = fb_row.idx
+								break
+					fb_row = asset.get("finance_books")[fb_idx - 1]
+					fb_row.value_after_depreciation -= d.debit
+					fb_row.db_update()
+				else:
+					asset.db_set("value_after_depreciation", asset.value_after_depreciation - d.debit)
 
 				asset.set_status()
 
@@ -316,42 +329,47 @@
 		if self.voucher_type != "Depreciation Entry":
 			return
 
-		processed_assets = []
-
 		for d in self.get("accounts"):
 			if (
-				d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets
+				d.reference_type == "Asset"
+				and d.reference_name
+				and d.account_type == "Depreciation"
+				and d.debit
 			):
-				processed_assets.append(d.reference_name)
-
 				asset = frappe.get_doc("Asset", d.reference_name)
 
 				if asset.calculate_depreciation:
 					je_found = False
 
-					for row in asset.get("finance_books"):
+					for fb_row in asset.get("finance_books"):
 						if je_found:
 							break
 
-						depr_schedule = get_depr_schedule(asset.name, "Active", row.finance_book)
+						depr_schedule = get_depr_schedule(asset.name, "Active", fb_row.finance_book)
 
 						for s in depr_schedule or []:
 							if s.journal_entry == self.name:
 								s.db_set("journal_entry", None)
 
-								row.value_after_depreciation += s.depreciation_amount
-								row.db_update()
-
-								asset.set_status()
+								fb_row.value_after_depreciation += d.debit
+								fb_row.db_update()
 
 								je_found = True
 								break
+					if not je_found:
+						fb_idx = 1
+						if self.finance_book:
+							for fb_row in asset.get("finance_books"):
+								if fb_row.finance_book == self.finance_book:
+									fb_idx = fb_row.idx
+									break
+
+						fb_row = asset.get("finance_books")[fb_idx - 1]
+						fb_row.value_after_depreciation += d.debit
+						fb_row.db_update()
 				else:
-					depr_value = d.debit or d.credit
-
-					asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value)
-
-					asset.set_status()
+					asset.db_set("value_after_depreciation", asset.value_after_depreciation + d.debit)
+				asset.set_status()
 
 	def unlink_inter_company_jv(self):
 		if (
@@ -885,6 +903,8 @@
 	def make_gl_entries(self, cancel=0, adv_adj=0):
 		from erpnext.accounts.general_ledger import make_gl_entries
 
+		merge_entries = frappe.db.get_single_value("Accounts Settings", "merge_similar_account_heads")
+
 		gl_map = self.build_gl_map()
 		if self.voucher_type in ("Deferred Revenue", "Deferred Expense"):
 			update_outstanding = "No"
@@ -892,7 +912,13 @@
 			update_outstanding = "Yes"
 
 		if gl_map:
-			make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
+			make_gl_entries(
+				gl_map,
+				cancel=cancel,
+				adv_adj=adv_adj,
+				merge_entries=merge_entries,
+				update_outstanding=update_outstanding,
+			)
 
 	@frappe.whitelist()
 	def get_balance(self, difference_account=None):
diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
index 88f1c90..5ebdf61 100644
--- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
+++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
@@ -2,6 +2,21 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on("Journal Entry Template", {
+	onload: function(frm) {
+		if(frm.is_new()) {
+			frappe.call({
+				type: "GET",
+				method: "erpnext.accounts.doctype.journal_entry_template.journal_entry_template.get_naming_series",
+				callback: function(r){
+					if(r.message) {
+						frm.set_df_property("naming_series", "options", r.message.split("\n"));
+						frm.set_value("naming_series", r.message.split("\n")[0]);
+						frm.refresh_field("naming_series");
+					}
+				}
+			});
+		}
+	},
 	refresh: function(frm) {
 		frappe.model.set_default_values(frm.doc);
 
@@ -19,18 +34,6 @@
 
 			return { filters: filters };
 		});
-
-		frappe.call({
-			type: "GET",
-			method: "erpnext.accounts.doctype.journal_entry_template.journal_entry_template.get_naming_series",
-			callback: function(r){
-				if(r.message){
-					frm.set_df_property("naming_series", "options", r.message.split("\n"));
-					frm.set_value("naming_series", r.message.split("\n")[0]);
-					frm.refresh_field("naming_series");
-				}
-			}
-		});
 	},
 	voucher_type: function(frm) {
 		var add_accounts = function(doc, r) {
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index ed6d0a7..07761c7 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -977,6 +977,7 @@
 							precision("difference_amount"));
 
 						const add_deductions = (details) => {
+							let row = null;
 							if (!write_off_row.length && difference_amount) {
 								row = frm.add_child("deductions");
 								row.account = details[account];
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 3583dc7..3df48e2 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -60,6 +60,7 @@
 	def validate(self):
 		self.setup_party_account_field()
 		self.set_missing_values()
+		self.set_missing_ref_details()
 		self.validate_payment_type()
 		self.validate_party_details()
 		self.set_exchange_rate()
@@ -219,11 +220,16 @@
 			else self.paid_to_account_currency
 		)
 
-		self.set_missing_ref_details()
-
-	def set_missing_ref_details(self, force=False):
+	def set_missing_ref_details(
+		self, force: bool = False, update_ref_details_only_for: list | None = None
+	) -> None:
 		for d in self.get("references"):
 			if d.allocated_amount:
+				if update_ref_details_only_for and (
+					not (d.reference_doctype, d.reference_name) in update_ref_details_only_for
+				):
+					continue
+
 				ref_details = get_reference_details(
 					d.reference_doctype, d.reference_name, self.party_account_currency
 				)
@@ -648,6 +654,28 @@
 				self.precision("base_received_amount"),
 			)
 
+	def calculate_base_allocated_amount_for_reference(self, d) -> float:
+		base_allocated_amount = 0
+		if d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"):
+			# When referencing Sales/Purchase Order, use the source/target exchange rate depending on payment type.
+			# This is so there are no Exchange Gain/Loss generated for such doctypes
+
+			exchange_rate = 1
+			if self.payment_type == "Receive":
+				exchange_rate = self.source_exchange_rate
+			elif self.payment_type == "Pay":
+				exchange_rate = self.target_exchange_rate
+
+			base_allocated_amount += flt(
+				flt(d.allocated_amount) * flt(exchange_rate), self.precision("base_paid_amount")
+			)
+		else:
+			base_allocated_amount += flt(
+				flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
+			)
+
+		return base_allocated_amount
+
 	def set_total_allocated_amount(self):
 		if self.payment_type == "Internal Transfer":
 			return
@@ -656,9 +684,7 @@
 		for d in self.get("references"):
 			if d.allocated_amount:
 				total_allocated_amount += flt(d.allocated_amount)
-				base_total_allocated_amount += flt(
-					flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
-				)
+				base_total_allocated_amount += self.calculate_base_allocated_amount_for_reference(d)
 
 		self.total_allocated_amount = abs(total_allocated_amount)
 		self.base_total_allocated_amount = abs(base_total_allocated_amount)
@@ -875,9 +901,7 @@
 					}
 				)
 
-				allocated_amount_in_company_currency = flt(
-					flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("paid_amount")
-				)
+				allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d)
 
 				gle.update(
 					{
@@ -1687,7 +1711,10 @@
 ):
 	reference_doc = None
 	doc = frappe.get_doc(dt, dn)
-	if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= 99.99:
+	over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
+	if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= (
+		100.0 + over_billing_allowance
+	):
 		frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
 
 	if not party_type:
@@ -1706,6 +1733,13 @@
 	# bank or cash
 	bank = get_bank_cash_account(doc, bank_account)
 
+	# 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)
+
 	paid_amount, received_amount = set_paid_amount_and_received_amount(
 		dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc
 	)
@@ -1811,6 +1845,7 @@
 
 	pe.setup_party_account_field()
 	pe.set_missing_values()
+	pe.set_missing_ref_details()
 
 	update_accounting_dimensions(pe, doc)
 
@@ -1921,19 +1956,27 @@
 	paid_amount = received_amount = 0
 	if party_account_currency == bank.account_currency:
 		paid_amount = received_amount = abs(outstanding_amount)
-	elif payment_type == "Receive":
-		paid_amount = abs(outstanding_amount)
-		if bank_amount:
-			received_amount = bank_amount
-		else:
-			received_amount = paid_amount * doc.get("conversion_rate", 1)
 	else:
-		received_amount = abs(outstanding_amount)
-		if bank_amount:
-			paid_amount = bank_amount
+		company_currency = frappe.get_cached_value("Company", doc.get("company"), "default_currency")
+		if payment_type == "Receive":
+			paid_amount = abs(outstanding_amount)
+			if bank_amount:
+				received_amount = bank_amount
+			else:
+				if company_currency != bank.account_currency:
+					received_amount = paid_amount / doc.get("conversion_rate", 1)
+				else:
+					received_amount = paid_amount * doc.get("conversion_rate", 1)
 		else:
-			# if party account currency and bank currency is different then populate paid amount as well
-			paid_amount = received_amount * doc.get("conversion_rate", 1)
+			received_amount = abs(outstanding_amount)
+			if bank_amount:
+				paid_amount = bank_amount
+			else:
+				if company_currency != bank.account_currency:
+					paid_amount = received_amount / doc.get("conversion_rate", 1)
+				else:
+					# if party account currency and bank currency is different then populate paid amount as well
+					paid_amount = received_amount * doc.get("conversion_rate", 1)
 
 	return paid_amount, received_amount
 
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 67049c4..68f333d 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -51,6 +51,38 @@
 		so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid")
 		self.assertEqual(so_advance_paid, 0)
 
+	def test_payment_against_sales_order_usd_to_inr(self):
+		so = make_sales_order(
+			customer="_Test Customer USD", currency="USD", qty=1, rate=100, do_not_submit=True
+		)
+		so.conversion_rate = 50
+		so.submit()
+		pe = get_payment_entry("Sales Order", so.name)
+		pe.source_exchange_rate = 55
+		pe.received_amount = 5500
+		pe.insert()
+		pe.submit()
+
+		# there should be no difference amount
+		pe.reload()
+		self.assertEqual(pe.difference_amount, 0)
+		self.assertEqual(pe.deductions, [])
+
+		expected_gle = dict(
+			(d[0], d)
+			for d in [["_Test Receivable USD - _TC", 0, 5500, so.name], ["Cash - _TC", 5500.0, 0, None]]
+		)
+
+		self.validate_gl_entries(pe.name, expected_gle)
+
+		so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid")
+		self.assertEqual(so_advance_paid, 100)
+
+		pe.cancel()
+
+		so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid")
+		self.assertEqual(so_advance_paid, 0)
+
 	def test_payment_entry_for_blocked_supplier_invoice(self):
 		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index caffac5..08d38dd 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -82,6 +82,32 @@
 			this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'default');
 			this.frm.change_custom_button_type('Allocate', null, 'default');
 		}
+
+		// 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
+							}
+						}
+					}).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");
+						}
+					});
+				}
+			});
+		}
+
 	}
 
 	company() {
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index d8082d0..cc2b942 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -7,9 +7,12 @@
 from frappe.model.document import Document
 from frappe.query_builder.custom import ConstantColumn
 from frappe.query_builder.functions import IfNull
-from frappe.utils import flt, getdate, nowdate, today
+from frappe.utils import flt, get_link_to_form, getdate, nowdate, today
 
 import erpnext
+from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import (
+	is_any_doc_running,
+)
 from erpnext.accounts.utils import (
 	QueryPaymentLedger,
 	get_outstanding_invoices,
@@ -304,9 +307,7 @@
 			}
 		)
 
-	@frappe.whitelist()
-	def reconcile(self):
-		self.validate_allocation()
+	def reconcile_allocations(self, skip_ref_details_update_for_pe=False):
 		dr_or_cr = (
 			"credit_in_account_currency"
 			if erpnext.get_party_account_type(self.party_type) == "Receivable"
@@ -330,12 +331,35 @@
 					self.make_difference_entry(payment_details)
 
 		if entry_list:
-			reconcile_against_document(entry_list)
+			reconcile_against_document(entry_list, skip_ref_details_update_for_pe)
 
 		if dr_or_cr_notes:
 			reconcile_dr_cr_note(dr_or_cr_notes, self.company)
 
+	@frappe.whitelist()
+	def reconcile(self):
+		if frappe.db.get_single_value("Accounts Settings", "auto_reconcile_payments"):
+			running_doc = is_any_doc_running(
+				dict(
+					company=self.company,
+					party_type=self.party_type,
+					party=self.party,
+					receivable_payable_account=self.receivable_payable_account,
+				)
+			)
+
+			if running_doc:
+				frappe.throw(
+					_("A Reconciliation Job {0} is running for the same filters. Cannot reconcile now").format(
+						get_link_to_form("Auto Reconcile", running_doc)
+					)
+				)
+				return
+
+		self.validate_allocation()
+		self.reconcile_allocations()
 		msgprint(_("Successfully Reconciled"))
+
 		self.get_unreconciled_entries()
 
 	def make_difference_entry(self, row):
diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
index 62ae857..5d08e8d 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
@@ -205,7 +205,7 @@
 
 		self.assertRaises(frappe.ValidationError, jv1.submit)
 
-	def test_closing_balance_with_dimensions(self):
+	def test_closing_balance_with_dimensions_and_test_reposting_entry(self):
 		frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
 		frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
 		frappe.db.sql("delete from `tabAccount Closing Balance` where company='Test PCV Company'")
@@ -299,6 +299,24 @@
 		self.assertEqual(cc2_closing_balance.credit, 500)
 		self.assertEqual(cc2_closing_balance.credit_in_account_currency, 500)
 
+		warehouse = frappe.db.get_value("Warehouse", {"company": company}, "name")
+
+		repost_doc = frappe.get_doc(
+			{
+				"doctype": "Repost Item Valuation",
+				"company": company,
+				"posting_date": "2020-03-15",
+				"based_on": "Item and Warehouse",
+				"item_code": "Test Item 1",
+				"warehouse": warehouse,
+			}
+		)
+
+		self.assertRaises(frappe.ValidationError, repost_doc.save)
+
+		repost_doc.posting_date = today()
+		repost_doc.save()
+
 	def make_period_closing_voucher(self, posting_date=None, submit=True):
 		surplus_account = create_account()
 		cost_center = create_cost_center("Test Cost Center 1")
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/__init__.py b/erpnext/accounts/doctype/process_payment_reconciliation/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation/__init__.py
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js
new file mode 100644
index 0000000..dd601bf
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js
@@ -0,0 +1,130 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Process Payment Reconciliation", {
+	onload: function(frm) {
+		// set queries
+		frm.set_query("party_type", function() {
+			return {
+				"filters": {
+					"name": ["in", Object.keys(frappe.boot.party_account_types)],
+				}
+			}
+		});
+		frm.set_query('receivable_payable_account',  function(doc) {
+			return {
+				filters: {
+					"company": doc.company,
+					"is_group": 0,
+					"account_type": frappe.boot.party_account_types[doc.party_type]
+				}
+			};
+		});
+		frm.set_query('cost_center', function(doc) {
+			return {
+				filters: {
+					"company": doc.company,
+					"is_group": 0,
+				}
+			};
+		});
+		frm.set_query('bank_cash_account', function(doc) {
+			return {
+				filters:[
+					['Account', 'company', '=', doc.company],
+					['Account', 'is_group', '=', 0],
+					['Account', 'account_type', 'in', ['Bank', 'Cash']]
+				]
+			};
+		});
+
+	},
+	refresh: function(frm) {
+		if (frm.doc.docstatus==1 && ['Queued', 'Paused'].find(x => x == frm.doc.status)) {
+			let execute_btn = __("Start / Resume")
+
+			frm.add_custom_button(execute_btn, () => {
+				frm.call({
+					method: 'erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.trigger_job_for_doc',
+					args: {
+						docname: frm.doc.name
+					}
+				}).then(r => {
+					if(!r.exc) {
+						frappe.show_alert(__("Job Started"));
+						frm.reload_doc();
+					}
+				});
+			});
+		}
+		if (frm.doc.docstatus==1 && ['Completed', 'Running', 'Paused', 'Partially Reconciled'].find(x => x == frm.doc.status)) {
+			frm.call({
+				'method': "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.get_reconciled_count",
+				args: {
+					"docname": frm.docname,
+				}
+			}).then(r => {
+				if (r.message) {
+					let progress = 0;
+					let description = "";
+
+					if (r.message.processed) {
+						progress = (r.message.processed/r.message.total) * 100;
+						description = r.message.processed + "/" + r.message.total +  " processed";
+					} else if (r.message.total == 0 && frm.doc.status == "Completed") {
+						progress = 100;
+					}
+
+
+					frm.dashboard.add_progress('Reconciliation Progress', progress, description);
+				}
+			})
+		}
+		if (frm.doc.docstatus==1 && frm.doc.status == 'Running') {
+			let execute_btn = __("Pause")
+
+			frm.add_custom_button(execute_btn, () => {
+				frm.call({
+					'method': "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.pause_job_for_doc",
+					args: {
+						"docname": frm.docname,
+					}
+				}).then(r => {
+					if (!r.exc) {
+						frappe.show_alert(__("Job Paused"));
+						frm.reload_doc()
+					}
+				});
+
+			});
+		}
+	},
+	company(frm) {
+		frm.set_value('party', '');
+		frm.set_value('receivable_payable_account', '');
+	},
+	party_type(frm) {
+		frm.set_value('party', '');
+	},
+
+	party(frm) {
+		frm.set_value('receivable_payable_account', '');
+		if (!frm.doc.receivable_payable_account && frm.doc.party_type && frm.doc.party) {
+			return frappe.call({
+				method: "erpnext.accounts.party.get_party_account",
+				args: {
+					company: frm.doc.company,
+					party_type: frm.doc.party_type,
+					party: frm.doc.party
+				},
+				callback: (r) => {
+					if (!r.exc && r.message) {
+						frm.set_value("receivable_payable_account", r.message);
+					}
+					frm.refresh();
+
+				}
+			});
+		}
+	}
+});
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json
new file mode 100644
index 0000000..8bb7092
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json
@@ -0,0 +1,173 @@
+{
+ "actions": [],
+ "autoname": "format:ACC-PPR-{#####}",
+ "beta": 1,
+ "creation": "2023-03-30 21:28:39.793927",
+ "default_view": "List",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "party_type",
+  "column_break_io6c",
+  "party",
+  "receivable_payable_account",
+  "filter_section",
+  "from_invoice_date",
+  "to_invoice_date",
+  "column_break_kegk",
+  "from_payment_date",
+  "to_payment_date",
+  "column_break_uj04",
+  "cost_center",
+  "bank_cash_account",
+  "section_break_2n02",
+  "status",
+  "error_log",
+  "section_break_a8yx",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "allow_on_submit": 1,
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "label": "Status",
+   "options": "\nQueued\nRunning\nPaused\nCompleted\nPartially Reconciled\nFailed\nCancelled",
+   "read_only": 1
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "party_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Party Type",
+   "options": "DocType",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_io6c",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "party",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "label": "Party",
+   "options": "party_type",
+   "reqd": 1
+  },
+  {
+   "fieldname": "receivable_payable_account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Receivable/Payable Account",
+   "options": "Account",
+   "reqd": 1
+  },
+  {
+   "fieldname": "filter_section",
+   "fieldtype": "Section Break",
+   "label": "Filters"
+  },
+  {
+   "fieldname": "from_invoice_date",
+   "fieldtype": "Date",
+   "label": "From Invoice Date"
+  },
+  {
+   "fieldname": "to_invoice_date",
+   "fieldtype": "Date",
+   "label": "To Invoice Date"
+  },
+  {
+   "fieldname": "column_break_kegk",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "from_payment_date",
+   "fieldtype": "Date",
+   "label": "From Payment Date"
+  },
+  {
+   "fieldname": "to_payment_date",
+   "fieldtype": "Date",
+   "label": "To Payment Date"
+  },
+  {
+   "fieldname": "column_break_uj04",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "bank_cash_account",
+   "fieldtype": "Link",
+   "label": "Bank/Cash Account",
+   "options": "Account"
+  },
+  {
+   "fieldname": "section_break_2n02",
+   "fieldtype": "Section Break",
+   "label": "Status"
+  },
+  {
+   "depends_on": "eval:doc.error_log",
+   "fieldname": "error_log",
+   "fieldtype": "Long Text",
+   "label": "Error Log"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Process Payment Reconciliation",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_a8yx",
+   "fieldtype": "Section Break"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2023-04-21 17:19:30.912953",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Process Payment Reconciliation",
+ "naming_rule": "Expression",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "title_field": "company"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py
new file mode 100644
index 0000000..ecb51ce
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py
@@ -0,0 +1,503 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _, qb
+from frappe.model.document import Document
+from frappe.utils import get_link_to_form
+from frappe.utils.scheduler import is_scheduler_inactive
+
+
+class ProcessPaymentReconciliation(Document):
+	def validate(self):
+		self.validate_receivable_payable_account()
+		self.validate_bank_cash_account()
+
+	def validate_receivable_payable_account(self):
+		if self.receivable_payable_account:
+			if self.company != frappe.db.get_value("Account", self.receivable_payable_account, "company"):
+				frappe.throw(
+					_("Receivable/Payable Account: {0} doesn't belong to company {1}").format(
+						frappe.bold(self.receivable_payable_account), frappe.bold(self.company)
+					)
+				)
+
+	def validate_bank_cash_account(self):
+		if self.bank_cash_account:
+			if self.company != frappe.db.get_value("Account", self.bank_cash_account, "company"):
+				frappe.throw(
+					_("Bank/Cash Account {0} doesn't belong to company {1}").format(
+						frappe.bold(self.bank_cash_account), frappe.bold(self.company)
+					)
+				)
+
+	def before_save(self):
+		self.status = ""
+		self.error_log = ""
+
+	def on_submit(self):
+		self.db_set("status", "Queued")
+		self.db_set("error_log", None)
+
+	def on_cancel(self):
+		self.db_set("status", "Cancelled")
+		log = frappe.db.get_value(
+			"Process Payment Reconciliation Log", filters={"process_pr": self.name}
+		)
+		if log:
+			frappe.db.set_value("Process Payment Reconciliation Log", log, "status", "Cancelled")
+
+
+@frappe.whitelist()
+def get_reconciled_count(docname: str | None = None) -> float:
+	current_status = {}
+	if docname:
+		reconcile_log = frappe.db.get_value(
+			"Process Payment Reconciliation Log", filters={"process_pr": docname}, fieldname="name"
+		)
+		if reconcile_log:
+			res = frappe.get_all(
+				"Process Payment Reconciliation Log",
+				filters={"name": reconcile_log},
+				fields=["reconciled_entries", "total_allocations"],
+				as_list=1,
+			)
+			current_status["processed"], current_status["total"] = res[0]
+
+	return current_status
+
+
+def get_pr_instance(doc: str):
+	process_payment_reconciliation = frappe.get_doc("Process Payment Reconciliation", doc)
+
+	pr = frappe.get_doc("Payment Reconciliation")
+	fields = [
+		"company",
+		"party_type",
+		"party",
+		"receivable_payable_account",
+		"from_invoice_date",
+		"to_invoice_date",
+		"from_payment_date",
+		"to_payment_date",
+	]
+	d = {}
+	for field in fields:
+		d[field] = process_payment_reconciliation.get(field)
+	pr.update(d)
+	pr.invoice_limit = 1000
+	pr.payment_limit = 1000
+	return pr
+
+
+def is_job_running(job_name: str) -> bool:
+	jobs = frappe.db.get_all("RQ Job", filters={"status": ["in", ["started", "queued"]]})
+	for x in jobs:
+		if x.job_name == job_name:
+			return True
+	return False
+
+
+@frappe.whitelist()
+def pause_job_for_doc(docname: str | None = None):
+	if docname:
+		frappe.db.set_value("Process Payment Reconciliation", docname, "status", "Paused")
+		log = frappe.db.get_value("Process Payment Reconciliation Log", filters={"process_pr": docname})
+		if log:
+			frappe.db.set_value("Process Payment Reconciliation Log", log, "status", "Paused")
+
+
+@frappe.whitelist()
+def trigger_job_for_doc(docname: str | None = None):
+	"""
+	Trigger background job
+	"""
+	if not docname:
+		return
+
+	if not frappe.db.get_single_value("Accounts Settings", "auto_reconcile_payments"):
+		frappe.throw(
+			_("Auto Reconciliation of Payments has been disabled. Enable it through {0}").format(
+				get_link_to_form("Accounts Settings", "Accounts Settings")
+			)
+		)
+
+		return
+
+	if not is_scheduler_inactive():
+		if frappe.db.get_value("Process Payment Reconciliation", docname, "status") == "Queued":
+			frappe.db.set_value("Process Payment Reconciliation", docname, "status", "Running")
+			job_name = f"start_processing_{docname}"
+			if not is_job_running(job_name):
+				job = frappe.enqueue(
+					method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile_based_on_filters",
+					queue="long",
+					is_async=True,
+					job_name=job_name,
+					enqueue_after_commit=True,
+					doc=docname,
+				)
+
+		elif frappe.db.get_value("Process Payment Reconciliation", docname, "status") == "Paused":
+			frappe.db.set_value("Process Payment Reconciliation", docname, "status", "Running")
+			log = frappe.db.get_value("Process Payment Reconciliation Log", filters={"process_pr": docname})
+			if log:
+				frappe.db.set_value("Process Payment Reconciliation Log", log, "status", "Running")
+
+			# Resume tasks for running doc
+			job_name = f"start_processing_{docname}"
+			if not is_job_running(job_name):
+				job = frappe.enqueue(
+					method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile_based_on_filters",
+					queue="long",
+					is_async=True,
+					job_name=job_name,
+					doc=docname,
+				)
+	else:
+		frappe.msgprint(_("Scheduler is Inactive. Can't trigger job now."))
+
+
+def trigger_reconciliation_for_queued_docs():
+	"""
+	Will be called from Cron Job
+	Fetch queued docs and start reconciliation process for each one
+	"""
+	if not frappe.db.get_single_value("Accounts Settings", "auto_reconcile_payments"):
+		frappe.throw(
+			_("Auto Reconciliation of Payments has been disabled. Enable it through {0}").format(
+				get_link_to_form("Accounts Settings", "Accounts Settings")
+			)
+		)
+
+		return
+
+	if not is_scheduler_inactive():
+		# Get all queued documents
+		all_queued = frappe.db.get_all(
+			"Process Payment Reconciliation",
+			filters={"docstatus": 1, "status": "Queued"},
+			order_by="creation desc",
+			as_list=1,
+		)
+
+		docs_to_trigger = []
+		unique_filters = set()
+		queue_size = 5
+
+		fields = ["company", "party_type", "party", "receivable_payable_account"]
+
+		def get_filters_as_tuple(fields, doc):
+			filters = ()
+			for x in fields:
+				filters += tuple(doc.get(x))
+			return filters
+
+		for x in all_queued:
+			doc = frappe.get_doc("Process Payment Reconciliation", x)
+			filters = get_filters_as_tuple(fields, doc)
+			if filters not in unique_filters:
+				unique_filters.add(filters)
+				docs_to_trigger.append(doc.name)
+			if len(docs_to_trigger) == queue_size:
+				break
+
+		# trigger reconcilation process for queue_size unique filters
+		for doc in docs_to_trigger:
+			trigger_job_for_doc(doc)
+
+	else:
+		frappe.msgprint(_("Scheduler is Inactive. Can't trigger jobs now."))
+
+
+def reconcile_based_on_filters(doc: None | str = None) -> None:
+	"""
+	Identify current state of document and execute next tasks in background
+	"""
+	if doc:
+		log = frappe.db.get_value("Process Payment Reconciliation Log", filters={"process_pr": doc})
+		if not log:
+			log = frappe.new_doc("Process Payment Reconciliation Log")
+			log.process_pr = doc
+			log.status = "Running"
+			log = log.save()
+
+			job_name = f"process_{doc}_fetch_and_allocate"
+			if not is_job_running(job_name):
+				job = frappe.enqueue(
+					method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.fetch_and_allocate",
+					queue="long",
+					timeout="3600",
+					is_async=True,
+					job_name=job_name,
+					enqueue_after_commit=True,
+					doc=doc,
+				)
+		else:
+			res = frappe.get_all(
+				"Process Payment Reconciliation Log",
+				filters={"name": log},
+				fields=["allocated", "reconciled"],
+				as_list=1,
+			)
+			allocated, reconciled = res[0]
+
+			if not allocated:
+				job_name = f"process__{doc}_fetch_and_allocate"
+				if not is_job_running(job_name):
+					job = frappe.enqueue(
+						method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.fetch_and_allocate",
+						queue="long",
+						timeout="3600",
+						is_async=True,
+						job_name=job_name,
+						enqueue_after_commit=True,
+						doc=doc,
+					)
+			elif not reconciled:
+				allocation = get_next_allocation(log)
+				if allocation:
+					reconcile_job_name = (
+						f"process_{doc}_reconcile_allocation_{allocation[0].idx}_{allocation[-1].idx}"
+					)
+				else:
+					reconcile_job_name = f"process_{doc}_reconcile"
+				if not is_job_running(reconcile_job_name):
+					job = frappe.enqueue(
+						method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile",
+						queue="long",
+						timeout="3600",
+						is_async=True,
+						job_name=reconcile_job_name,
+						enqueue_after_commit=True,
+						doc=doc,
+					)
+			elif reconciled:
+				frappe.db.set_value("Process Payment Reconciliation", doc, "status", "Completed")
+
+
+def get_next_allocation(log: str) -> list:
+	if log:
+		allocations = []
+		next = frappe.db.get_all(
+			"Process Payment Reconciliation Log Allocations",
+			filters={"parent": log, "reconciled": 0},
+			fields=["reference_type", "reference_name"],
+			order_by="idx",
+			limit=1,
+		)
+
+		if next:
+			allocations = frappe.db.get_all(
+				"Process Payment Reconciliation Log Allocations",
+				filters={
+					"parent": log,
+					"reconciled": 0,
+					"reference_type": next[0].reference_type,
+					"reference_name": next[0].reference_name,
+				},
+				fields=["*"],
+				order_by="idx",
+			)
+
+		return allocations
+	return []
+
+
+def fetch_and_allocate(doc: str) -> None:
+	"""
+	Fetch Invoices and Payments based on filters applied. FIFO ordering is used for allocation.
+	"""
+
+	if doc:
+		log = frappe.db.get_value("Process Payment Reconciliation Log", filters={"process_pr": doc})
+		if log:
+			if not frappe.db.get_value("Process Payment Reconciliation Log", log, "allocated"):
+				reconcile_log = frappe.get_doc("Process Payment Reconciliation Log", log)
+
+				pr = get_pr_instance(doc)
+				pr.get_unreconciled_entries()
+
+				if len(pr.invoices) > 0 and len(pr.payments) > 0:
+					invoices = [x.as_dict() for x in pr.invoices]
+					payments = [x.as_dict() for x in pr.payments]
+					pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+
+					for x in pr.get("allocation"):
+						reconcile_log.append(
+							"allocations",
+							x.as_dict().update(
+								{
+									"parenttype": "Process Payment Reconciliation Log",
+									"parent": reconcile_log.name,
+									"name": None,
+									"reconciled": False,
+								}
+							),
+						)
+				reconcile_log.allocated = True
+				reconcile_log.total_allocations = len(reconcile_log.get("allocations"))
+				reconcile_log.reconciled_entries = 0
+				reconcile_log.save()
+
+				# generate reconcile job name
+				allocation = get_next_allocation(log)
+				if allocation:
+					reconcile_job_name = (
+						f"process_{doc}_reconcile_allocation_{allocation[0].idx}_{allocation[-1].idx}"
+					)
+				else:
+					reconcile_job_name = f"process_{doc}_reconcile"
+
+				if not is_job_running(reconcile_job_name):
+					job = frappe.enqueue(
+						method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile",
+						queue="long",
+						timeout="3600",
+						is_async=True,
+						job_name=reconcile_job_name,
+						enqueue_after_commit=True,
+						doc=doc,
+					)
+
+
+def reconcile(doc: None | str = None) -> None:
+	if doc:
+		log = frappe.db.get_value("Process Payment Reconciliation Log", filters={"process_pr": doc})
+		if log:
+			res = frappe.get_all(
+				"Process Payment Reconciliation Log",
+				filters={"name": log},
+				fields=["reconciled_entries", "total_allocations"],
+				as_list=1,
+				limit=1,
+			)
+
+			reconciled_entries, total_allocations = res[0]
+			if reconciled_entries != total_allocations:
+				try:
+					# Fetch next allocation
+					allocations = get_next_allocation(log)
+
+					pr = get_pr_instance(doc)
+
+					# pass allocation to PR instance
+					for x in allocations:
+						pr.append("allocation", x)
+
+					# reconcile
+					pr.reconcile_allocations(skip_ref_details_update_for_pe=True)
+
+					# If Payment Entry, update details only for newly linked references
+					# This is for performance
+					if allocations[0].reference_type == "Payment Entry":
+
+						references = [(x.invoice_type, x.invoice_number) for x in allocations]
+						pe = frappe.get_doc(allocations[0].reference_type, allocations[0].reference_name)
+						pe.flags.ignore_validate_update_after_submit = True
+						pe.set_missing_ref_details(update_ref_details_only_for=references)
+						pe.save()
+
+					# Update reconciled flag
+					allocation_names = [x.name for x in allocations]
+					ppa = qb.DocType("Process Payment Reconciliation Log Allocations")
+					qb.update(ppa).set(ppa.reconciled, True).where(ppa.name.isin(allocation_names)).run()
+
+					# Update reconciled count
+					reconciled_count = frappe.db.count(
+						"Process Payment Reconciliation Log Allocations", filters={"parent": log, "reconciled": True}
+					)
+					frappe.db.set_value(
+						"Process Payment Reconciliation Log", log, "reconciled_entries", reconciled_count
+					)
+
+				except Exception as err:
+					# Update the parent doc about the exception
+					frappe.db.rollback()
+
+					traceback = frappe.get_traceback()
+					if traceback:
+						message = "Traceback: <br>" + traceback
+						frappe.db.set_value("Process Payment Reconciliation Log", log, "error_log", message)
+						frappe.db.set_value(
+							"Process Payment Reconciliation",
+							doc,
+							"error_log",
+							message,
+						)
+					if reconciled_entries and total_allocations and reconciled_entries < total_allocations:
+						frappe.db.set_value(
+							"Process Payment Reconciliation Log", log, "status", "Partially Reconciled"
+						)
+						frappe.db.set_value(
+							"Process Payment Reconciliation",
+							doc,
+							"status",
+							"Partially Reconciled",
+						)
+					else:
+						frappe.db.set_value("Process Payment Reconciliation Log", log, "status", "Failed")
+						frappe.db.set_value(
+							"Process Payment Reconciliation",
+							doc,
+							"status",
+							"Failed",
+						)
+				finally:
+					if reconciled_entries == total_allocations:
+						frappe.db.set_value("Process Payment Reconciliation Log", log, "status", "Reconciled")
+						frappe.db.set_value("Process Payment Reconciliation Log", log, "reconciled", True)
+						frappe.db.set_value("Process Payment Reconciliation", doc, "status", "Completed")
+					else:
+
+						if not (frappe.db.get_value("Process Payment Reconciliation", doc, "status") == "Paused"):
+							# trigger next batch in job
+							# generate reconcile job name
+							allocation = get_next_allocation(log)
+							if allocation:
+								reconcile_job_name = (
+									f"process_{doc}_reconcile_allocation_{allocation[0].idx}_{allocation[-1].idx}"
+								)
+							else:
+								reconcile_job_name = f"process_{doc}_reconcile"
+
+							if not is_job_running(reconcile_job_name):
+								job = frappe.enqueue(
+									method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile",
+									queue="long",
+									timeout="3600",
+									is_async=True,
+									job_name=reconcile_job_name,
+									enqueue_after_commit=True,
+									doc=doc,
+								)
+			else:
+				frappe.db.set_value("Process Payment Reconciliation Log", log, "status", "Reconciled")
+				frappe.db.set_value("Process Payment Reconciliation Log", log, "reconciled", True)
+				frappe.db.set_value("Process Payment Reconciliation", doc, "status", "Completed")
+
+
+@frappe.whitelist()
+def is_any_doc_running(for_filter: str | dict | None = None) -> str | None:
+	running_doc = None
+	if for_filter:
+		if type(for_filter) == str:
+			for_filter = frappe.json.loads(for_filter)
+
+		running_doc = frappe.db.get_value(
+			"Process Payment Reconciliation",
+			filters={
+				"docstatus": 1,
+				"status": ["in", ["Running", "Paused"]],
+				"company": for_filter.get("company"),
+				"party_type": for_filter.get("party_type"),
+				"party": for_filter.get("party"),
+				"receivable_payable_account": for_filter.get("receivable_payable_account"),
+			},
+			fieldname="name",
+		)
+	else:
+		running_doc = frappe.db.get_value(
+			"Process Payment Reconciliation", filters={"docstatus": 1, "status": "Running"}
+		)
+	return running_doc
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation_dashboard.py b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation_dashboard.py
new file mode 100644
index 0000000..784f454
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation_dashboard.py
@@ -0,0 +1,15 @@
+from frappe import _
+
+
+def get_data():
+	return {
+		"fieldname": "process_pr",
+		"transactions": [
+			{
+				"label": _("Reconciliation Logs"),
+				"items": [
+					"Process Payment Reconciliation Log",
+				],
+			},
+		],
+	}
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation_list.js b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation_list.js
new file mode 100644
index 0000000..8012d6e
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation_list.js
@@ -0,0 +1,15 @@
+frappe.listview_settings['Process Payment Reconciliation'] = {
+	add_fields: ["status"],
+	get_indicator: function(doc) {
+		let colors = {
+			'Queued': 'orange',
+			'Paused': 'orange',
+			'Completed': 'green',
+			'Partially Reconciled': 'orange',
+			'Running': 'blue',
+			'Failed': 'red',
+		};
+		let status = doc.status;
+		return [__(status), colors[status], 'status,=,'+status];
+	},
+};
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py
new file mode 100644
index 0000000..ad1e952
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestProcessPaymentReconciliation(FrappeTestCase):
+	pass
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/__init__.py b/erpnext/accounts/doctype/process_payment_reconciliation_log/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/__init__.py
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.js b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.js
new file mode 100644
index 0000000..2468f10
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.js
@@ -0,0 +1,17 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Process Payment Reconciliation Log", {
+	refresh(frm) {
+		if (['Completed', 'Running', 'Paused', 'Partially Reconciled'].find(x => x == frm.doc.status)) {
+			let progress = 0;
+			if (frm.doc.reconciled_entries != 0) {
+				progress = frm.doc.reconciled_entries / frm.doc.total_allocations * 100;
+			} else if(frm.doc.total_allocations == 0 && frm.doc.status == "Completed"){
+				progress = 100;
+			}
+			frm.dashboard.add_progress(__('Reconciliation Progress'), progress);
+		}
+
+	},
+});
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json
new file mode 100644
index 0000000..1131a0f
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json
@@ -0,0 +1,137 @@
+{
+ "actions": [],
+ "autoname": "format:PPR-LOG-{##}",
+ "beta": 1,
+ "creation": "2023-03-13 15:00:09.149681",
+ "default_view": "List",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "process_pr",
+  "section_break_fvdw",
+  "status",
+  "tasks_section",
+  "allocated",
+  "reconciled",
+  "column_break_yhin",
+  "total_allocations",
+  "reconciled_entries",
+  "section_break_4ywv",
+  "error_log",
+  "allocations_section",
+  "allocations"
+ ],
+ "fields": [
+  {
+   "fieldname": "allocations",
+   "fieldtype": "Table",
+   "label": "Allocations",
+   "options": "Process Payment Reconciliation Log Allocations",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "description": "All allocations have been successfully reconciled",
+   "fieldname": "reconciled",
+   "fieldtype": "Check",
+   "label": "Reconciled",
+   "read_only": 1
+  },
+  {
+   "fieldname": "total_allocations",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Total Allocations",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "description": "Invoices and Payments have been Fetched and Allocated",
+   "fieldname": "allocated",
+   "fieldtype": "Check",
+   "label": "Allocated",
+   "read_only": 1
+  },
+  {
+   "fieldname": "reconciled_entries",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Reconciled Entries",
+   "read_only": 1
+  },
+  {
+   "fieldname": "tasks_section",
+   "fieldtype": "Section Break",
+   "label": "Tasks"
+  },
+  {
+   "fieldname": "allocations_section",
+   "fieldtype": "Section Break",
+   "label": "Allocations"
+  },
+  {
+   "fieldname": "column_break_yhin",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_4ywv",
+   "fieldtype": "Section Break"
+  },
+  {
+   "depends_on": "eval:doc.error_log",
+   "fieldname": "error_log",
+   "fieldtype": "Long Text",
+   "label": "Reconciliation Error Log",
+   "read_only": 1
+  },
+  {
+   "fieldname": "process_pr",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Parent Document",
+   "options": "Process Payment Reconciliation",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_fvdw",
+   "fieldtype": "Section Break",
+   "label": "Status"
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "label": "Status",
+   "options": "Running\nPaused\nReconciled\nPartially Reconciled\nFailed\nCancelled",
+   "read_only": 1
+  }
+ ],
+ "in_create": 1,
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2023-04-21 17:36:26.642617",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Process Payment Reconciliation Log",
+ "naming_rule": "Expression",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "search_fields": "allocated, reconciled, total_allocations, reconciled_entries",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.py b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.py
new file mode 100644
index 0000000..85d70a4
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class ProcessPaymentReconciliationLog(Document):
+	pass
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log_list.js b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log_list.js
new file mode 100644
index 0000000..5a65204
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log_list.js
@@ -0,0 +1,15 @@
+frappe.listview_settings['Process Payment Reconciliation Log'] = {
+	add_fields: ["status"],
+	get_indicator: function(doc) {
+		var colors = {
+			'Partially Reconciled': 'orange',
+			'Paused': 'orange',
+			'Reconciled': 'green',
+			'Failed': 'red',
+			'Cancelled': 'red',
+			'Running': 'blue',
+		};
+		let status = doc.status;
+		return [__(status), colors[status], "status,=,"+status];
+	},
+};
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/test_process_payment_reconciliation_log.py b/erpnext/accounts/doctype/process_payment_reconciliation_log/test_process_payment_reconciliation_log.py
new file mode 100644
index 0000000..c2da62e
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/test_process_payment_reconciliation_log.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestProcessPaymentReconciliationLog(FrappeTestCase):
+	pass
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/__init__.py b/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/__init__.py
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.json b/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.json
new file mode 100644
index 0000000..b97d738
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.json
@@ -0,0 +1,170 @@
+{
+ "actions": [],
+ "creation": "2023-03-13 13:51:27.351463",
+ "default_view": "List",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "reference_type",
+  "reference_name",
+  "reference_row",
+  "column_break_3",
+  "invoice_type",
+  "invoice_number",
+  "section_break_6",
+  "allocated_amount",
+  "unreconciled_amount",
+  "column_break_8",
+  "amount",
+  "is_advance",
+  "section_break_5",
+  "difference_amount",
+  "column_break_7",
+  "difference_account",
+  "exchange_rate",
+  "currency",
+  "reconciled"
+ ],
+ "fields": [
+  {
+   "fieldname": "reference_type",
+   "fieldtype": "Link",
+   "label": "Reference Type",
+   "options": "DocType",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "reference_name",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "label": "Reference Name",
+   "options": "reference_type",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "reference_row",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Reference Row",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "invoice_type",
+   "fieldtype": "Link",
+   "label": "Invoice Type",
+   "options": "DocType",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "invoice_number",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "label": "Invoice Number",
+   "options": "invoice_type",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "allocated_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Allocated Amount",
+   "options": "currency",
+   "reqd": 1
+  },
+  {
+   "fieldname": "unreconciled_amount",
+   "fieldtype": "Currency",
+   "hidden": 1,
+   "label": "Unreconciled Amount",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_8",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "hidden": 1,
+   "label": "Amount",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "is_advance",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Is Advance",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_5",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "difference_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Difference Amount",
+   "options": "Currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_7",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "difference_account",
+   "fieldtype": "Link",
+   "label": "Difference Account",
+   "options": "Account",
+   "read_only": 1
+  },
+  {
+   "fieldname": "exchange_rate",
+   "fieldtype": "Float",
+   "label": "Exchange Rate",
+   "read_only": 1
+  },
+  {
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "hidden": 1,
+   "label": "Currency",
+   "options": "Currency"
+  },
+  {
+   "default": "0",
+   "fieldname": "reconciled",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Reconciled"
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2023-03-20 21:05:43.121945",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Process Payment Reconciliation Log Allocations",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.py b/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.py
new file mode 100644
index 0000000..c3e4329
--- /dev/null
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class ProcessPaymentReconciliationLogAllocations(Document):
+	pass
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
index b9680df..03abc93 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
@@ -15,7 +15,12 @@
 	</div>
 	<h2 class="text-center">{{ _("STATEMENTS OF ACCOUNTS") }}</h2>
 	<div>
-		<h5 style="float: left;">{{ _("Customer: ") }} <b>{{filters.party_name[0] }}</b></h5>
+		{% if filters.party[0] == filters.party_name[0] %}
+			<h5 style="float: left;">{{ _("Customer: ") }} <b>{{ filters.party_name[0] }}</b></h5>
+		{% else %}
+			<h5 style="float: left;">{{ _("Customer: ") }} <b>{{ filters.party[0] }}</b></h5>
+			<h5 style="float: left; margin-left:15px">{{ _("Customer Name: ") }} <b>{{filters.party_name[0] }}</b></h5>
+		{% endif %}
 		<h5 style="float: right;">
 			{{ _("Date: ") }}
 			<b>{{ frappe.format(filters.from_date, 'Date')}}
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
index 16602d3..e23620f 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
@@ -36,6 +36,8 @@
   "terms_and_conditions",
   "section_break_1",
   "enable_auto_email",
+  "column_break_ocfq",
+  "sender",
   "section_break_18",
   "frequency",
   "filter_duration",
@@ -298,10 +300,20 @@
    "fieldname": "show_net_values_in_party_account",
    "fieldtype": "Check",
    "label": "Show Net Values in Party Account"
+  },
+  {
+   "fieldname": "sender",
+   "fieldtype": "Link",
+   "label": "Sender",
+   "options": "Email Account"
+  },
+  {
+   "fieldname": "column_break_ocfq",
+   "fieldtype": "Column Break"
   }
  ],
  "links": [],
- "modified": "2022-11-10 17:44:17.165991",
+ "modified": "2023-04-26 12:46:43.645455",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Process Statement Of Accounts",
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 a482931..b36f33b 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
@@ -334,7 +334,7 @@
 				queue="short",
 				method=frappe.sendmail,
 				recipients=recipients,
-				sender=frappe.session.user,
+				sender=doc.sender or frappe.session.user,
 				cc=cc,
 				subject=subject,
 				message=message,
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json b/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json
index 8bffd6a..1749d72 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json
+++ b/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json
@@ -27,7 +27,7 @@
   },
   {
    "fieldname": "billing_email",
-   "fieldtype": "Read Only",
+   "fieldtype": "Data",
    "in_list_view": 1,
    "label": "Billing Email"
   },
@@ -41,7 +41,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2023-03-13 00:12:34.508086",
+ "modified": "2023-04-26 13:02:41.964499",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Process Statement Of Accounts Customer",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index b4d369e..f76dfff 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -89,6 +89,7 @@
   "column_break8",
   "grand_total",
   "rounding_adjustment",
+  "use_company_roundoff_cost_center",
   "rounded_total",
   "in_words",
   "total_advance",
@@ -1559,13 +1560,19 @@
    "fieldname": "only_include_allocated_payments",
    "fieldtype": "Check",
    "label": "Only Include Allocated Payments"
+  },
+  {
+   "default": "0",
+   "fieldname": "use_company_roundoff_cost_center",
+   "fieldtype": "Check",
+   "label": "Use Company Default Round Off Cost Center"
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 204,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-04-03 22:57:14.074982",
+ "modified": "2023-04-28 12:57:50.832598",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index a617447..868a150 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -978,7 +978,7 @@
 
 	def make_precision_loss_gl_entry(self, gl_entries):
 		round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
-			self.company, "Purchase Invoice", self.name
+			self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center
 		)
 
 		precision_loss = self.get("base_net_total") - flt(
@@ -992,7 +992,9 @@
 						"account": round_off_account,
 						"against": self.supplier,
 						"credit": precision_loss,
-						"cost_center": self.cost_center or round_off_cost_center,
+						"cost_center": round_off_cost_center
+						if self.use_company_roundoff_cost_center
+						else self.cost_center or round_off_cost_center,
 						"remarks": _("Net total calculation precision loss"),
 					}
 				)
@@ -1386,7 +1388,7 @@
 			not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment
 		):
 			round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
-				self.company, "Purchase Invoice", self.name
+				self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center
 			)
 
 			gl_entries.append(
@@ -1396,7 +1398,9 @@
 						"against": self.supplier,
 						"debit_in_account_currency": self.rounding_adjustment,
 						"debit": self.base_rounding_adjustment,
-						"cost_center": self.cost_center or round_off_cost_center,
+						"cost_center": round_off_cost_center
+						if self.use_company_roundoff_cost_center
+						else (self.cost_center or round_off_cost_center),
 					},
 					item=self,
 				)
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 56e412b..8cb2950 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -334,6 +334,7 @@
 	}
 
 	make_inter_company_invoice() {
+		let me = this;
 		frappe.model.open_mapped_doc({
 			method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_inter_company_purchase_invoice",
 			frm: me.frm
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index a41e13c..6a65b30 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -79,6 +79,7 @@
   "column_break5",
   "grand_total",
   "rounding_adjustment",
+  "use_company_roundoff_cost_center",
   "rounded_total",
   "in_words",
   "total_advance",
@@ -2135,6 +2136,12 @@
    "fieldname": "only_include_allocated_payments",
    "fieldtype": "Check",
    "label": "Only Include Allocated Payments"
+  },
+  {
+   "default": "0",
+   "fieldname": "use_company_roundoff_cost_center",
+   "fieldtype": "Check",
+   "label": "Use Company default Cost Center for Round off"
   }
  ],
  "icon": "fa fa-file-text",
@@ -2147,7 +2154,7 @@
    "link_fieldname": "consolidated_invoice"
   }
  ],
- "modified": "2023-04-03 22:55:14.206473",
+ "modified": "2023-04-28 14:15:59.901154",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index db61995..e16b1b1 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1464,7 +1464,7 @@
 			and not self.is_internal_transfer()
 		):
 			round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
-				self.company, "Sales Invoice", self.name
+				self.company, "Sales Invoice", self.name, self.use_company_roundoff_cost_center
 			)
 
 			gl_entries.append(
@@ -1476,7 +1476,9 @@
 							self.rounding_adjustment, self.precision("rounding_adjustment")
 						),
 						"credit": flt(self.base_rounding_adjustment, self.precision("base_rounding_adjustment")),
-						"cost_center": self.cost_center or round_off_cost_center,
+						"cost_center": round_off_cost_center
+						if self.use_company_roundoff_cost_center
+						else (self.cost_center or round_off_cost_center),
 					},
 					item=self,
 				)
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index f0146ea..ad3477e 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -215,7 +215,7 @@
 	}
 
 
-def get_lower_deduction_certificate(tax_details, pan_no):
+def get_lower_deduction_certificate(company, tax_details, pan_no):
 	ldc_name = frappe.db.get_value(
 		"Lower Deduction Certificate",
 		{
@@ -223,6 +223,7 @@
 			"tax_withholding_category": tax_details.tax_withholding_category,
 			"valid_from": (">=", tax_details.from_date),
 			"valid_upto": ("<=", tax_details.to_date),
+			"company": company,
 		},
 		"name",
 	)
@@ -255,7 +256,7 @@
 	tax_amount = 0
 
 	if party_type == "Supplier":
-		ldc = get_lower_deduction_certificate(tax_details, pan_no)
+		ldc = get_lower_deduction_certificate(inv.company, tax_details, pan_no)
 		if tax_deducted:
 			net_total = inv.tax_withholding_net_total
 			if ldc:
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 6b2546e..a929ff1 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -475,7 +475,9 @@
 			round_off_gle[dimension] = dimension_values.get(dimension)
 
 
-def get_round_off_account_and_cost_center(company, voucher_type, voucher_no):
+def get_round_off_account_and_cost_center(
+	company, voucher_type, voucher_no, use_company_default=False
+):
 	round_off_account, round_off_cost_center = frappe.get_cached_value(
 		"Company", company, ["round_off_account", "round_off_cost_center"]
 	) or [None, None]
@@ -483,7 +485,7 @@
 	meta = frappe.get_meta(voucher_type)
 
 	# Give first preference to parent cost center for round off GLE
-	if meta.has_field("cost_center"):
+	if not use_company_default and meta.has_field("cost_center"):
 		parent_cost_center = frappe.db.get_value(voucher_type, voucher_no, "cost_center")
 		if parent_cost_center:
 			round_off_cost_center = parent_cost_center
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index ac9368e..7747042 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -259,6 +259,8 @@
 	)
 
 	if doctype in TRANSACTION_TYPES:
+		# required to set correct region
+		frappe.flags.company = company
 		get_regional_address_details(party_details, doctype, company)
 
 	return party_address, shipping_address
diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py
index 5827697..d67eee3 100644
--- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py
+++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py
@@ -114,28 +114,6 @@
 			   sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
 			   sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
 		from (SELECT a.asset_category,
-				   ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
-								   ds.depreciation_amount
-							  else
-								   0
-							  end), 0) as accumulated_depreciation_as_on_from_date,
-				   ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
-										and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then
-								   ds.depreciation_amount
-							  else
-								   0
-							  end), 0) as depreciation_eliminated_during_the_period,
-				   ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s
-										and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then
-								   ds.depreciation_amount
-							  else
-								   0
-							  end), 0) as depreciation_amount_during_the_period
-			from `tabAsset` a, `tabAsset Depreciation Schedule` ads, `tabDepreciation Schedule` ds
-			where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and ads.asset = a.name and ads.docstatus=1 and ads.name = ds.parent and ifnull(ds.journal_entry, '') != ''
-			group by a.asset_category
-			union
-			SELECT a.asset_category,
 				   ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
 								   gle.debit
 							  else
@@ -160,7 +138,7 @@
 				aca.parent = a.asset_category and aca.company_name = %(company)s
 			join `tabCompany` company on
 				company.name = %(company)s
-			where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
+			where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
 			group by a.asset_category
 			union
 			SELECT a.asset_category,
diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
index 306af72..2d68bb7 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
@@ -80,7 +80,7 @@
 	payment_entries = frappe.db.sql(
 		"""SELECT
 			"Payment Entry", name, posting_date, reference_no, clearance_date, party,
-			if(paid_from=%(account)s, paid_amount * -1, received_amount)
+			if(paid_from=%(account)s, ((paid_amount * -1) - total_taxes_and_charges) , received_amount)
 		FROM
 			`tabPayment Entry`
 		WHERE
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index debe655..76a01db 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -538,13 +538,20 @@
 			query = query.where(gl_entry.cost_center.isin(filters.cost_center))
 
 		if filters.get("include_default_book_entries"):
+			company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
+
+			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 Entries'")
+				)
+
 			query = query.where(
-				(gl_entry.finance_book.isin([cstr(filters.finance_book), cstr(filters.company_fb), ""]))
+				(gl_entry.finance_book.isin([cstr(filters.finance_book), cstr(company_fb)]))
 				| (gl_entry.finance_book.isnull())
 			)
 		else:
 			query = query.where(
-				(gl_entry.finance_book.isin([cstr(filters.company_fb), ""])) | (gl_entry.finance_book.isnull())
+				(gl_entry.finance_book.isin([cstr(filters.finance_book)])) | (gl_entry.finance_book.isnull())
 			)
 
 	if accounting_dimensions:
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 2100f26..57a9091 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -176,7 +176,8 @@
 		{
 			"fieldname": "include_default_book_entries",
 			"label": __("Include Default Book Entries"),
-			"fieldtype": "Check"
+			"fieldtype": "Check",
+			"default": 1
 		},
 		{
 			"fieldname": "show_cancelled_entries",
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 27b84c4..0b05c11 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -244,13 +244,23 @@
 	if filters.get("project"):
 		conditions.append("project in %(project)s")
 
-	if filters.get("finance_book"):
-		if filters.get("include_default_book_entries"):
-			conditions.append(
-				"(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)"
-			)
+	if filters.get("include_default_book_entries"):
+		if filters.get("finance_book"):
+			if filters.get("company_fb") and cstr(filters.get("finance_book")) != cstr(
+				filters.get("company_fb")
+			):
+				frappe.throw(
+					_("To use a different finance book, please uncheck 'Include Default Book Entries'")
+				)
+			else:
+				conditions.append("(finance_book in (%(finance_book)s) OR finance_book IS NULL)")
 		else:
-			conditions.append("finance_book in (%(finance_book)s)")
+			conditions.append("(finance_book in (%(company_fb)s) OR finance_book IS NULL)")
+	else:
+		if filters.get("finance_book"):
+			conditions.append("(finance_book in (%(finance_book)s) OR finance_book IS NULL)")
+		else:
+			conditions.append("(finance_book IS NULL)")
 
 	if not filters.get("show_cancelled_entries"):
 		conditions.append("is_cancelled = 0")
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js
index e89d429..53921dc 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.js
+++ b/erpnext/accounts/report/gross_profit/gross_profit.js
@@ -73,7 +73,7 @@
 		if (column.fieldname == "sales_invoice" && column.options == "Item" && data && data.indent == 0) {
 			column._options = "Sales Invoice";
 		} else {
-			column._options = "Item";
+			column._options = "";
 		}
 		value = default_formatter(value, row, column, data);
 
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 01fee28..81f5928 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -250,7 +250,7 @@
 				"label": _("Warehouse"),
 				"fieldname": "warehouse",
 				"fieldtype": "Link",
-				"options": "warehouse",
+				"options": "Warehouse",
 				"width": 100,
 			},
 			"qty": {"label": _("Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 80},
@@ -305,7 +305,8 @@
 			"sales_person": {
 				"label": _("Sales Person"),
 				"fieldname": "sales_person",
-				"fieldtype": "Data",
+				"fieldtype": "Link",
+				"options": "Sales Person",
 				"width": 100,
 			},
 			"allocated_amount": {
@@ -326,14 +327,14 @@
 				"label": _("Customer Group"),
 				"fieldname": "customer_group",
 				"fieldtype": "Link",
-				"options": "customer",
+				"options": "Customer Group",
 				"width": 100,
 			},
 			"territory": {
 				"label": _("Territory"),
 				"fieldname": "territory",
 				"fieldtype": "Link",
-				"options": "territory",
+				"options": "Territory",
 				"width": 100,
 			},
 			"monthly": {
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
index bfe2a0f..9883890 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
@@ -4,7 +4,6 @@
 
 import frappe
 from frappe import _
-from frappe.utils import flt
 
 
 def execute(filters=None):
@@ -66,12 +65,6 @@
 			else:
 				total_amount_credited += entry.credit
 
-		## Check if ldc is applied and show rate as per ldc
-		actual_rate = (tds_deducted / total_amount_credited) * 100
-
-		if flt(actual_rate) < flt(rate):
-			rate = actual_rate
-
 		if tds_deducted:
 			row = {
 				"pan"
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 53611ab..57dac2a 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -248,13 +248,20 @@
 		opening_balance = opening_balance.where(closing_balance.project == filters.project)
 
 	if filters.get("include_default_book_entries"):
+		company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
+
+		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 Entries'")
+			)
+
 		opening_balance = opening_balance.where(
-			(closing_balance.finance_book.isin([cstr(filters.finance_book), cstr(filters.company_fb), ""]))
+			(closing_balance.finance_book.isin([cstr(filters.finance_book), cstr(company_fb)]))
 			| (closing_balance.finance_book.isnull())
 		)
 	else:
 		opening_balance = opening_balance.where(
-			(closing_balance.finance_book.isin([cstr(filters.finance_book), ""]))
+			(closing_balance.finance_book.isin([cstr(filters.finance_book)]))
 			| (closing_balance.finance_book.isnull())
 		)
 
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 2ab9ef6..0ee06e8 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -436,7 +436,7 @@
 	return cc.name
 
 
-def reconcile_against_document(args):  # nosemgrep
+def reconcile_against_document(args, skip_ref_details_update_for_pe=False):  # nosemgrep
 	"""
 	Cancel PE or JV, Update against document, split if required and resubmit
 	"""
@@ -465,7 +465,9 @@
 			if voucher_type == "Journal Entry":
 				update_reference_in_journal_entry(entry, doc, do_not_save=True)
 			else:
-				update_reference_in_payment_entry(entry, doc, do_not_save=True)
+				update_reference_in_payment_entry(
+					entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe
+				)
 
 		doc.save(ignore_permissions=True)
 		# re-submit advance entry
@@ -602,7 +604,9 @@
 		journal_entry.save(ignore_permissions=True)
 
 
-def update_reference_in_payment_entry(d, payment_entry, do_not_save=False):
+def update_reference_in_payment_entry(
+	d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False
+):
 	reference_details = {
 		"reference_doctype": d.against_voucher_type,
 		"reference_name": d.against_voucher,
@@ -646,6 +650,8 @@
 	payment_entry.flags.ignore_validate_update_after_submit = True
 	payment_entry.setup_party_account_field()
 	payment_entry.set_missing_values()
+	if not skip_ref_details_update_for_pe:
+		payment_entry.set_missing_ref_details()
 	payment_entry.set_amounts()
 
 	if not do_not_save:
@@ -1368,10 +1374,7 @@
 		if wh_details.account == account and not wh_details.is_group
 	]
 
-	total_stock_value = 0.0
-	for warehouse in related_warehouses:
-		value = get_stock_value_on(warehouse, posting_date)
-		total_stock_value += value
+	total_stock_value = get_stock_value_on(related_warehouses, posting_date)
 
 	precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
 	return flt(account_balance, precision), flt(total_stock_value, precision), related_warehouses
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 6001254..42f5311 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -783,7 +783,7 @@
 	je.voucher_type = "Depreciation Entry"
 	je.naming_series = depreciation_series
 	je.company = asset.company
-	je.remark = "Depreciation Entry against asset {0}".format(asset_name)
+	je.remark = _("Depreciation Entry against asset {0}").format(asset_name)
 
 	je.append(
 		"accounts",
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index 028e3d6..f23ae2f 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -157,6 +157,7 @@
 			je.append("accounts", debit_entry)
 
 			je.flags.ignore_permissions = True
+			je.flags.planned_depr_entry = True
 			je.save()
 			if not je.meta.get_workflow():
 				je.submit()
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index cde0280..203612f 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -1511,7 +1511,7 @@
 		)
 
 		self.assertEqual(asset.status, "Submitted")
-		self.assertEqual(asset.get("value_after_depreciation"), 100000)
+		self.assertEqual(asset.get_value_after_depreciation(), 100000)
 
 		jv = make_journal_entry(
 			"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
@@ -1524,12 +1524,68 @@
 		jv.submit()
 
 		asset.reload()
-		self.assertEqual(asset.get("value_after_depreciation"), 99900)
+		self.assertEqual(asset.get_value_after_depreciation(), 99900)
 
 		jv.cancel()
 
 		asset.reload()
-		self.assertEqual(asset.get("value_after_depreciation"), 100000)
+		self.assertEqual(asset.get_value_after_depreciation(), 100000)
+
+	def test_manual_depreciation_for_depreciable_asset(self):
+		asset = create_asset(
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			purchase_date="2020-01-30",
+			available_for_use_date="2020-01-30",
+			expected_value_after_useful_life=10000,
+			total_number_of_depreciations=10,
+			frequency_of_depreciation=1,
+			submit=1,
+		)
+
+		self.assertEqual(asset.status, "Submitted")
+		self.assertEqual(asset.get_value_after_depreciation(), 100000)
+
+		jv = make_journal_entry(
+			"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
+		)
+		for d in jv.accounts:
+			d.reference_type = "Asset"
+			d.reference_name = asset.name
+		jv.voucher_type = "Depreciation Entry"
+		jv.insert()
+		jv.submit()
+
+		asset.reload()
+		self.assertEqual(asset.get_value_after_depreciation(), 99900)
+
+		jv.cancel()
+
+		asset.reload()
+		self.assertEqual(asset.get_value_after_depreciation(), 100000)
+
+	def test_manual_depreciation_with_incorrect_jv_voucher_type(self):
+		asset = create_asset(
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			purchase_date="2020-01-30",
+			available_for_use_date="2020-01-30",
+			expected_value_after_useful_life=10000,
+			total_number_of_depreciations=10,
+			frequency_of_depreciation=1,
+			submit=1,
+		)
+
+		jv = make_journal_entry(
+			"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
+		)
+		for d in jv.accounts:
+			d.reference_type = "Asset"
+			d.reference_name = asset.name
+			d.account_type = "Depreciation"
+		jv.voucher_type = "Journal Entry"
+
+		self.assertRaises(frappe.ValidationError, jv.insert)
 
 
 def create_asset_data():
diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
index 0213328..8426ed4 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
@@ -150,7 +150,9 @@
 			if d.depreciation_method in ("Straight Line", "Manual"):
 				end_date = max(s.schedule_date for s in depr_schedule)
 				total_days = date_diff(end_date, self.date)
-				rate_per_day = flt(d.value_after_depreciation) / flt(total_days)
+				rate_per_day = flt(d.value_after_depreciation - d.expected_value_after_useful_life) / flt(
+					total_days
+				)
 				from_date = self.date
 			else:
 				no_of_depreciations = len([s.name for s in depr_schedule if not s.journal_entry])
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 65a4226..4f7b836 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
@@ -94,11 +94,11 @@
 			label: __("Finance Book"),
 			fieldtype: "Link",
 			options: "Finance Book",
-			depends_on: "eval: doc.only_depreciable_assets == 1",
+			depends_on: "eval: doc.filter_by_finance_book == 1",
 		},
 		{
-			fieldname:"only_depreciable_assets",
-			label: __("Only depreciable assets"),
+			fieldname:"filter_by_finance_book",
+			label: __("Filter by Finance Book"),
 			fieldtype: "Check"
 		},
 		{
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 5fbcbe2..984b3fd 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -45,8 +45,6 @@
 		filters.year_end_date = getdate(fiscal_year.year_end_date)
 
 		conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]]
-	if filters.get("only_depreciable_assets"):
-		conditions["calculate_depreciation"] = filters.get("only_depreciable_assets")
 	if filters.get("only_existing_assets"):
 		conditions["is_existing_asset"] = filters.get("only_existing_assets")
 	if filters.get("asset_category"):
@@ -106,7 +104,7 @@
 
 	assets_linked_to_fb = None
 
-	if filters.only_depreciable_assets:
+	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")},
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 29afc84..ff08ddd 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -495,6 +495,7 @@
    "allow_bulk_edit": 1,
    "fieldname": "items",
    "fieldtype": "Table",
+   "label": "Items",
    "oldfieldname": "po_details",
    "oldfieldtype": "Table",
    "options": "Purchase Order Item",
@@ -1100,8 +1101,7 @@
   {
    "fieldname": "before_items_section",
    "fieldtype": "Section Break",
-   "hide_border": 1,
-   "label": "Items"
+   "hide_border": 1
   },
   {
    "fieldname": "items_col_break",
@@ -1271,7 +1271,7 @@
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-01-28 18:59:16.322824",
+ "modified": "2023-04-14 16:42:29.448464",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_list.js b/erpnext/buying/doctype/purchase_order/purchase_order_list.js
index d7907e4..6594746 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order_list.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order_list.js
@@ -43,7 +43,7 @@
 		});
 
 		listview.page.add_action_item(__("Advance Payment"), ()=>{
-			erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Advance Payment");
+			erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Payment Entry");
 		});
 
 	}
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index c5b369b..11ff91a 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -310,7 +310,6 @@
    "fieldname": "items_section",
    "fieldtype": "Section Break",
    "hide_border": 1,
-   "label": "Items",
    "oldfieldtype": "Section Break",
    "options": "fa fa-shopping-cart"
   },
@@ -318,6 +317,7 @@
    "allow_bulk_edit": 1,
    "fieldname": "items",
    "fieldtype": "Table",
+   "label": "Items",
    "oldfieldname": "po_details",
    "oldfieldtype": "Table",
    "options": "Supplier Quotation Item",
@@ -844,7 +844,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-12-12 18:35:39.740974",
+ "modified": "2023-04-14 16:43:41.714832",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier Quotation",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index c741622..d0ec654 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -5,7 +5,7 @@
 import json
 
 import frappe
-from frappe import _, throw
+from frappe import _, bold, throw
 from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied
 from frappe.query_builder.functions import Abs, Sum
 from frappe.utils import (
@@ -405,6 +405,15 @@
 				msg += _("Please create purchase from internal sale or delivery document itself")
 				frappe.throw(msg, title=_("Internal Sales Reference Missing"))
 
+			label = "Delivery Note Item" if self.doctype == "Purchase Receipt" else "Sales Invoice Item"
+
+			field = frappe.scrub(label)
+
+			for row in self.get("items"):
+				if not row.get(field):
+					msg = f"At Row {row.idx}: The field {bold(label)} is mandatory for internal transfer"
+					frappe.throw(_(msg), title=_("Internal Transfer Reference Missing"))
+
 	def disable_pricing_rule_on_internal_transfer(self):
 		if not self.get("ignore_pricing_rule") and self.is_internal_transfer():
 			self.ignore_pricing_rule = 1
@@ -1653,7 +1662,10 @@
 				)
 				self.append("payment_schedule", data)
 
-		if not automatically_fetch_payment_terms:
+		if not (
+			automatically_fetch_payment_terms
+			and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype)
+		):
 			for d in self.get("payment_schedule"):
 				if d.invoice_portion:
 					d.payment_amount = flt(
@@ -1892,12 +1904,14 @@
 		reconcilation_entry.party = secondary_party
 		reconcilation_entry.reference_type = self.doctype
 		reconcilation_entry.reference_name = self.name
-		reconcilation_entry.cost_center = self.cost_center
+		reconcilation_entry.cost_center = self.cost_center or erpnext.get_default_cost_center(
+			self.company
+		)
 
 		advance_entry.account = primary_account
 		advance_entry.party_type = primary_party_type
 		advance_entry.party = primary_party
-		advance_entry.cost_center = self.cost_center
+		advance_entry.cost_center = self.cost_center or erpnext.get_default_cost_center(self.company)
 		advance_entry.is_advance = "Yes"
 
 		if self.doctype == "Sales Invoice":
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index b0cf724..799fed9 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -576,7 +576,9 @@
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
-def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters):
+def get_filtered_dimensions(
+	doctype, txt, searchfield, start, page_len, filters, reference_doctype=None
+):
 	from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
 		get_dimension_filter_map,
 	)
@@ -617,7 +619,12 @@
 		query_filters.append(["name", query_selector, dimensions])
 
 	output = frappe.get_list(
-		doctype, fields=fields, filters=query_filters, or_filters=or_filters, as_list=1
+		doctype,
+		fields=fields,
+		filters=query_filters,
+		or_filters=or_filters,
+		as_list=1,
+		reference_doctype=reference_doctype,
 	)
 
 	return [tuple(d) for d in set(output)]
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 479fef7..a27e348 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -329,9 +329,10 @@
 		"""Create batches if required. Called before submit"""
 		for d in self.items:
 			if d.get(warehouse_field) and not d.batch_no:
-				has_batch_no, create_new_batch = frappe.db.get_value(
+				has_batch_no, create_new_batch = frappe.get_cached_value(
 					"Item", d.item_code, ["has_batch_no", "create_new_batch"]
 				)
+
 				if has_batch_no and create_new_batch:
 					d.batch_no = (
 						frappe.get_doc(
@@ -414,7 +415,7 @@
 				"voucher_no": self.name,
 				"voucher_detail_no": d.name,
 				"actual_qty": (self.docstatus == 1 and 1 or -1) * flt(d.get("stock_qty")),
-				"stock_uom": frappe.db.get_value(
+				"stock_uom": frappe.get_cached_value(
 					"Item", args.get("item_code") or d.get("item_code"), "stock_uom"
 				),
 				"incoming_rate": 0,
@@ -609,7 +610,7 @@
 	def validate_customer_provided_item(self):
 		for d in self.get("items"):
 			# Customer Provided parts will have zero valuation rate
-			if frappe.db.get_value("Item", d.item_code, "is_customer_provided_item"):
+			if frappe.get_cached_value("Item", d.item_code, "is_customer_provided_item"):
 				d.allow_zero_valuation_rate = 1
 
 	def set_rate_of_stock_uom(self):
@@ -722,7 +723,7 @@
 		message += _("Please adjust the qty or edit {0} to proceed.").format(rule_link)
 		return message
 
-	def repost_future_sle_and_gle(self):
+	def repost_future_sle_and_gle(self, force=False):
 		args = frappe._dict(
 			{
 				"posting_date": self.posting_date,
@@ -733,7 +734,7 @@
 			}
 		)
 
-		if future_sle_exists(args) or repost_required_for_queue(self):
+		if force or future_sle_exists(args) or repost_required_for_queue(self):
 			item_based_reposting = cint(
 				frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting")
 			)
@@ -894,9 +895,6 @@
 	)
 
 	for d in data:
-		if key not in frappe.local.future_sle:
-			frappe.local.future_sle[key] = frappe._dict({})
-
 		frappe.local.future_sle[key][(d.item_code, d.warehouse)] = d.total_row
 
 	return len(data)
@@ -919,7 +917,7 @@
 
 def get_cached_data(args, key):
 	if key not in frappe.local.future_sle:
-		return False
+		frappe.local.future_sle[key] = frappe._dict({})
 
 	if args.get("item_code"):
 		item_key = (args.get("item_code"), args.get("warehouse"))
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 1edd7bf..4661c5c 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -976,6 +976,8 @@
 
 @frappe.whitelist()
 def get_round_off_applicable_accounts(company, account_list):
+	# required to set correct region
+	frappe.flags.company = company
 	account_list = get_regional_round_off_accounts(company, account_list)
 
 	return account_list
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index 6a5fead..2a8d65f 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -59,7 +59,7 @@
 			if not self.get(field) and frappe.db.field_exists(self.opportunity_from, field):
 				try:
 					value = frappe.db.get_value(self.opportunity_from, self.party_name, field)
-					self.db_set(field, value)
+					self.set(field, value)
 				except Exception:
 					continue
 
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.py b/erpnext/e_commerce/doctype/website_item/website_item.py
index 3e5d5f7..81b8eca 100644
--- a/erpnext/e_commerce/doctype/website_item/website_item.py
+++ b/erpnext/e_commerce/doctype/website_item/website_item.py
@@ -315,6 +315,7 @@
 			self.item_code, skip_quotation_creation=True
 		)
 
+	@frappe.whitelist()
 	def copy_specification_from_item_group(self):
 		self.set("website_specifications", [])
 		if self.item_group:
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 862a546..02b301e 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -362,6 +362,7 @@
 	"cron": {
 		"0/15 * * * *": [
 			"erpnext.manufacturing.doctype.bom_update_log.bom_update_log.resume_bom_cost_update_jobs",
+			"erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.trigger_reconciliation_for_queued_docs",
 		],
 		"0/30 * * * *": [
 			"erpnext.utilities.doctype.video.video.update_youtube_data",
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index 4304193..ad9aafe 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -411,7 +411,6 @@
 		}
 
 		frm.set_value("process_loss_qty", qty);
-		frm.set_value("add_process_loss_cost_in_fg", qty ? 1: 0);
 	}
 });
 
@@ -653,7 +652,7 @@
 
 frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) {
 	var d = locals[cdt][cdn];
-
+	if(!d.workstation) return;
 	frappe.call({
 		"method": "frappe.client.get",
 		args: {
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index a085af8..b53149a 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -1317,7 +1317,7 @@
 		if not field in searchfields
 	]
 
-	query_filters = {"disabled": 0, "end_of_life": (">", today())}
+	query_filters = {"disabled": 0, "ifnull(end_of_life, '3099-12-31')": (">", today())}
 
 	or_cond_filters = {}
 	if txt:
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index e82f379..877362d 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -74,6 +74,52 @@
 		self.update_sub_operation_status()
 		self.validate_work_order()
 
+	def on_update(self):
+		self.validate_job_card_qty()
+
+	def validate_job_card_qty(self):
+		if not (self.operation_id and self.work_order):
+			return
+
+		wo_qty = flt(frappe.get_cached_value("Work Order", self.work_order, "qty"))
+
+		completed_qty = flt(
+			frappe.db.get_value("Work Order Operation", self.operation_id, "completed_qty")
+		)
+
+		over_production_percentage = flt(
+			frappe.db.get_single_value("Manufacturing Settings", "overproduction_percentage_for_work_order")
+		)
+
+		wo_qty = wo_qty + (wo_qty * over_production_percentage / 100)
+
+		job_card_qty = frappe.get_all(
+			"Job Card",
+			fields=["sum(for_quantity)"],
+			filters={
+				"work_order": self.work_order,
+				"operation_id": self.operation_id,
+				"docstatus": ["!=", 2],
+			},
+			as_list=1,
+		)
+
+		job_card_qty = flt(job_card_qty[0][0]) if job_card_qty else 0
+
+		if job_card_qty and ((job_card_qty - completed_qty) > wo_qty):
+			form_link = get_link_to_form("Manufacturing Settings", "Manufacturing Settings")
+
+			msg = f"""
+				Qty To Manufacture in the job card
+				cannot be greater than Qty To Manufacture in the
+				work order for the operation {bold(self.operation)}.
+				<br><br><b>Solution: </b> Either you can reduce the
+				Qty To Manufacture in the job card or set the
+				'Overproduction Percentage For Work Order'
+				in the {form_link}."""
+
+			frappe.throw(_(msg), title=_("Extra Job Card Quantity"))
+
 	def set_sub_operations(self):
 		if not self.sub_operations and self.operation:
 			self.sub_operations = []
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
index 8c61d54..09bf1d8 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
@@ -16,6 +16,7 @@
   "column_break_4",
   "quantity",
   "uom",
+  "conversion_factor",
   "projected_qty",
   "reserved_qty_for_production",
   "safety_stock",
@@ -169,11 +170,17 @@
    "label": "Qty As Per BOM",
    "no_copy": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "conversion_factor",
+   "fieldtype": "Float",
+   "label": "Conversion Factor",
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2022-11-26 14:59:25.879631",
+ "modified": "2023-05-03 12:43:29.895754",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Material Request Plan Item",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 62715e6..ab7aa52 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -336,10 +336,6 @@
 	},
 
 	get_items_for_material_requests(frm, warehouses) {
-		let set_fields = ['actual_qty', 'item_code','item_name', 'description', 'uom', 'from_warehouse',
-			'min_order_qty', 'required_bom_qty', 'quantity', 'sales_order', 'warehouse', 'projected_qty', 'ordered_qty',
-			'reserved_qty_for_production', 'material_request_type'];
-
 		frappe.call({
 			method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_items_for_material_requests",
 			freeze: true,
@@ -352,11 +348,11 @@
 					frm.set_value('mr_items', []);
 					r.message.forEach(row => {
 						let d = frm.add_child('mr_items');
-						set_fields.forEach(field => {
-							if (row[field]) {
+						for (let field in row) {
+							if (field !== 'name') {
 								d[field] = row[field];
 							}
-						});
+						}
 					});
 				}
 				refresh_field('mr_items');
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 0cc0f80..df50cbf 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -28,6 +28,7 @@
 from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
 from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
 from erpnext.stock.get_item_details import get_conversion_factor
+from erpnext.stock.utils import get_or_make_bin
 from erpnext.utilities.transaction_base import validate_uom_is_integer
 
 
@@ -398,9 +399,20 @@
 		self.set_status()
 		self.db_set("status", self.status)
 
+	def on_submit(self):
+		self.update_bin_qty()
+
 	def on_cancel(self):
 		self.db_set("status", "Cancelled")
 		self.delete_draft_work_order()
+		self.update_bin_qty()
+
+	def update_bin_qty(self):
+		for d in self.mr_items:
+			if d.warehouse:
+				bin_name = get_or_make_bin(d.item_code, d.warehouse)
+				bin = frappe.get_doc("Bin", bin_name, for_update=True)
+				bin.update_reserved_qty_for_production_plan()
 
 	def delete_draft_work_order(self):
 		for d in frappe.get_all(
@@ -1068,6 +1080,7 @@
 			"item_code": row.item_code,
 			"item_name": row.item_name,
 			"quantity": required_qty / conversion_factor,
+			"conversion_factor": conversion_factor,
 			"required_bom_qty": total_qty,
 			"stock_uom": row.get("stock_uom"),
 			"warehouse": warehouse
@@ -1474,3 +1487,34 @@
 	for field in ["wip_warehouse", "fg_warehouse"]:
 		if not row.get(field):
 			row[field] = default_warehouses.get(field)
+
+
+def get_reserved_qty_for_production_plan(item_code, warehouse):
+	from erpnext.manufacturing.doctype.work_order.work_order import get_reserved_qty_for_production
+
+	table = frappe.qb.DocType("Production Plan")
+	child = frappe.qb.DocType("Material Request Plan Item")
+
+	query = (
+		frappe.qb.from_(table)
+		.inner_join(child)
+		.on(table.name == child.parent)
+		.select(Sum(child.quantity * IfNull(child.conversion_factor, 1.0)))
+		.where(
+			(table.docstatus == 1)
+			& (child.item_code == item_code)
+			& (child.warehouse == warehouse)
+			& (table.status.notin(["Completed", "Closed"]))
+		)
+	).run()
+
+	if not query:
+		return 0.0
+
+	reserved_qty_for_production_plan = flt(query[0][0])
+
+	reserved_qty_for_production = flt(
+		get_reserved_qty_for_production(item_code, warehouse, check_production_plan=True)
+	)
+
+	return reserved_qty_for_production_plan - reserved_qty_for_production
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 2bf14c2..91864d0 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -868,6 +868,27 @@
 		for item_code in mr_items:
 			self.assertTrue(item_code in validate_mr_items)
 
+	def test_resered_qty_for_production_plan_for_material_requests(self):
+		from erpnext.stock.utils import get_or_make_bin
+
+		bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
+		before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+
+		pln = create_production_plan(item_code="Test Production Item 1")
+
+		bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
+		after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+
+		self.assertEqual(after_qty - before_qty, 1)
+
+		pln = frappe.get_doc("Production Plan", pln.name)
+		pln.cancel()
+
+		bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
+		after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+
+		self.assertEqual(after_qty, before_qty)
+
 
 def create_production_plan(**args):
 	"""
diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js
index b480c70..784e83a 100644
--- a/erpnext/manufacturing/doctype/routing/routing.js
+++ b/erpnext/manufacturing/doctype/routing/routing.js
@@ -50,7 +50,7 @@
 
 	workstation: function(frm, cdt, cdn) {
 		const d = locals[cdt][cdn];
-
+		if(!d.workstation) return;
 		frappe.call({
 			"method": "frappe.client.get",
 			args: {
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 729ed42..bb53c8c 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -1598,6 +1598,65 @@
 			self.assertEqual(row.to_time, add_to_date(planned_start_date, minutes=30))
 			self.assertEqual(row.workstation, workstations_to_check[index])
 
+	def test_job_card_extra_qty(self):
+		items = [
+			"Test FG Item for Scrap Item Test 1",
+			"Test RM Item 1 for Scrap Item Test 1",
+			"Test RM Item 2 for Scrap Item Test 1",
+		]
+
+		company = "_Test Company with perpetual inventory"
+		for item_code in items:
+			create_item(
+				item_code=item_code,
+				is_stock_item=1,
+				is_purchase_item=1,
+				opening_stock=100,
+				valuation_rate=10,
+				company=company,
+				warehouse="Stores - TCP1",
+			)
+
+		item = "Test FG Item for Scrap Item Test 1"
+		raw_materials = ["Test RM Item 1 for Scrap Item Test 1", "Test RM Item 2 for Scrap Item Test 1"]
+		if not frappe.db.get_value("BOM", {"item": item}):
+			bom = make_bom(
+				item=item, source_warehouse="Stores - TCP1", raw_materials=raw_materials, do_not_save=True
+			)
+			bom.with_operations = 1
+			bom.append(
+				"operations",
+				{
+					"operation": "_Test Operation 1",
+					"workstation": "_Test Workstation 1",
+					"hour_rate": 20,
+					"time_in_mins": 60,
+				},
+			)
+
+			bom.submit()
+
+		wo_order = make_wo_order_test_record(
+			item=item,
+			company=company,
+			planned_start_date=now(),
+			qty=20,
+		)
+		job_card = frappe.db.get_value("Job Card", {"work_order": wo_order.name}, "name")
+		job_card_doc = frappe.get_doc("Job Card", job_card)
+
+		# Make another Job Card for the same Work Order
+		job_card2 = frappe.copy_doc(job_card_doc)
+		self.assertRaises(frappe.ValidationError, job_card2.save)
+
+		frappe.db.set_single_value(
+			"Manufacturing Settings", "overproduction_percentage_for_work_order", 100
+		)
+
+		job_card2 = frappe.copy_doc(job_card_doc)
+		job_card2.time_logs = []
+		job_card2.save()
+
 
 def prepare_data_for_workstation_type_check():
 	from erpnext.manufacturing.doctype.operation.test_operation import make_operation
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 97480b2..d0c9966 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -625,20 +625,18 @@
 								// all materials transferred for manufacturing, make this primary
 								finish_btn.addClass('btn-primary');
 							}
-						} else {
-							frappe.db.get_doc("Manufacturing Settings").then((doc) => {
-								let allowance_percentage = doc.overproduction_percentage_for_work_order;
+						} else if (frm.doc.__onload && frm.doc.__onload.overproduction_percentage) {
+							let allowance_percentage = frm.doc.__onload.overproduction_percentage;
 
-								if (allowance_percentage > 0) {
-									let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty);
+							if (allowance_percentage > 0) {
+								let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty);
 
-									if ((flt(doc.produced_qty) < allowed_qty)) {
-										frm.add_custom_button(__('Finish'), function() {
-											erpnext.work_order.make_se(frm, 'Manufacture');
-										});
-									}
+								if ((flt(doc.produced_qty) < allowed_qty)) {
+									frm.add_custom_button(__('Finish'), function() {
+										erpnext.work_order.make_se(frm, 'Manufacture');
+									});
 								}
-							});
+							}
 						}
 					}
 				} else {
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 66b871c..7584522 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -558,12 +558,19 @@
 			and self.production_plan_item
 			and not self.production_plan_sub_assembly_item
 		):
-			qty = frappe.get_value("Production Plan Item", self.production_plan_item, "ordered_qty") or 0.0
+			table = frappe.qb.DocType("Work Order")
 
-			if self.docstatus == 1:
-				qty += self.qty
-			elif self.docstatus == 2:
-				qty -= self.qty
+			query = (
+				frappe.qb.from_(table)
+				.select(Sum(table.qty))
+				.where(
+					(table.production_plan == self.production_plan)
+					& (table.production_plan_item == self.production_plan_item)
+					& (table.docstatus == 1)
+				)
+			).run()
+
+			qty = flt(query[0][0]) if query else 0
 
 			frappe.db.set_value("Production Plan Item", self.production_plan_item, "ordered_qty", qty)
 
@@ -1476,12 +1483,14 @@
 	return doc
 
 
-def get_reserved_qty_for_production(item_code: str, warehouse: str) -> float:
+def get_reserved_qty_for_production(
+	item_code: str, warehouse: str, check_production_plan: bool = False
+) -> float:
 	"""Get total reserved quantity for any item in specified warehouse"""
 	wo = frappe.qb.DocType("Work Order")
 	wo_item = frappe.qb.DocType("Work Order Item")
 
-	return (
+	query = (
 		frappe.qb.from_(wo)
 		.from_(wo_item)
 		.select(
@@ -1502,7 +1511,12 @@
 				| (wo_item.required_qty > wo_item.consumed_qty)
 			)
 		)
-	).run()[0][0] or 0.0
+	)
+
+	if check_production_plan:
+		query = query.where(wo.production_plan.isnotnull())
+
+	return query.run()[0][0] or 0.0
 
 
 @frappe.whitelist()
diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
index de96a6c..38e0585 100644
--- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
+++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
@@ -69,7 +69,7 @@
 			"label": _("Id"),
 			"fieldname": "name",
 			"fieldtype": "Link",
-			"options": "Work Order",
+			"options": "Quality Inspection",
 			"width": 100,
 		},
 		{"label": _("Report Date"), "fieldname": "report_date", "fieldtype": "Date", "width": 150},
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 3357b06..03c7b01 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -327,7 +327,9 @@
 erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch
 erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance
 erpnext.patches.v14_0.update_closing_balances
+execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
 # below migration patches should always run last
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
 execute:frappe.delete_doc_if_exists("Report", "Tax Detail")
 erpnext.patches.v15_0.enable_all_leads
+erpnext.patches.v14_0.update_company_in_ldc
diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py
index 40a1851..f47e730 100644
--- a/erpnext/patches/v14_0/update_closing_balances.py
+++ b/erpnext/patches/v14_0/update_closing_balances.py
@@ -26,7 +26,15 @@
 			pcv_doc.year_start_date = get_fiscal_year(
 				pcv.posting_date, pcv.fiscal_year, company=pcv.company
 			)[1]
-			gl_entries = pcv_doc.get_gl_entries()
+
+			gl_entries = frappe.db.get_all(
+				"GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"]
+			)
+			for entry in gl_entries:
+				entry["is_period_closing_voucher_entry"] = 1
+				entry["closing_date"] = pcv_doc.posting_date
+				entry["period_closing_voucher"] = pcv_doc.name
+
 			closing_entries = pcv_doc.get_grouped_gl_entries(get_opening_entries=get_opening_entries)
 			make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name)
 			company_wise_order[pcv.company].append(pcv.posting_date)
diff --git a/erpnext/patches/v14_0/update_company_in_ldc.py b/erpnext/patches/v14_0/update_company_in_ldc.py
new file mode 100644
index 0000000..ca95cf2
--- /dev/null
+++ b/erpnext/patches/v14_0/update_company_in_ldc.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# License: MIT. See LICENSE
+
+
+import frappe
+
+from erpnext import get_default_company
+
+
+def execute():
+	company = get_default_company()
+	if company:
+		for d in frappe.get_all("Lower Deduction Certificate", pluck="name"):
+			frappe.db.set_value("Lower Deduction Certificate", d, "company", company, update_modified=False)
diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json
index 4683006..ba6262d 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.json
+++ b/erpnext/projects/doctype/timesheet/timesheet.json
@@ -96,7 +96,6 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval:!doc.work_order || doc.docstatus == 1",
    "fieldname": "employee_detail",
    "fieldtype": "Section Break",
    "label": "Employee Detail"
@@ -311,7 +310,7 @@
  "idx": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-02-14 04:55:41.735991",
+ "modified": "2023-04-20 15:59:11.107831",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Timesheet",
diff --git a/erpnext/public/js/projects/timer.js b/erpnext/public/js/projects/timer.js
index 9dae711..0209f4c 100644
--- a/erpnext/public/js/projects/timer.js
+++ b/erpnext/public/js/projects/timer.js
@@ -68,7 +68,7 @@
 			// New activity if no activities found
 			var args = dialog.get_values();
 			if(!args) return;
-			if (frm.doc.time_logs.length <= 1 && !frm.doc.time_logs[0].activity_type && !frm.doc.time_logs[0].from_time) {
+			if (frm.doc.time_logs.length == 1 && !frm.doc.time_logs[0].activity_type && !frm.doc.time_logs[0].from_time) {
 				frm.doc.time_logs = [];
 			}
 			row = frappe.model.add_child(frm.doc, "Timesheet Detail", "time_logs");
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
index c32ab6b..d332b4e 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
@@ -10,6 +10,7 @@
   "tax_withholding_category",
   "fiscal_year",
   "column_break_3",
+  "company",
   "certificate_no",
   "section_break_3",
   "supplier",
@@ -123,11 +124,18 @@
    "label": "Tax Withholding Category",
    "options": "Tax Withholding Category",
    "reqd": 1
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2021-10-23 18:33:38.962622",
+ "modified": "2023-04-18 08:25:35.302081",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "Lower Deduction Certificate",
@@ -136,5 +144,6 @@
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py" "b/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py"
index c75179e..6717989 100644
--- "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py"
+++ "b/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py"
@@ -1,31 +1,135 @@
 # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-
 import re
 
 import frappe
 from frappe import _
 from frappe.utils import format_datetime
 
+COLUMNS = [
+	{
+		"label": "JournalCode",
+		"fieldname": "JournalCode",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "JournalLib",
+		"fieldname": "JournalLib",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "EcritureNum",
+		"fieldname": "EcritureNum",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "EcritureDate",
+		"fieldname": "EcritureDate",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "CompteNum",
+		"fieldname": "CompteNum",
+		"fieldtype": "Link",
+		"options": "Account",
+		"width": 100,
+	},
+	{
+		"label": "CompteLib",
+		"fieldname": "CompteLib",
+		"fieldtype": "Link",
+		"options": "Account",
+		"width": 200,
+	},
+	{
+		"label": "CompAuxNum",
+		"fieldname": "CompAuxNum",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "CompAuxLib",
+		"fieldname": "CompAuxLib",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "PieceRef",
+		"fieldname": "PieceRef",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "PieceDate",
+		"fieldname": "PieceDate",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "EcritureLib",
+		"fieldname": "EcritureLib",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "Debit",
+		"fieldname": "Debit",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "Credit",
+		"fieldname": "Credit",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "EcritureLet",
+		"fieldname": "EcritureLet",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "DateLet",
+		"fieldname": "DateLet",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "ValidDate",
+		"fieldname": "ValidDate",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "Montantdevise",
+		"fieldname": "Montantdevise",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+	{
+		"label": "Idevise",
+		"fieldname": "Idevise",
+		"fieldtype": "Data",
+		"width": 90,
+	},
+]
+
 
 def execute(filters=None):
-	account_details = {}
-	for acc in frappe.db.sql("""select name, is_group from tabAccount""", as_dict=1):
-		account_details.setdefault(acc.name, acc)
-
-	validate_filters(filters, account_details)
-
-	filters = set_account_currency(filters)
-
-	columns = get_columns(filters)
-
-	res = get_result(filters)
-
-	return columns, res
+	validate_filters(filters)
+	return COLUMNS, get_result(
+		company=filters["company"],
+		fiscal_year=filters["fiscal_year"],
+	)
 
 
-def validate_filters(filters, account_details):
+def validate_filters(filters):
 	if not filters.get("company"):
 		frappe.throw(_("{0} is mandatory").format(_("Company")))
 
@@ -33,107 +137,96 @@
 		frappe.throw(_("{0} is mandatory").format(_("Fiscal Year")))
 
 
-def set_account_currency(filters):
+def get_gl_entries(company, fiscal_year):
+	gle = frappe.qb.DocType("GL Entry")
+	sales_invoice = frappe.qb.DocType("Sales Invoice")
+	purchase_invoice = frappe.qb.DocType("Purchase Invoice")
+	journal_entry = frappe.qb.DocType("Journal Entry")
+	payment_entry = frappe.qb.DocType("Payment Entry")
+	customer = frappe.qb.DocType("Customer")
+	supplier = frappe.qb.DocType("Supplier")
+	employee = frappe.qb.DocType("Employee")
 
-	filters["company_currency"] = frappe.get_cached_value(
-		"Company", filters.company, "default_currency"
+	debit = frappe.query_builder.functions.Sum(gle.debit).as_("debit")
+	credit = frappe.query_builder.functions.Sum(gle.credit).as_("credit")
+	debit_currency = frappe.query_builder.functions.Sum(gle.debit_in_account_currency).as_(
+		"debitCurr"
+	)
+	credit_currency = frappe.query_builder.functions.Sum(gle.credit_in_account_currency).as_(
+		"creditCurr"
 	)
 
-	return filters
-
-
-def get_columns(filters):
-	columns = [
-		"JournalCode" + "::90",
-		"JournalLib" + "::90",
-		"EcritureNum" + ":Dynamic Link:90",
-		"EcritureDate" + "::90",
-		"CompteNum" + ":Link/Account:100",
-		"CompteLib" + ":Link/Account:200",
-		"CompAuxNum" + "::90",
-		"CompAuxLib" + "::90",
-		"PieceRef" + "::90",
-		"PieceDate" + "::90",
-		"EcritureLib" + "::90",
-		"Debit" + "::90",
-		"Credit" + "::90",
-		"EcritureLet" + "::90",
-		"DateLet" + "::90",
-		"ValidDate" + "::90",
-		"Montantdevise" + "::90",
-		"Idevise" + "::90",
-	]
-
-	return columns
-
-
-def get_result(filters):
-	gl_entries = get_gl_entries(filters)
-
-	result = get_result_as_list(gl_entries, filters)
-
-	return result
-
-
-def get_gl_entries(filters):
-
-	group_by_condition = (
-		"group by voucher_type, voucher_no, account"
-		if filters.get("group_by_voucher")
-		else "group by gl.name"
+	query = (
+		frappe.qb.from_(gle)
+		.left_join(sales_invoice)
+		.on(gle.voucher_no == sales_invoice.name)
+		.left_join(purchase_invoice)
+		.on(gle.voucher_no == purchase_invoice.name)
+		.left_join(journal_entry)
+		.on(gle.voucher_no == journal_entry.name)
+		.left_join(payment_entry)
+		.on(gle.voucher_no == payment_entry.name)
+		.left_join(customer)
+		.on(gle.party == customer.name)
+		.left_join(supplier)
+		.on(gle.party == supplier.name)
+		.left_join(employee)
+		.on(gle.party == employee.name)
+		.select(
+			gle.posting_date.as_("GlPostDate"),
+			gle.name.as_("GlName"),
+			gle.account,
+			gle.transaction_date,
+			debit,
+			credit,
+			debit_currency,
+			credit_currency,
+			gle.voucher_type,
+			gle.voucher_no,
+			gle.against_voucher_type,
+			gle.against_voucher,
+			gle.account_currency,
+			gle.against,
+			gle.party_type,
+			gle.party,
+			sales_invoice.name.as_("InvName"),
+			sales_invoice.title.as_("InvTitle"),
+			sales_invoice.posting_date.as_("InvPostDate"),
+			purchase_invoice.name.as_("PurName"),
+			purchase_invoice.title.as_("PurTitle"),
+			purchase_invoice.posting_date.as_("PurPostDate"),
+			journal_entry.cheque_no.as_("JnlRef"),
+			journal_entry.posting_date.as_("JnlPostDate"),
+			journal_entry.title.as_("JnlTitle"),
+			payment_entry.name.as_("PayName"),
+			payment_entry.posting_date.as_("PayPostDate"),
+			payment_entry.title.as_("PayTitle"),
+			customer.customer_name,
+			customer.name.as_("cusName"),
+			supplier.supplier_name,
+			supplier.name.as_("supName"),
+			employee.employee_name,
+			employee.name.as_("empName"),
+		)
+		.where((gle.company == company) & (gle.fiscal_year == fiscal_year))
+		.groupby(gle.voucher_type, gle.voucher_no, gle.account)
+		.orderby(gle.posting_date, gle.voucher_no)
 	)
 
-	gl_entries = frappe.db.sql(
-		"""
-		select
-			gl.posting_date as GlPostDate, gl.name as GlName, gl.account, gl.transaction_date,
-			sum(gl.debit) as debit, sum(gl.credit) as credit,
-			sum(gl.debit_in_account_currency) as debitCurr, sum(gl.credit_in_account_currency) as creditCurr,
-			gl.voucher_type, gl.voucher_no, gl.against_voucher_type,
-			gl.against_voucher, gl.account_currency, gl.against,
-			gl.party_type, gl.party,
-			inv.name as InvName, inv.title as InvTitle, inv.posting_date as InvPostDate,
-			pur.name as PurName, pur.title as PurTitle, pur.posting_date as PurPostDate,
-			jnl.cheque_no as JnlRef, jnl.posting_date as JnlPostDate, jnl.title as JnlTitle,
-			pay.name as PayName, pay.posting_date as PayPostDate, pay.title as PayTitle,
-			cus.customer_name, cus.name as cusName,
-			sup.supplier_name, sup.name as supName,
-			emp.employee_name, emp.name as empName,
-			stu.title as student_name, stu.name as stuName,
-			member_name, mem.name as memName
-
-		from `tabGL Entry` gl
-			left join `tabSales Invoice` inv on gl.voucher_no = inv.name
-			left join `tabPurchase Invoice` pur on gl.voucher_no = pur.name
-			left join `tabJournal Entry` jnl on gl.voucher_no = jnl.name
-			left join `tabPayment Entry` pay on gl.voucher_no = pay.name
-			left join `tabCustomer` cus on gl.party = cus.name
-			left join `tabSupplier` sup on gl.party = sup.name
-			left join `tabEmployee` emp on gl.party = emp.name
-			left join `tabStudent` stu on gl.party = stu.name
-			left join `tabMember` mem on gl.party = mem.name
-		where gl.company=%(company)s and gl.fiscal_year=%(fiscal_year)s
-		{group_by_condition}
-		order by GlPostDate, voucher_no""".format(
-			group_by_condition=group_by_condition
-		),
-		filters,
-		as_dict=1,
-	)
-
-	return gl_entries
+	return query.run(as_dict=True)
 
 
-def get_result_as_list(data, filters):
+def get_result(company, fiscal_year):
+	data = get_gl_entries(company, fiscal_year)
+
 	result = []
 
-	company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
+	company_currency = frappe.get_cached_value("Company", company, "default_currency")
 	accounts = frappe.get_all(
-		"Account", filters={"Company": filters.company}, fields=["name", "account_number"]
+		"Account", filters={"Company": company}, fields=["name", "account_number"]
 	)
 
 	for d in data:
-
 		JournalCode = re.split("-|/|[0-9]", d.get("voucher_no"))[0]
 
 		if d.get("voucher_no").startswith("{0}-".format(JournalCode)) or d.get("voucher_no").startswith(
@@ -141,9 +234,7 @@
 		):
 			EcritureNum = re.split("-|/", d.get("voucher_no"))[1]
 		else:
-			EcritureNum = re.search(
-				r"{0}(\d+)".format(JournalCode), d.get("voucher_no"), re.IGNORECASE
-			).group(1)
+			EcritureNum = re.search(r"{0}(\d+)".format(JournalCode), d.get("voucher_no"), re.IGNORECASE)[1]
 
 		EcritureDate = format_datetime(d.get("GlPostDate"), "yyyyMMdd")
 
@@ -185,7 +276,7 @@
 
 		ValidDate = format_datetime(d.get("GlPostDate"), "yyyyMMdd")
 
-		PieceRef = d.get("voucher_no") if d.get("voucher_no") else "Sans Reference"
+		PieceRef = d.get("voucher_no") or "Sans Reference"
 
 		# EcritureLib is the reference title unless it is an opening entry
 		if d.get("is_opening") == "Yes":
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index eb2c0a4..2ffa6a5 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -416,7 +416,6 @@
    "fieldname": "items_section",
    "fieldtype": "Section Break",
    "hide_border": 1,
-   "label": "Items",
    "oldfieldtype": "Section Break",
    "options": "fa fa-shopping-cart"
   },
@@ -424,6 +423,7 @@
    "allow_bulk_edit": 1,
    "fieldname": "items",
    "fieldtype": "Table",
+   "label": "Items",
    "oldfieldname": "quotation_details",
    "oldfieldtype": "Table",
    "options": "Quotation Item",
@@ -1072,7 +1072,7 @@
  "idx": 82,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-12-12 18:32:28.671332",
+ "modified": "2023-04-14 16:50:44.550098",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Quotation",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index ccea840..4f498fb 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -30,10 +30,6 @@
   "cost_center",
   "dimension_col_break",
   "project",
-  "column_break_77",
-  "source",
-  "campaign",
-  "custom_dimensions_section",
   "currency_and_price_list",
   "currency",
   "conversion_rate",
@@ -162,7 +158,9 @@
   "is_internal_customer",
   "represents_company",
   "column_break_152",
+  "source",
   "inter_company_order_reference",
+  "campaign",
   "party_account_currency",
   "connections_tab"
  ],
@@ -1165,12 +1163,6 @@
    "read_only": 1
   },
   {
-   "fieldname": "column_break_77",
-   "fieldtype": "Column Break",
-   "hide_days": 1,
-   "hide_seconds": 1
-  },
-  {
    "fieldname": "source",
    "fieldtype": "Link",
    "hide_days": 1,
@@ -1613,10 +1605,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "fieldname": "custom_dimensions_section",
-   "fieldtype": "Section Break"
-  },
-  {
    "collapsible": 1,
    "fieldname": "additional_info_section",
    "fieldtype": "Section Break",
@@ -1643,7 +1631,7 @@
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-12-12 18:34:00.681780",
+ "modified": "2023-04-20 11:14:01.036202",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index ee9161b..de63f6d 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -547,7 +547,7 @@
 		# qty is for packed items, because packed items don't have stock_qty field
 		qty = source.get("qty")
 		target.project = source_parent.project
-		target.qty = qty - requested_item_qty.get(source.name, 0)
+		target.qty = qty - requested_item_qty.get(source.name, 0) - source.delivered_qty
 		target.stock_qty = flt(target.qty) * flt(target.conversion_factor)
 
 		args = target.as_dict().copy()
@@ -581,7 +581,7 @@
 				"doctype": "Material Request Item",
 				"field_map": {"name": "sales_order_item", "parent": "sales_order"},
 				"condition": lambda doc: not frappe.db.exists("Product Bundle", doc.item_code)
-				and doc.stock_qty > requested_item_qty.get(doc.name, 0),
+				and (doc.stock_qty - doc.delivered_qty) > requested_item_qty.get(doc.name, 0),
 				"postprocess": update_item,
 			},
 		},
@@ -1340,8 +1340,9 @@
 						.select(Sum(wo.qty))
 						.where(
 							(wo.production_item == i.item_code)
-							& (wo.sales_order == so.name) * (wo.sales_order_item == i.name)
-							& (wo.docstatus.lte(2))
+							& (wo.sales_order == so.name)
+							& (wo.sales_order_item == i.name)
+							& (wo.docstatus.lt(2))
 						)
 						.run()[0][0]
 					)
diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js
index 4691190..64c58ef 100644
--- a/erpnext/selling/doctype/sales_order/sales_order_list.js
+++ b/erpnext/selling/doctype/sales_order/sales_order_list.js
@@ -57,7 +57,7 @@
 		});
 
 		listview.page.add_action_item(__("Advance Payment"), ()=>{
-			erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Advance Payment");
+			erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Payment Entry");
 		});
 
 	}
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 627914f..ba8bbc2 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1878,6 +1878,37 @@
 		self.assertEqual(pe.references[1].reference_name, so.name)
 		self.assertEqual(pe.references[1].allocated_amount, 300)
 
+	def test_delivered_item_material_request(self):
+		"SO -> MR (Manufacture) -> WO. Test if WO Qty is updated in SO."
+		from erpnext.manufacturing.doctype.work_order.work_order import (
+			make_stock_entry as make_se_from_wo,
+		)
+		from erpnext.stock.doctype.material_request.material_request import raise_work_orders
+
+		so = make_sales_order(
+			item_list=[
+				{"item_code": "_Test FG Item", "qty": 10, "rate": 100, "warehouse": "Work In Progress - _TC"}
+			]
+		)
+
+		make_stock_entry(
+			item_code="_Test FG Item", target="Work In Progress - _TC", qty=4, basic_rate=100
+		)
+
+		dn = make_delivery_note(so.name)
+		dn.items[0].qty = 4
+		dn.submit()
+
+		so.load_from_db()
+		self.assertEqual(so.items[0].delivered_qty, 4)
+
+		mr = make_material_request(so.name)
+		mr.material_request_type = "Purchase"
+		mr.schedule_date = today()
+		mr.save()
+
+		self.assertEqual(mr.items[0].qty, 6)
+
 
 def automatically_fetch_payment_terms(enable=1):
 	accounts_settings = frappe.get_doc("Accounts Settings")
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 46320e5..016ebf0 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -559,8 +559,10 @@
 
 				item_row = this.frm.add_child('items', new_item);
 
-				if (field === 'qty' && value !== 0 && !this.allow_negative_stock)
-					await this.check_stock_availability(item_row, value, this.frm.doc.set_warehouse);
+				if (field === 'qty' && value !== 0 && !this.allow_negative_stock) {
+					const qty_needed = value * item_row.conversion_factor;
+					await this.check_stock_availability(item_row, qty_needed, this.frm.doc.set_warehouse);
+				}
 
 				await this.trigger_new_item_events(item_row);
 
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index 3b9fe7b..1843c6e 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -6,7 +6,7 @@
 from frappe import _
 from frappe.model.document import Document
 from frappe.model.naming import make_autoname, revert_series_if_last
-from frappe.query_builder.functions import CurDate, Sum, Timestamp
+from frappe.query_builder.functions import CombineDatetime, CurDate, Sum
 from frappe.utils import cint, flt, get_link_to_form, nowtime
 from frappe.utils.data import add_days
 from frappe.utils.jinja import render_template
@@ -192,7 +192,8 @@
 				posting_time = nowtime()
 
 			query = query.where(
-				Timestamp(sle.posting_date, sle.posting_time) <= Timestamp(posting_date, posting_time)
+				CombineDatetime(sle.posting_date, sle.posting_time)
+				<= CombineDatetime(posting_date, posting_time)
 			)
 
 		out = query.run(as_list=True)[0][0] or 0
@@ -376,7 +377,7 @@
 
 	p = frappe.qb.DocType("POS Invoice").as_("p")
 	item = frappe.qb.DocType("POS Invoice Item").as_("item")
-	sum_qty = frappe.query_builder.functions.Sum(item.qty).as_("qty")
+	sum_qty = frappe.query_builder.functions.Sum(item.stock_qty).as_("qty")
 
 	reserved_batch_qty = (
 		frappe.qb.from_(p)
diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json
index d822f4a..a115727 100644
--- a/erpnext/stock/doctype/bin/bin.json
+++ b/erpnext/stock/doctype/bin/bin.json
@@ -15,6 +15,7 @@
   "projected_qty",
   "reserved_qty_for_production",
   "reserved_qty_for_sub_contract",
+  "reserved_qty_for_production_plan",
   "ma_rate",
   "stock_uom",
   "fcfs_rate",
@@ -165,13 +166,19 @@
    "oldfieldname": "stock_value",
    "oldfieldtype": "Currency",
    "read_only": 1
+  },
+  {
+   "fieldname": "reserved_qty_for_production_plan",
+   "fieldtype": "Float",
+   "label": "Reserved Qty for Production Plan",
+   "read_only": 1
   }
  ],
  "hide_toolbar": 1,
  "idx": 1,
  "in_create": 1,
  "links": [],
- "modified": "2022-03-30 07:22:23.868602",
+ "modified": "2023-05-02 23:26:21.806965",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Bin",
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 72654e6..5abea9e 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -24,8 +24,30 @@
 			- flt(self.reserved_qty)
 			- flt(self.reserved_qty_for_production)
 			- flt(self.reserved_qty_for_sub_contract)
+			- flt(self.reserved_qty_for_production_plan)
 		)
 
+	def update_reserved_qty_for_production_plan(self, skip_project_qty_update=False):
+		"""Update qty reserved for production from Production Plan tables
+		in open production plan"""
+		from erpnext.manufacturing.doctype.production_plan.production_plan import (
+			get_reserved_qty_for_production_plan,
+		)
+
+		self.reserved_qty_for_production_plan = get_reserved_qty_for_production_plan(
+			self.item_code, self.warehouse
+		)
+
+		self.db_set(
+			"reserved_qty_for_production_plan",
+			flt(self.reserved_qty_for_production_plan),
+			update_modified=True,
+		)
+
+		if not skip_project_qty_update:
+			self.set_projected_qty()
+			self.db_set("projected_qty", self.projected_qty, update_modified=True)
+
 	def update_reserved_qty_for_production(self):
 		"""Update qty reserved for production from Production Item tables
 		in open work orders"""
@@ -35,11 +57,13 @@
 			self.item_code, self.warehouse
 		)
 
-		self.set_projected_qty()
-
 		self.db_set(
 			"reserved_qty_for_production", flt(self.reserved_qty_for_production), update_modified=True
 		)
+
+		self.update_reserved_qty_for_production_plan(skip_project_qty_update=True)
+
+		self.set_projected_qty()
 		self.db_set("projected_qty", self.projected_qty, update_modified=True)
 
 	def update_reserved_qty_for_sub_contracting(self, subcontract_doctype="Subcontracting Order"):
@@ -141,6 +165,7 @@
 			"planned_qty",
 			"reserved_qty_for_production",
 			"reserved_qty_for_sub_contract",
+			"reserved_qty_for_production_plan",
 		],
 		as_dict=1,
 	)
@@ -188,6 +213,7 @@
 		- flt(reserved_qty)
 		- flt(bin_details.reserved_qty_for_production)
 		- flt(bin_details.reserved_qty_for_sub_contract)
+		- flt(bin_details.reserved_qty_for_production_plan)
 	)
 
 	frappe.db.set_value(
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 0c1f820..2adf9c3 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -28,8 +28,6 @@
   "column_break_18",
   "project",
   "dimension_col_break",
-  "campaign",
-  "source",
   "custom_dimensions_section",
   "currency_and_price_list",
   "currency",
@@ -161,11 +159,12 @@
   "inter_company_reference",
   "customer_group",
   "territory",
+  "source",
+  "campaign",
   "column_break5",
   "excise_page",
   "instructions",
-  "connections_tab",
-  "column_break_25"
+  "connections_tab"
  ],
  "fields": [
   {
@@ -1340,10 +1339,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "fieldname": "column_break_25",
-   "fieldtype": "Column Break"
-  },
-  {
    "fieldname": "section_break_30",
    "fieldtype": "Section Break",
    "hide_border": 1
@@ -1403,7 +1398,7 @@
  "idx": 146,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-02-14 04:45:44.179670",
+ "modified": "2023-04-21 11:15:23.931084",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 9f9f5cb..c18e851 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -118,7 +118,7 @@
 
 	def so_required(self):
 		"""check in manage account if sales order required or not"""
-		if frappe.db.get_value("Selling Settings", None, "so_required") == "Yes":
+		if frappe.db.get_single_value("Selling Settings", "so_required") == "Yes":
 			for d in self.get("items"):
 				if not d.against_sales_order:
 					frappe.throw(_("Sales Order required for Item {0}").format(d.item_code))
@@ -205,7 +205,7 @@
 		super(DeliveryNote, self).validate_warehouse()
 
 		for d in self.get_item_list():
-			if not d["warehouse"] and frappe.db.get_value("Item", d["item_code"], "is_stock_item") == 1:
+			if not d["warehouse"] and frappe.get_cached_value("Item", d["item_code"], "is_stock_item") == 1:
 				frappe.throw(_("Warehouse required for stock Item {0}").format(d["item_code"]))
 
 	def update_current_stock(self):
@@ -271,6 +271,9 @@
 	def check_credit_limit(self):
 		from erpnext.selling.doctype.customer.customer import check_credit_limit
 
+		if self.per_billed == 100:
+			return
+
 		extra_amount = 0
 		validate_against_credit_limit = False
 		bypass_credit_limit_check_at_sales_order = cint(
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 180adee..e46cab0 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -629,7 +629,8 @@
    "no_copy": 1,
    "options": "Sales Order",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "against_sales_invoice",
@@ -662,7 +663,8 @@
    "label": "Against Sales Invoice Item",
    "no_copy": 1,
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "installed_qty",
@@ -854,7 +856,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-04-06 09:28:29.182053",
+ "modified": "2023-05-01 21:05:14.175640",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 8aeb751..3967282 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -616,7 +616,7 @@
 		target.set_transfer_qty()
 		target.set_actual_qty()
 		target.calculate_rate_and_amount(raise_error_if_no_rate=False)
-		target.set_stock_entry_type()
+		target.stock_entry_type = target.purpose
 		target.set_job_card_data()
 
 	doclist = get_mapped_doc(
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index a707c74..03f58c6 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -54,6 +54,8 @@
 		mr.submit()
 		se = make_stock_entry(mr.name)
 
+		self.assertEqual(se.stock_entry_type, "Material Transfer")
+		self.assertEqual(se.purpose, "Material Transfer")
 		self.assertEqual(se.doctype, "Stock Entry")
 		self.assertEqual(len(se.get("items")), len(mr.get("items")))
 
@@ -69,6 +71,8 @@
 		in_transit_warehouse = get_in_transit_warehouse(mr.company)
 		se = make_in_transit_stock_entry(mr.name, in_transit_warehouse)
 
+		self.assertEqual(se.stock_entry_type, "Material Transfer")
+		self.assertEqual(se.purpose, "Material Transfer")
 		self.assertEqual(se.doctype, "Stock Entry")
 		for row in se.get("items"):
 			self.assertEqual(row.t_warehouse, in_transit_warehouse)
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index d268cc1..5304273 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -380,7 +380,19 @@
 
 					outgoing_amount = d.base_net_amount
 					if self.is_internal_supplier and d.valuation_rate:
-						outgoing_amount = d.valuation_rate * d.stock_qty
+						outgoing_amount = abs(
+							frappe.db.get_value(
+								"Stock Ledger Entry",
+								{
+									"voucher_type": "Purchase Receipt",
+									"voucher_no": self.name,
+									"voucher_detail_no": d.name,
+									"warehouse": d.from_warehouse,
+									"is_cancelled": 0,
+								},
+								"stock_value_difference",
+							)
+						)
 						credit_amount = outgoing_amount
 
 					if credit_amount:
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 7567cfe..c34f9da 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -1610,6 +1610,147 @@
 
 		frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 0)
 
+	def test_internal_pr_gl_entries(self):
+		from erpnext.stock import get_warehouse_account_map
+		from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+		from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
+			create_stock_reconciliation,
+		)
+
+		prepare_data_for_internal_transfer()
+		customer = "_Test Internal Customer 2"
+		company = "_Test Company with perpetual inventory"
+		from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company)
+		target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company)
+		to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company)
+
+		item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100})
+		make_stock_entry(
+			purpose="Material Receipt",
+			item_code=item.name,
+			qty=10,
+			company=company,
+			to_warehouse=from_warehouse,
+			posting_date=add_days(today(), -3),
+		)
+
+		# Step - 1: Create Delivery Note with Internal Customer
+		dn = create_delivery_note(
+			item_code=item.name,
+			company=company,
+			customer=customer,
+			cost_center="Main - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			qty=10,
+			rate=100,
+			warehouse=from_warehouse,
+			target_warehouse=target_warehouse,
+			posting_date=add_days(today(), -2),
+		)
+
+		# Step - 2: Create Internal Purchase Receipt
+		pr = make_inter_company_purchase_receipt(dn.name)
+		pr.items[0].qty = 10
+		pr.items[0].from_warehouse = target_warehouse
+		pr.items[0].warehouse = to_warehouse
+		pr.items[0].rejected_warehouse = from_warehouse
+		pr.save()
+		pr.submit()
+
+		# Step - 3: Create back-date Stock Reconciliation [After DN and Before PR]
+		create_stock_reconciliation(
+			item_code=item,
+			warehouse=target_warehouse,
+			qty=10,
+			rate=50,
+			company=company,
+			posting_date=add_days(today(), -1),
+		)
+
+		warehouse_account = get_warehouse_account_map(company)
+		stock_account_value = frappe.db.get_value(
+			"GL Entry",
+			{
+				"account": warehouse_account[target_warehouse]["account"],
+				"voucher_type": "Purchase Receipt",
+				"voucher_no": pr.name,
+				"is_cancelled": 0,
+			},
+			fieldname=["credit"],
+		)
+		stock_diff = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
+				"voucher_type": "Purchase Receipt",
+				"voucher_no": pr.name,
+				"is_cancelled": 0,
+			},
+			fieldname=["sum(stock_value_difference)"],
+		)
+
+		# Value of Stock Account should be equal to the sum of Stock Value Difference
+		self.assertEqual(stock_account_value, stock_diff)
+
+	def test_internal_pr_reference(self):
+		item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100})
+		customer = "_Test Internal Customer 2"
+		company = "_Test Company with perpetual inventory"
+		from_warehouse = create_warehouse("_Test Internal From Warehouse New 1", company=company)
+		target_warehouse = create_warehouse("_Test Internal GIT Warehouse New 1", company=company)
+		to_warehouse = create_warehouse("_Test Internal To Warehouse New 1", company=company)
+
+		# Step 2: Create Stock Entry (Material Receipt)
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
+		make_stock_entry(
+			purpose="Material Receipt",
+			item_code=item.name,
+			qty=15,
+			company=company,
+			to_warehouse=from_warehouse,
+		)
+
+		# Step 3: Create Delivery Note with Internal Customer
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+
+		dn = create_delivery_note(
+			item_code=item.name,
+			company=company,
+			customer=customer,
+			cost_center="Main - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			qty=10,
+			rate=100,
+			warehouse=from_warehouse,
+			target_warehouse=target_warehouse,
+		)
+
+		# Step 4: Create Internal Purchase Receipt
+		from erpnext.controllers.status_updater import OverAllowanceError
+		from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
+
+		pr = make_inter_company_purchase_receipt(dn.name)
+		pr.inter_company_reference = ""
+		self.assertRaises(frappe.ValidationError, pr.save)
+
+		pr.inter_company_reference = dn.name
+		pr.items[0].qty = 10
+		pr.items[0].from_warehouse = target_warehouse
+		pr.items[0].warehouse = to_warehouse
+		pr.items[0].rejected_warehouse = from_warehouse
+		pr.save()
+
+		delivery_note_item = pr.items[0].delivery_note_item
+		pr.items[0].delivery_note_item = ""
+
+		self.assertRaises(frappe.ValidationError, pr.save)
+
+		pr.load_from_db()
+		pr.items[0].delivery_note_item = delivery_note_item
+		pr.save()
+
 
 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 bbed099..aabc6fc 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -6,7 +6,7 @@
 from frappe.exceptions import QueryDeadlockError, QueryTimeoutError
 from frappe.model.document import Document
 from frappe.query_builder import DocType, Interval
-from frappe.query_builder.functions import Now
+from frappe.query_builder.functions import Max, Now
 from frappe.utils import cint, get_link_to_form, get_weekday, getdate, now, nowtime
 from frappe.utils.user import get_users_with_role
 from rq.timeouts import JobTimeoutException
@@ -36,11 +36,38 @@
 		)
 
 	def validate(self):
+		self.validate_period_closing_voucher()
 		self.set_status(write=False)
 		self.reset_field_values()
 		self.set_company()
 		self.validate_accounts_freeze()
 
+	def validate_period_closing_voucher(self):
+		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}"
+			frappe.throw(_(msg))
+
+	@staticmethod
+	def get_max_year_end_date(company):
+		data = frappe.get_all(
+			"Period Closing Voucher", fields=["fiscal_year"], filters={"docstatus": 1, "company": company}
+		)
+
+		if not data:
+			return
+
+		fiscal_years = [d.fiscal_year for d in data]
+		table = frappe.qb.DocType("Fiscal Year")
+
+		query = (
+			frappe.qb.from_(table)
+			.select(Max(table.year_end_date))
+			.where((table.name.isin(fiscal_years)) & (table.disabled == 0))
+		).run()
+
+		return query[0][0] if query else None
+
 	def validate_accounts_freeze(self):
 		acc_settings = frappe.db.get_value(
 			"Accounts Settings",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 36c875f..b5e5299 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -2346,7 +2346,7 @@
 @frappe.whitelist()
 def make_stock_in_entry(source_name, target_doc=None):
 	def set_missing_values(source, target):
-		target.set_stock_entry_type()
+		target.stock_entry_type = "Material Transfer"
 		target.set_missing_values()
 
 	def update_item(source_doc, target_doc, source_parent):
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index cc06bd7..c43a1b1 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -202,6 +202,9 @@
 		)
 
 		end_transit_entry = make_stock_in_entry(transit_entry.name)
+
+		self.assertEqual(end_transit_entry.stock_entry_type, "Material Transfer")
+		self.assertEqual(end_transit_entry.purpose, "Material Transfer")
 		self.assertEqual(transit_entry.name, end_transit_entry.outgoing_stock_entry)
 		self.assertEqual(transit_entry.name, end_transit_entry.items[0].against_stock_entry)
 		self.assertEqual(transit_entry.items[0].name, end_transit_entry.items[0].ste_detail)
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 7d59441..2e5d2c3 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -530,7 +530,9 @@
 		# check if cancellation of stock reco is blocked
 		self.assertRaises(NegativeStockError, sr.cancel)
 
-		repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name}))
+		repost_exists = bool(
+			frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name, "status": "Queued"})
+		)
 		self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation")
 
 	def test_intermediate_sr_bin_update(self):
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index ce85702..f3adefb 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -1508,11 +1508,15 @@
 	elif args.get("against_sales_invoice"):
 		sales_order = frappe.db.get_all(
 			"Sales Invoice Item",
-			filters={"parent": args.get("against_sales_invoice"), "item_code": args.get("item_code")},
+			filters={
+				"parent": args.get("against_sales_invoice"),
+				"item_code": args.get("item_code"),
+				"docstatus": 1,
+			},
 			fields="sales_order",
 		)
 		if sales_order and sales_order[0]:
-			if get_reserved_qty_for_so(sales_order[0][0], args.get("item_code")):
+			if get_reserved_qty_for_so(sales_order[0].sales_order, args.get("item_code")):
 				reserved_so = sales_order[0]
 	elif args.get("sales_order"):
 		if get_reserved_qty_for_so(args.get("sales_order"), args.get("item_code")):
diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
index df01b14..e4f657c 100644
--- a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
+++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe import _
 from frappe.query_builder import Field
-from frappe.query_builder.functions import Min, Timestamp
+from frappe.query_builder.functions import CombineDatetime, Min
 from frappe.utils import add_days, getdate, today
 
 import erpnext
@@ -75,7 +75,7 @@
 			& (sle.company == report_filters.company)
 			& (sle.is_cancelled == 0)
 		)
-		.orderby(Timestamp(sle.posting_date, sle.posting_time), sle.creation)
+		.orderby(CombineDatetime(sle.posting_date, sle.posting_time), sle.creation)
 	).run(as_dict=True)
 
 	for d in data:
@@ -84,7 +84,7 @@
 	closing_date = add_days(from_date, -1)
 	for key, stock_data in voucher_wise_dict.items():
 		prev_stock_value = get_stock_value_on(
-			posting_date=closing_date, item_code=key[0], warehouse=key[1]
+			posting_date=closing_date, item_code=key[0], warehouses=key[1]
 		)
 		for data in stock_data:
 			expected_stock_value = prev_stock_value + data.stock_value_difference
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
index f477d8f..31c756d 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -76,6 +76,7 @@
 				bin.ordered_qty,
 				bin.reserved_qty,
 				bin.reserved_qty_for_production,
+				bin.reserved_qty_for_production_plan,
 				bin.reserved_qty_for_sub_contract,
 				reserved_qty_for_pos,
 				bin.projected_qty,
@@ -174,6 +175,13 @@
 			"convertible": "qty",
 		},
 		{
+			"label": _("Reserved for Production Plan"),
+			"fieldname": "reserved_qty_for_production_plan",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
 			"label": _("Reserved for Sub Contracting"),
 			"fieldname": "reserved_qty_for_sub_contract",
 			"fieldtype": "Float",
@@ -232,6 +240,7 @@
 			bin.reserved_qty,
 			bin.reserved_qty_for_production,
 			bin.reserved_qty_for_sub_contract,
+			bin.reserved_qty_for_production_plan,
 			bin.projected_qty,
 		)
 		.orderby(bin.item_code, bin.warehouse)
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index b638f08..8b517bf 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -1388,7 +1388,11 @@
 def regenerate_sle_for_batch_stock_reco(detail):
 	doc = frappe.get_cached_doc("Stock Reconciliation", detail.voucher_no)
 	doc.recalculate_current_qty(detail.item_code, detail.batch_no)
-	doc.repost_future_sle_and_gle()
+
+	if not frappe.db.exists(
+		"Repost Item Valuation", {"voucher_no": doc.name, "status": "Queued", "docstatus": "1"}
+	):
+		doc.repost_future_sle_and_gle(force=True)
 
 
 def get_stock_reco_qty_shift(args):
@@ -1441,22 +1445,23 @@
 				(
 					CombineDatetime(sle.posting_date, sle.posting_time)
 					> CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
-					| (
-						(
-							CombineDatetime(sle.posting_date, sle.posting_time)
-							== CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
-						)
-						& (sle.creation > kwargs.get("creation"))
+				)
+				| (
+					(
+						CombineDatetime(sle.posting_date, sle.posting_time)
+						== CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
 					)
+					& (sle.creation > kwargs.get("creation"))
 				)
 			)
 		)
 		.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
 		.orderby(sle.creation)
+		.limit(1)
 	)
 
 	if kwargs.get("batch_no"):
-		query.where(sle.batch_no == kwargs.get("batch_no"))
+		query = query.where(sle.batch_no == kwargs.get("batch_no"))
 
 	return query.run(as_dict=True)
 
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index b8c5187..fb52697 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -7,10 +7,11 @@
 
 import frappe
 from frappe import _
-from frappe.query_builder.functions import CombineDatetime
+from frappe.query_builder.functions import CombineDatetime, IfNull, Sum
 from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime
 
 import erpnext
+from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
 from erpnext.stock.valuation import FIFOValuation, LIFOValuation
 
 BarcodeScanResult = Dict[str, Optional[str]]
@@ -53,50 +54,36 @@
 	return stock_value
 
 
-def get_stock_value_on(warehouse=None, posting_date=None, item_code=None):
+def get_stock_value_on(
+	warehouses: list | str = None, posting_date: str = None, item_code: str = None
+) -> float:
 	if not posting_date:
 		posting_date = nowdate()
 
-	values, condition = [posting_date], ""
-
-	if warehouse:
-
-		lft, rgt, is_group = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt", "is_group"])
-
-		if is_group:
-			values.extend([lft, rgt])
-			condition += "and exists (\
-				select name from `tabWarehouse` wh where wh.name = sle.warehouse\
-				and wh.lft >= %s and wh.rgt <= %s)"
-
-		else:
-			values.append(warehouse)
-			condition += " AND warehouse = %s"
-
-	if item_code:
-		values.append(item_code)
-		condition += " AND item_code = %s"
-
-	stock_ledger_entries = frappe.db.sql(
-		"""
-		SELECT item_code, stock_value, name, warehouse
-		FROM `tabStock Ledger Entry` sle
-		WHERE posting_date <= %s {0}
-			and is_cancelled = 0
-		ORDER BY timestamp(posting_date, posting_time) DESC, creation DESC
-	""".format(
-			condition
-		),
-		values,
-		as_dict=1,
+	sle = frappe.qb.DocType("Stock Ledger Entry")
+	query = (
+		frappe.qb.from_(sle)
+		.select(IfNull(Sum(sle.stock_value_difference), 0))
+		.where((sle.posting_date <= posting_date) & (sle.is_cancelled == 0))
+		.orderby(CombineDatetime(sle.posting_date, sle.posting_time), order=frappe.qb.desc)
+		.orderby(sle.creation, order=frappe.qb.desc)
 	)
 
-	sle_map = {}
-	for sle in stock_ledger_entries:
-		if not (sle.item_code, sle.warehouse) in sle_map:
-			sle_map[(sle.item_code, sle.warehouse)] = flt(sle.stock_value)
+	if warehouses:
+		if isinstance(warehouses, str):
+			warehouses = [warehouses]
 
-	return sum(sle_map.values())
+		warehouses = set(warehouses)
+		for wh in list(warehouses):
+			if frappe.db.get_value("Warehouse", wh, "is_group"):
+				warehouses.update(get_child_warehouses(wh))
+
+		query = query.where(sle.warehouse.isin(warehouses))
+
+	if item_code:
+		query = query.where(sle.item_code == item_code)
+
+	return query.run(as_list=True)[0][0]
 
 
 @frappe.whitelist()
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
index 3a2c53f..45289b1 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
@@ -67,6 +67,15 @@
 			}
 		});
 
+		frm.set_query('batch_no', 'supplied_items', function(doc, cdt, cdn) {
+			var row = locals[cdt][cdn];
+			return {
+				filters: {
+					item: row.rm_item_code
+				}
+			}
+		});
+
 		let batch_no_field = frm.get_docfield("items", "batch_no");
 		if (batch_no_field) {
 			batch_no_field.get_route_options_for_new_doc = function(row) {
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
index 1698e23..1c6f24b 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
@@ -192,7 +192,7 @@
   }
  ],
  "links": [],
- "modified": "2021-11-26 15:45:33.289911",
+ "modified": "2023-04-21 17:16:56.192560",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Service Level Agreement",
@@ -212,19 +212,12 @@
    "write": 1
   },
   {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
    "read": 1,
-   "report": 1,
-   "role": "All",
-   "share": 1,
-   "write": 1
+   "role": "All"
   }
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py
index c1579b3..5e57b31 100644
--- a/erpnext/utilities/bulk_transaction.py
+++ b/erpnext/utilities/bulk_transaction.py
@@ -69,7 +69,7 @@
 		"Sales Order": {
 			"Sales Invoice": sales_order.make_sales_invoice,
 			"Delivery Note": sales_order.make_delivery_note,
-			"Advance Payment": payment_entry.get_payment_entry,
+			"Payment Entry": payment_entry.get_payment_entry,
 		},
 		"Sales Invoice": {
 			"Delivery Note": sales_invoice.make_delivery_note,
@@ -86,11 +86,11 @@
 		"Supplier Quotation": {
 			"Purchase Order": supplier_quotation.make_purchase_order,
 			"Purchase Invoice": supplier_quotation.make_purchase_invoice,
-			"Advance Payment": payment_entry.get_payment_entry,
 		},
 		"Purchase Order": {
 			"Purchase Invoice": purchase_order.make_purchase_invoice,
 			"Purchase Receipt": purchase_order.make_purchase_receipt,
+			"Payment Entry": payment_entry.get_payment_entry,
 		},
 		"Purchase Invoice": {
 			"Purchase Receipt": purchase_invoice.make_purchase_receipt,
@@ -98,12 +98,13 @@
 		},
 		"Purchase Receipt": {"Purchase Invoice": purchase_receipt.make_purchase_invoice},
 	}
-	if to_doctype in ["Advance Payment", "Payment Entry"]:
+	if to_doctype in ["Payment Entry"]:
 		obj = mapper[from_doctype][to_doctype](from_doctype, doc_name)
 	else:
 		obj = mapper[from_doctype][to_doctype](doc_name)
 
 	obj.flags.ignore_validate = True
+	obj.set_title_field()
 	obj.insert(ignore_mandatory=True)