Merge branch 'develop' into asset_depreciation_schedule
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
index 325a356..220b747 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
@@ -485,6 +485,10 @@
 			"default_payable_account": frappe.db.get_value(
 				"Account", {"company": company.name, "account_type": "Payable", "is_group": 0}
 			),
+			"default_provisional_account": frappe.db.get_value(
+				"Account",
+				{"company": company.name, "account_type": "Service Received But Not Billed", "is_group": 0},
+			),
 		}
 	)
 
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index 3fc1adf..4a7a57b 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -305,6 +305,7 @@
    "fieldname": "source_exchange_rate",
    "fieldtype": "Float",
    "label": "Exchange Rate",
+   "precision": "9",
    "print_hide": 1,
    "reqd": 1
   },
@@ -334,6 +335,7 @@
    "fieldname": "target_exchange_rate",
    "fieldtype": "Float",
    "label": "Exchange Rate",
+   "precision": "9",
    "print_hide": 1,
    "reqd": 1
   },
@@ -731,7 +733,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-02-23 20:08:39.559814",
+ "modified": "2022-12-08 16:25:43.824051",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Entry",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 0a704ac..79fab64 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -1187,6 +1187,7 @@
 
 	ple = qb.DocType("Payment Ledger Entry")
 	common_filter = []
+	accounting_dimensions_filter = []
 	posting_and_due_date = []
 
 	# confirm that Supplier is not blocked
@@ -1216,7 +1217,7 @@
 	# Add cost center condition
 	if args.get("cost_center"):
 		condition += " and cost_center='%s'" % args.get("cost_center")
-		common_filter.append(ple.cost_center == args.get("cost_center"))
+		accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center"))
 
 	date_fields_dict = {
 		"posting_date": ["from_posting_date", "to_posting_date"],
@@ -1242,6 +1243,7 @@
 		posting_date=posting_and_due_date,
 		min_outstanding=args.get("outstanding_amt_greater_than"),
 		max_outstanding=args.get("outstanding_amt_less_than"),
+		accounting_dimensions=accounting_dimensions_filter,
 	)
 
 	outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
@@ -1638,7 +1640,7 @@
 ):
 	reference_doc = None
 	doc = frappe.get_doc(dt, dn)
-	if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
+	if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= 99.99:
 		frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
 
 	if not party_type:
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 52efd33..ff212f2 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -23,6 +23,7 @@
 	def __init__(self, *args, **kwargs):
 		super(PaymentReconciliation, self).__init__(*args, **kwargs)
 		self.common_filter_conditions = []
+		self.accounting_dimension_filter_conditions = []
 		self.ple_posting_date_filter = []
 
 	@frappe.whitelist()
@@ -193,6 +194,7 @@
 			posting_date=self.ple_posting_date_filter,
 			min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None,
 			max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None,
+			accounting_dimensions=self.accounting_dimension_filter_conditions,
 		)
 
 		if self.invoice_limit:
@@ -381,7 +383,7 @@
 		self.common_filter_conditions.append(ple.company == self.company)
 
 		if self.get("cost_center") and (get_invoices or get_return_invoices):
-			self.common_filter_conditions.append(ple.cost_center == self.cost_center)
+			self.accounting_dimension_filter_conditions.append(ple.cost_center == self.cost_center)
 
 		if get_invoices:
 			if self.from_invoice_date:
diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
index dae029b..6030134 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
@@ -8,6 +8,8 @@
 from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, nowdate
 
+from erpnext import get_default_cost_center
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
 from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.accounts.party import get_party_account
@@ -20,6 +22,7 @@
 		self.create_item()
 		self.create_customer()
 		self.create_account()
+		self.create_cost_center()
 		self.clear_old_entries()
 
 	def tearDown(self):
@@ -216,6 +219,22 @@
 		)
 		return je
 
+	def create_cost_center(self):
+		# Setup cost center
+		cc_name = "Sub"
+
+		self.main_cc = frappe.get_doc("Cost Center", get_default_cost_center(self.company))
+
+		cc_exists = frappe.db.get_list("Cost Center", filters={"cost_center_name": cc_name})
+		if cc_exists:
+			self.sub_cc = frappe.get_doc("Cost Center", cc_exists[0].name)
+		else:
+			sub_cc = frappe.new_doc("Cost Center")
+			sub_cc.cost_center_name = "Sub"
+			sub_cc.parent_cost_center = self.main_cc.parent_cost_center
+			sub_cc.company = self.main_cc.company
+			self.sub_cc = sub_cc.save()
+
 	def test_filter_min_max(self):
 		# check filter condition minimum and maximum amount
 		self.create_sales_invoice(qty=1, rate=300)
@@ -578,3 +597,24 @@
 		self.assertEqual(len(pr.payments), 1)
 		self.assertEqual(pr.payments[0].amount, amount)
 		self.assertEqual(pr.payments[0].currency, "EUR")
+
+	def test_differing_cost_center_on_invoice_and_payment(self):
+		"""
+		Cost Center filter should not affect outstanding amount calculation
+		"""
+
+		si = self.create_sales_invoice(qty=1, rate=100, do_not_submit=True)
+		si.cost_center = self.main_cc.name
+		si.submit()
+		pr = get_payment_entry(si.doctype, si.name)
+		pr.cost_center = self.sub_cc.name
+		pr = pr.save().submit()
+
+		pr = self.create_payment_reconciliation()
+		pr.cost_center = self.main_cc.name
+
+		pr.get_unreconciled_entries()
+
+		# check PR tool output
+		self.assertEqual(len(pr.get("invoices")), 0)
+		self.assertEqual(len(pr.get("payments")), 0)
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js
index 901ef19..e913912 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.js
+++ b/erpnext/accounts/doctype/payment_request/payment_request.js
@@ -42,7 +42,7 @@
 		});
 	}
 
-	if(!frm.doc.payment_gateway_account && frm.doc.status == "Initiated") {
+	if((!frm.doc.payment_gateway_account || frm.doc.payment_request_type == "Outward") && frm.doc.status == "Initiated") {
 		frm.add_custom_button(__('Create Payment Entry'), function(){
 			frappe.call({
 				method: "erpnext.accounts.doctype.payment_request.payment_request.make_payment_entry",
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index d82083c..fc93801 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -261,6 +261,7 @@
 
 		payment_entry.update(
 			{
+				"mode_of_payment": self.mode_of_payment,
 				"reference_no": self.name,
 				"reference_date": nowdate(),
 				"remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(
@@ -410,25 +411,22 @@
 		else ""
 	)
 
-	existing_payment_request = None
-	if args.order_type == "Shopping Cart":
-		existing_payment_request = frappe.db.get_value(
-			"Payment Request",
-			{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ("!=", 2)},
-		)
+	draft_payment_request = frappe.db.get_value(
+		"Payment Request",
+		{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": 0},
+	)
 
-	if existing_payment_request:
+	existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn)
+
+	if existing_payment_request_amount:
+		grand_total -= existing_payment_request_amount
+
+	if draft_payment_request:
 		frappe.db.set_value(
-			"Payment Request", existing_payment_request, "grand_total", grand_total, update_modified=False
+			"Payment Request", draft_payment_request, "grand_total", grand_total, update_modified=False
 		)
-		pr = frappe.get_doc("Payment Request", existing_payment_request)
+		pr = frappe.get_doc("Payment Request", draft_payment_request)
 	else:
-		if args.order_type != "Shopping Cart":
-			existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn)
-
-			if existing_payment_request_amount:
-				grand_total -= existing_payment_request_amount
-
 		pr = frappe.new_doc("Payment Request")
 		pr.update(
 			{
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index 9dab4e9..8708342 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -280,7 +280,8 @@
 		self.validate_plans_billing_cycle(self.get_billing_cycle_and_interval())
 		self.validate_end_date()
 		self.validate_to_follow_calendar_months()
-		self.cost_center = erpnext.get_default_cost_center(self.get("company"))
+		if not self.cost_center:
+			self.cost_center = erpnext.get_default_cost_center(self.get("company"))
 
 	def validate_trial_period(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 30ed91b..b834d14 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -121,12 +121,24 @@
 	else:
 		tax_row = get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted)
 
+	cost_center = get_cost_center(inv)
+	tax_row.update({"cost_center": cost_center})
+
 	if inv.doctype == "Purchase Invoice":
 		return tax_row, tax_deducted_on_advances, voucher_wise_amount
 	else:
 		return tax_row
 
 
+def get_cost_center(inv):
+	cost_center = frappe.get_cached_value("Company", inv.company, "cost_center")
+
+	if len(inv.get("taxes", [])) > 0:
+		cost_center = inv.get("taxes")[0].cost_center
+
+	return cost_center
+
+
 def get_tax_withholding_details(tax_withholding_category, posting_date, company):
 	tax_withholding = frappe.get_doc("Tax Withholding Category", tax_withholding_category)
 
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index a195c57..fb2e444 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -99,6 +99,9 @@
 		# Get return entries
 		self.get_return_entries()
 
+		# Get Exchange Rate Revaluations
+		self.get_exchange_rate_revaluations()
+
 		self.data = []
 
 		for ple in self.ple_entries:
@@ -251,7 +254,8 @@
 			row.invoice_grand_total = row.invoiced
 
 			if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and (
-				abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision
+				(abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision)
+				or (row.voucher_no in self.err_journals)
 			):
 
 				# non-zero oustanding, we must consider this row
@@ -794,19 +798,19 @@
 
 		if self.filters.get("payment_terms_template"):
 			self.qb_selection_filter.append(
-				self.ple.party_isin(
-					qb.from_(self.customer).where(
-						self.customer.payment_terms == self.filters.get("payment_terms_template")
-					)
+				self.ple.party.isin(
+					qb.from_(self.customer)
+					.select(self.customer.name)
+					.where(self.customer.payment_terms == self.filters.get("payment_terms_template"))
 				)
 			)
 
 		if self.filters.get("sales_partner"):
 			self.qb_selection_filter.append(
-				self.ple.party_isin(
-					qb.from_(self.customer).where(
-						self.customer.default_sales_partner == self.filters.get("payment_terms_template")
-					)
+				self.ple.party.isin(
+					qb.from_(self.customer)
+					.select(self.customer.name)
+					.where(self.customer.default_sales_partner == self.filters.get("payment_terms_template"))
 				)
 			)
 
@@ -1028,3 +1032,17 @@
 			"data": {"labels": self.ageing_column_labels, "datasets": rows},
 			"type": "percentage",
 		}
+
+	def get_exchange_rate_revaluations(self):
+		je = qb.DocType("Journal Entry")
+		results = (
+			qb.from_(je)
+			.select(je.name)
+			.where(
+				(je.company == self.filters.company)
+				& (je.posting_date.lte(self.filters.report_date))
+				& (je.voucher_type == "Exchange Rate Revaluation")
+			)
+			.run()
+		)
+		self.err_journals = [x[0] for x in results] if results else []
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index bac8bee..97a9c15 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -1,9 +1,10 @@
 import unittest
 
 import frappe
-from frappe.tests.utils import FrappeTestCase
-from frappe.utils import add_days, getdate, today
+from frappe.tests.utils import FrappeTestCase, change_settings
+from frappe.utils import add_days, flt, getdate, today
 
+from erpnext import get_default_cost_center
 from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute
@@ -17,10 +18,37 @@
 		frappe.db.sql("delete from `tabPayment Entry` where company='_Test Company 2'")
 		frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
 		frappe.db.sql("delete from `tabPayment Ledger Entry` where company='_Test Company 2'")
+		frappe.db.sql("delete from `tabJournal Entry` where company='_Test Company 2'")
+		frappe.db.sql("delete from `tabExchange Rate Revaluation` where company='_Test Company 2'")
+
+		self.create_usd_account()
 
 	def tearDown(self):
 		frappe.db.rollback()
 
+	def create_usd_account(self):
+		name = "Debtors USD"
+		exists = frappe.db.get_list(
+			"Account", filters={"company": "_Test Company 2", "account_name": "Debtors USD"}
+		)
+		if exists:
+			self.debtors_usd = exists[0].name
+		else:
+			debtors = frappe.get_doc(
+				"Account",
+				frappe.db.get_list(
+					"Account", filters={"company": "_Test Company 2", "account_name": "Debtors"}
+				)[0].name,
+			)
+
+			debtors_usd = frappe.new_doc("Account")
+			debtors_usd.company = debtors.company
+			debtors_usd.account_name = "Debtors USD"
+			debtors_usd.account_currency = "USD"
+			debtors_usd.parent_account = debtors.parent_account
+			debtors_usd.account_type = debtors.account_type
+			self.debtors_usd = debtors_usd.save().name
+
 	def test_accounts_receivable(self):
 		filters = {
 			"company": "_Test Company 2",
@@ -33,7 +61,7 @@
 		}
 
 		# check invoice grand total and invoiced column's value for 3 payment terms
-		name = make_sales_invoice()
+		name = make_sales_invoice().name
 		report = execute(filters)
 
 		expected_data = [[100, 30], [100, 50], [100, 20]]
@@ -118,8 +146,74 @@
 			],
 		)
 
+	@change_settings(
+		"Accounts Settings", {"allow_multi_currency_invoices_against_single_party_account": 1}
+	)
+	def test_exchange_revaluation_for_party(self):
+		"""
+		Exchange Revaluation for party on Receivable/Payable shoule be included
+		"""
 
-def make_sales_invoice():
+		company = "_Test Company 2"
+		customer = "_Test Customer 2"
+
+		# Using Exchange Gain/Loss account for unrealized as well.
+		company_doc = frappe.get_doc("Company", company)
+		company_doc.unrealized_exchange_gain_loss_account = company_doc.exchange_gain_loss_account
+		company_doc.save()
+
+		si = make_sales_invoice(no_payment_schedule=True, do_not_submit=True)
+		si.currency = "USD"
+		si.conversion_rate = 0.90
+		si.debit_to = self.debtors_usd
+		si = si.save().submit()
+
+		# Exchange Revaluation
+		err = frappe.new_doc("Exchange Rate Revaluation")
+		err.company = company
+		err.posting_date = today()
+		accounts = err.get_accounts_data()
+		err.extend("accounts", accounts)
+		err.accounts[0].new_exchange_rate = 0.95
+		row = err.accounts[0]
+		row.new_balance_in_base_currency = flt(
+			row.new_exchange_rate * flt(row.balance_in_account_currency)
+		)
+		row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
+		err.set_total_gain_loss()
+		err = err.save().submit()
+
+		# Submit JV for ERR
+		jv = frappe.get_doc(err.make_jv_entry())
+		jv = jv.save()
+		for x in jv.accounts:
+			x.cost_center = get_default_cost_center(jv.company)
+		jv.submit()
+
+		filters = {
+			"company": company,
+			"report_date": today(),
+			"range1": 30,
+			"range2": 60,
+			"range3": 90,
+			"range4": 120,
+		}
+		report = execute(filters)
+
+		expected_data_for_err = [0, -5, 0, 5]
+		row = [x for x in report[1] if x.voucher_type == jv.doctype and x.voucher_no == jv.name][0]
+		self.assertEqual(
+			expected_data_for_err,
+			[
+				row.invoiced,
+				row.paid,
+				row.credit_note,
+				row.outstanding,
+			],
+		)
+
+
+def make_sales_invoice(no_payment_schedule=False, do_not_submit=False):
 	frappe.set_user("Administrator")
 
 	si = create_sales_invoice(
@@ -134,22 +228,26 @@
 		do_not_save=1,
 	)
 
-	si.append(
-		"payment_schedule",
-		dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30),
-	)
-	si.append(
-		"payment_schedule",
-		dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50),
-	)
-	si.append(
-		"payment_schedule",
-		dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20),
-	)
+	if not no_payment_schedule:
+		si.append(
+			"payment_schedule",
+			dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30),
+		)
+		si.append(
+			"payment_schedule",
+			dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50),
+		)
+		si.append(
+			"payment_schedule",
+			dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20),
+		)
 
-	si.submit()
+	si = si.save()
 
-	return si.name
+	if not do_not_submit:
+		si = si.submit()
+
+	return si
 
 
 def make_payment(docname):
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py
index d4f2011..cb3c78a 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/cash_flow.py
@@ -8,6 +8,7 @@
 
 from erpnext.accounts.report.financial_statements import (
 	get_columns,
+	get_cost_centers_with_children,
 	get_data,
 	get_filtered_list_for_consolidated_report,
 	get_period_list,
@@ -160,10 +161,11 @@
 	total = 0
 	for period in period_list:
 		start_date = get_start_date(period, accumulated_values, company)
+		filters.start_date = start_date
+		filters.end_date = period["to_date"]
+		filters.account_type = account_type
 
-		amount = get_account_type_based_gl_data(
-			company, start_date, period["to_date"], account_type, filters
-		)
+		amount = get_account_type_based_gl_data(company, filters)
 
 		if amount and account_type == "Depreciation":
 			amount *= -1
@@ -175,7 +177,7 @@
 	return data
 
 
-def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters=None):
+def get_account_type_based_gl_data(company, filters=None):
 	cond = ""
 	filters = frappe._dict(filters or {})
 
@@ -191,17 +193,21 @@
 			frappe.db.escape(cstr(filters.finance_book))
 		)
 
+	if filters.get("cost_center"):
+		filters.cost_center = get_cost_centers_with_children(filters.cost_center)
+		cond += " and cost_center in %(cost_center)s"
+
 	gl_sum = frappe.db.sql_list(
 		"""
 		select sum(credit) - sum(debit)
 		from `tabGL Entry`
-		where company=%s and posting_date >= %s and posting_date <= %s
+		where company=%(company)s and posting_date >= %(start_date)s and posting_date <= %(end_date)s
 			and voucher_type != 'Period Closing Voucher'
-			and account in ( SELECT name FROM tabAccount WHERE account_type = %s) {cond}
+			and account in ( SELECT name FROM tabAccount WHERE account_type = %(account_type)s) {cond}
 	""".format(
 			cond=cond
 		),
-		(company, start_date, end_date, account_type),
+		filters,
 	)
 
 	return gl_sum[0] if gl_sum and gl_sum[0] else 0
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index d269e1f..ddee9fc 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -268,10 +268,12 @@
 def get_account_type_based_data(account_type, companies, fiscal_year, filters):
 	data = {}
 	total = 0
+	filters.account_type = account_type
+	filters.start_date = fiscal_year.year_start_date
+	filters.end_date = fiscal_year.year_end_date
+
 	for company in companies:
-		amount = get_account_type_based_gl_data(
-			company, fiscal_year.year_start_date, fiscal_year.year_end_date, account_type, filters
-		)
+		amount = get_account_type_based_gl_data(company, filters)
 
 		if amount and account_type == "Depreciation":
 			amount *= -1
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 99e86ae..8ba2310 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -607,6 +607,7 @@
 					return abs(previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
 				else:
 					return flt(row.qty) * self.get_average_buying_rate(row, item_code)
+		return 0.0
 
 	def get_buying_amount(self, row, item_code):
 		# IMP NOTE
diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py
index 0ea6b5c..fa11a41 100644
--- a/erpnext/accounts/report/gross_profit/test_gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py
@@ -6,6 +6,8 @@
 from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.accounts.report.gross_profit.gross_profit import execute
+from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 
@@ -14,6 +16,7 @@
 	def setUp(self):
 		self.create_company()
 		self.create_item()
+		self.create_bundle()
 		self.create_customer()
 		self.create_sales_invoice()
 		self.clear_old_entries()
@@ -42,6 +45,7 @@
 		self.company = company.name
 		self.cost_center = company.cost_center
 		self.warehouse = "Stores - " + abbr
+		self.finished_warehouse = "Finished Goods - " + abbr
 		self.income_account = "Sales - " + abbr
 		self.expense_account = "Cost of Goods Sold - " + abbr
 		self.debit_to = "Debtors - " + abbr
@@ -53,6 +57,23 @@
 		)
 		self.item = item if isinstance(item, str) else item.item_code
 
+	def create_bundle(self):
+		from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
+
+		item2 = create_item(
+			item_code="_Test GP Item 2", is_stock_item=1, company=self.company, warehouse=self.warehouse
+		)
+		self.item2 = item2 if isinstance(item2, str) else item2.item_code
+
+		# This will be parent item
+		bundle = create_item(
+			item_code="_Test GP bundle", is_stock_item=0, company=self.company, warehouse=self.warehouse
+		)
+		self.bundle = bundle if isinstance(bundle, str) else bundle.item_code
+
+		# Create Product Bundle
+		self.product_bundle = make_product_bundle(parent=self.bundle, items=[self.item, self.item2])
+
 	def create_customer(self):
 		name = "_Test GP Customer"
 		if frappe.db.exists("Customer", name):
@@ -93,6 +114,28 @@
 		)
 		return sinv
 
+	def create_delivery_note(
+		self, item=None, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
+	):
+		"""
+		Helper function to populate default values in Delivery Note
+		"""
+		dnote = create_delivery_note(
+			company=self.company,
+			customer=self.customer,
+			currency="INR",
+			item=item or self.item,
+			qty=qty,
+			rate=rate,
+			cost_center=self.cost_center,
+			warehouse=self.warehouse,
+			return_against=None,
+			expense_account=self.expense_account,
+			do_not_save=do_not_save,
+			do_not_submit=do_not_submit,
+		)
+		return dnote
+
 	def clear_old_entries(self):
 		doctype_list = [
 			"Sales Invoice",
@@ -207,3 +250,55 @@
 		}
 		gp_entry = [x for x in data if x.parent_invoice == sinv.name]
 		self.assertDictContainsSubset(expected_entry_with_dn, gp_entry[0])
+
+	def test_bundled_delivery_note_with_different_warehouses(self):
+		"""
+		Test Delivery Note with bundled item. Packed Item from the bundle having different warehouses
+		"""
+		se = make_stock_entry(
+			company=self.company,
+			item_code=self.item,
+			target=self.warehouse,
+			qty=1,
+			basic_rate=100,
+			do_not_submit=True,
+		)
+		item = se.items[0]
+		se.append(
+			"items",
+			{
+				"item_code": self.item2,
+				"s_warehouse": "",
+				"t_warehouse": self.finished_warehouse,
+				"qty": 1,
+				"basic_rate": 100,
+				"conversion_factor": item.conversion_factor or 1.0,
+				"transfer_qty": flt(item.qty) * (flt(item.conversion_factor) or 1.0),
+				"serial_no": item.serial_no,
+				"batch_no": item.batch_no,
+				"cost_center": item.cost_center,
+				"expense_account": item.expense_account,
+			},
+		)
+		se = se.save().submit()
+
+		# Make a Delivery note with Product bundle
+		# Packed Items will have different warehouses
+		dnote = self.create_delivery_note(item=self.bundle, qty=1, rate=200, do_not_submit=True)
+		dnote.packed_items[1].warehouse = self.finished_warehouse
+		dnote = dnote.submit()
+
+		# make Sales Invoice for above delivery note
+		sinv = make_sales_invoice(dnote.name)
+		sinv = sinv.save().submit()
+
+		filters = frappe._dict(
+			company=self.company,
+			from_date=nowdate(),
+			to_date=nowdate(),
+			group_by="Invoice",
+			sales_invoice=sinv.name,
+		)
+
+		columns, data = execute(filters=filters)
+		self.assertGreater(len(data), 0)
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 41702d6..1e573b0 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -836,6 +836,7 @@
 	posting_date=None,
 	min_outstanding=None,
 	max_outstanding=None,
+	accounting_dimensions=None,
 ):
 
 	ple = qb.DocType("Payment Ledger Entry")
@@ -866,6 +867,7 @@
 		min_outstanding=min_outstanding,
 		max_outstanding=max_outstanding,
 		get_invoices=True,
+		accounting_dimensions=accounting_dimensions or [],
 	)
 
 	for d in invoice_list:
@@ -1615,6 +1617,7 @@
 			.where(ple.delinked == 0)
 			.where(Criterion.all(filter_on_voucher_no))
 			.where(Criterion.all(self.common_filter))
+			.where(Criterion.all(self.dimensions_filter))
 			.where(Criterion.all(self.voucher_posting_date))
 			.groupby(ple.voucher_type, ple.voucher_no, ple.party_type, ple.party)
 		)
@@ -1702,6 +1705,7 @@
 		max_outstanding=None,
 		get_payments=False,
 		get_invoices=False,
+		accounting_dimensions=None,
 	):
 		"""
 		Fetch voucher amount and outstanding amount from Payment Ledger using Database CTE
@@ -1717,6 +1721,7 @@
 		self.reset()
 		self.vouchers = vouchers
 		self.common_filter = common_filter or []
+		self.dimensions_filter = accounting_dimensions or []
 		self.voucher_posting_date = posting_date or []
 		self.min_outstanding = min_outstanding
 		self.max_outstanding = max_outstanding
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 06fdea0..47089f7 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -235,11 +235,11 @@
 						cur_frm.add_custom_button(__('Purchase Invoice'),
 							this.make_purchase_invoice, __('Create'));
 
-					if(flt(doc.per_billed)==0 && doc.status != "Delivered") {
+					if(flt(doc.per_billed) < 100 && doc.status != "Delivered") {
 						cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create'));
 					}
 
-					if(flt(doc.per_billed)==0) {
+					if(flt(doc.per_billed) < 100) {
 						this.frm.add_custom_button(__('Payment Request'),
 							function() { me.make_payment_request() }, __('Create'));
 					}
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index 98c7dc9..a9f5afb 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -57,44 +57,96 @@
 				});
 			}, __("Tools"));
 
-			frm.add_custom_button(__('Download PDF'), () => {
-				var suppliers = [];
-				const fields = [{
-					fieldtype: 'Link',
-					label: __('Select a Supplier'),
-					fieldname: 'supplier',
-					options: 'Supplier',
-					reqd: 1,
-					get_query: () => {
-						return {
-							filters: [
-								["Supplier", "name", "in", frm.doc.suppliers.map((row) => {return row.supplier;})]
-							]
-						}
-					}
-				}];
-
-				frappe.prompt(fields, data => {
-					var child = locals[cdt][cdn]
-
-					var w = window.open(
-						frappe.urllib.get_full_url("/api/method/erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_pdf?"
-						+"doctype="+encodeURIComponent(frm.doc.doctype)
-						+"&name="+encodeURIComponent(frm.doc.name)
-						+"&supplier="+encodeURIComponent(data.supplier)
-						+"&no_letterhead=0"));
-					if(!w) {
-						frappe.msgprint(__("Please enable pop-ups")); return;
-					}
+			frm.add_custom_button(
+				__("Download PDF"),
+				() => {
+					frappe.prompt(
+						[
+							{
+								fieldtype: "Link",
+								label: "Select a Supplier",
+								fieldname: "supplier",
+								options: "Supplier",
+								reqd: 1,
+								default: frm.doc.suppliers?.length == 1 ? frm.doc.suppliers[0].supplier : "",
+								get_query: () => {
+									return {
+										filters: [
+											[
+												"Supplier",
+												"name",
+												"in",
+												frm.doc.suppliers.map((row) => {
+													return row.supplier;
+												}),
+											],
+										],
+									};
+								},
+							},
+							{
+								fieldtype: "Section Break",
+								label: "Print Settings",
+								fieldname: "print_settings",
+								collapsible: 1,
+							},
+							{
+								fieldtype: "Link",
+								label: "Print Format",
+								fieldname: "print_format",
+								options: "Print Format",
+								placeholder: "Standard",
+								get_query: () => {
+									return {
+										filters: {
+											doc_type: "Request for Quotation",
+										},
+									};
+								},
+							},
+							{
+								fieldtype: "Link",
+								label: "Language",
+								fieldname: "language",
+								options: "Language",
+								default: frappe.boot.lang,
+							},
+							{
+								fieldtype: "Link",
+								label: "Letter Head",
+								fieldname: "letter_head",
+								options: "Letter Head",
+								default: frm.doc.letter_head,
+							},
+						],
+						(data) => {
+							var w = window.open(
+								frappe.urllib.get_full_url(
+									"/api/method/erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_pdf?" +
+									new URLSearchParams({
+										doctype: frm.doc.doctype,
+										name: frm.doc.name,
+										supplier: data.supplier,
+										print_format: data.print_format || "Standard",
+										language: data.language || frappe.boot.lang,
+										letter_head: data.letter_head || frm.doc.letter_head || "",
+									}).toString()
+								)
+							);
+							if (!w) {
+								frappe.msgprint(__("Please enable pop-ups"));
+								return;
+							}
+						},
+						"Download PDF for Supplier",
+						"Download"
+					);
 				},
-				'Download PDF for Supplier',
-				'Download');
-			},
-			__("Tools"));
+				__("Tools")
+			);
 
-			frm.page.set_inner_btn_group_as_primary(__('Create'));
+			frm.page.set_inner_btn_group_as_primary(__("Create"));
 		}
-
 	},
 
 	make_supplier_quotation: function(frm) {
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index bdbc9ce..dbc3644 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -389,10 +389,17 @@
 
 
 @frappe.whitelist()
-def get_pdf(doctype, name, supplier):
-	doc = get_rfq_doc(doctype, name, supplier)
-	if doc:
-		download_pdf(doctype, name, doc=doc)
+def get_pdf(doctype, name, supplier, print_format=None, language=None, letter_head=None):
+	# permissions get checked in `download_pdf`
+	if doc := get_rfq_doc(doctype, name, supplier):
+		download_pdf(
+			doctype,
+			name,
+			print_format,
+			doc=doc,
+			language=language,
+			letter_head=letter_head or None,
+		)
 
 
 def get_rfq_doc(doctype, name, supplier):
diff --git a/erpnext/crm/doctype/appointment/appointment.json b/erpnext/crm/doctype/appointment/appointment.json
index fe7b4e1..c26b064 100644
--- a/erpnext/crm/doctype/appointment/appointment.json
+++ b/erpnext/crm/doctype/appointment/appointment.json
@@ -102,7 +102,7 @@
   }
  ],
  "links": [],
- "modified": "2021-06-30 13:09:14.228756",
+ "modified": "2022-12-15 11:11:02.131986",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Appointment",
@@ -123,16 +123,6 @@
   },
   {
    "create": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Guest",
-   "share": 1
-  },
-  {
-   "create": 1,
    "delete": 1,
    "email": 1,
    "export": 1,
@@ -170,5 +160,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py
index 6e7ba1f..bd49bdc 100644
--- a/erpnext/crm/doctype/appointment/appointment.py
+++ b/erpnext/crm/doctype/appointment/appointment.py
@@ -6,7 +6,9 @@
 
 import frappe
 from frappe import _
+from frappe.desk.form.assign_to import add as add_assignment
 from frappe.model.document import Document
+from frappe.share import add_docshare
 from frappe.utils import get_url, getdate, now
 from frappe.utils.verified_command import get_signed_params
 
@@ -130,21 +132,18 @@
 		self.party = lead.name
 
 	def auto_assign(self):
-		from frappe.desk.form.assign_to import add as add_assignemnt
-
 		existing_assignee = self.get_assignee_from_latest_opportunity()
 		if existing_assignee:
 			# If the latest opportunity is assigned to someone
 			# Assign the appointment to the same
-			add_assignemnt({"doctype": self.doctype, "name": self.name, "assign_to": [existing_assignee]})
+			self.assign_agent(existing_assignee)
 			return
 		if self._assign:
 			return
 		available_agents = _get_agents_sorted_by_asc_workload(getdate(self.scheduled_time))
 		for agent in available_agents:
 			if _check_agent_availability(agent, self.scheduled_time):
-				agent = agent[0]
-				add_assignemnt({"doctype": self.doctype, "name": self.name, "assign_to": [agent]})
+				self.assign_agent(agent[0])
 			break
 
 	def get_assignee_from_latest_opportunity(self):
@@ -199,9 +198,15 @@
 		params = {"email": self.customer_email, "appointment": self.name}
 		return get_url(verify_route + "?" + get_signed_params(params))
 
+	def assign_agent(self, agent):
+		if not frappe.has_permission(doc=self, user=agent):
+			add_docshare(self.doctype, self.name, agent, flags={"ignore_share_permission": True})
+
+		add_assignment({"doctype": self.doctype, "name": self.name, "assign_to": [agent]})
+
 
 def _get_agents_sorted_by_asc_workload(date):
-	appointments = frappe.db.get_list("Appointment", fields="*")
+	appointments = frappe.get_all("Appointment", fields="*")
 	agent_list = _get_agent_list_as_strings()
 	if not appointments:
 		return agent_list
@@ -226,7 +231,7 @@
 
 
 def _check_agent_availability(agent_email, scheduled_time):
-	appointemnts_at_scheduled_time = frappe.get_list(
+	appointemnts_at_scheduled_time = frappe.get_all(
 		"Appointment", filters={"scheduled_time": scheduled_time}
 	)
 	for appointment in appointemnts_at_scheduled_time:
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
index 4b26e49..436eb10 100644
--- a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2019-08-27 10:56:48.309824",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -101,7 +102,8 @@
   }
  ],
  "issingle": 1,
- "modified": "2019-11-26 12:14:17.669366",
+ "links": [],
+ "modified": "2022-12-15 11:10:13.517742",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Appointment Booking Settings",
@@ -118,13 +120,6 @@
    "write": 1
   },
   {
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "role": "Guest",
-   "share": 1
-  },
-  {
    "create": 1,
    "email": 1,
    "print": 1,
@@ -147,5 +142,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index fd19d25..7d72c76 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -420,6 +420,7 @@
 		"erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall.create_process_loan_security_shortfall",
 		"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_term_loans",
 		"erpnext.crm.utils.open_leads_opportunities_based_on_todays_event",
+		"erpnext.stock.doctype.stock_entry.stock_entry.audit_incorrect_valuation_entries",
 	],
 	"monthly_long": [
 		"erpnext.accounts.deferred_revenue.process_deferred_accounting",
diff --git a/erpnext/portal/utils.py b/erpnext/portal/utils.py
index 7be8c5d..c8b03e6 100644
--- a/erpnext/portal/utils.py
+++ b/erpnext/portal/utils.py
@@ -102,7 +102,7 @@
 	contact = frappe.new_doc("Contact")
 	contact.update({"first_name": fullname, "email_id": user})
 	contact.append("links", dict(link_doctype=doctype, link_name=party_name))
-	contact.append("email_ids", dict(email_id=user))
+	contact.append("email_ids", dict(email_id=user, is_primary=True))
 	contact.flags.ignore_mandatory = True
 	contact.insert(ignore_permissions=True)
 
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 58d8de2..aa57bc2 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -298,7 +298,7 @@
 	}
 
 	make_payment_request() {
-		var me = this;
+		let me = this;
 		const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], this.frm.doc.doctype))
 			? "Inward" : "Outward";
 
@@ -314,7 +314,7 @@
 			},
 			callback: function(r) {
 				if(!r.exc){
-					var doc = frappe.model.sync(r.message);
+					frappe.model.sync(r.message);
 					frappe.set_route("Form", r.message.doctype, r.message.name);
 				}
 			}
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
index 91748bc..f3f931e 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
@@ -45,6 +45,12 @@
 			}
 		},
 		{
+			"fieldname": "warehouse",
+			"label": __("Warehouse"),
+			"fieldtype": "Link",
+			"options": "Warehouse"
+		},
+		{
 			"fieldname": "status",
 			"label": __("Status"),
 			"fieldtype": "MultiSelectList",
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
index 720aa41..63d339a 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
@@ -53,6 +53,9 @@
 	if filters.get("status"):
 		conditions += " and so.status in %(status)s"
 
+	if filters.get("warehouse"):
+		conditions += " and soi.warehouse = %(warehouse)s"
+
 	return conditions
 
 
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index d6f2378..07ee289 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -70,9 +70,6 @@
 
 		self.abbr = self.abbr.strip()
 
-		# if self.get('__islocal') and len(self.abbr) > 5:
-		# 	frappe.throw(_("Abbreviation cannot have more than 5 characters"))
-
 		if not self.abbr.strip():
 			frappe.throw(_("Abbreviation is mandatory"))
 
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
index c18a4b2..4256a7d 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -204,7 +204,7 @@
 
 @frappe.whitelist()
 def get_doctypes_to_be_ignored():
-	doctypes_to_be_ignored_list = [
+	doctypes_to_be_ignored = [
 		"Account",
 		"Cost Center",
 		"Warehouse",
@@ -223,4 +223,7 @@
 		"Customer",
 		"Supplier",
 	]
-	return doctypes_to_be_ignored_list
+
+	doctypes_to_be_ignored.extend(frappe.get_hooks("company_data_to_be_ignored") or [])
+
+	return doctypes_to_be_ignored
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 5de7fed..aff5e05 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -192,13 +192,13 @@
 
 			if item_map.get(key):
 				item_map[key].qty += item.qty
-				item_map[key].stock_qty += item.stock_qty
+				item_map[key].stock_qty += flt(item.stock_qty, item.precision("stock_qty"))
 			else:
 				item_map[key] = item
 
 			# maintain count of each item (useful to limit get query)
 			self.item_count_map.setdefault(item_code, 0)
-			self.item_count_map[item_code] += item.stock_qty
+			self.item_count_map[item_code] += flt(item.stock_qty, item.precision("stock_qty"))
 
 		return item_map.values()
 
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index a2748d0..541d4d1 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -766,13 +766,13 @@
 
 @frappe.whitelist()
 def auto_fetch_serial_number(
-	qty: float,
+	qty: int,
 	item_code: str,
 	warehouse: str,
 	posting_date: Optional[str] = None,
 	batch_nos: Optional[Union[str, List[str]]] = None,
 	for_doctype: Optional[str] = None,
-	exclude_sr_nos: Optional[List[str]] = None,
+	exclude_sr_nos=None,
 ) -> List[str]:
 
 	filters = frappe._dict({"item_code": item_code, "warehouse": warehouse})
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index d401f81..a047a9b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -4,12 +4,24 @@
 
 import json
 from collections import defaultdict
+from typing import Dict
 
 import frappe
 from frappe import _
 from frappe.model.mapper import get_mapped_doc
 from frappe.query_builder.functions import Sum
-from frappe.utils import cint, comma_or, cstr, flt, format_time, formatdate, getdate, nowdate
+from frappe.utils import (
+	add_days,
+	cint,
+	comma_or,
+	cstr,
+	flt,
+	format_time,
+	formatdate,
+	getdate,
+	nowdate,
+	today,
+)
 
 import erpnext
 from erpnext.accounts.general_ledger import process_gl_map
@@ -2700,3 +2712,62 @@
 		)
 		.orderby(stock_entry.creation, stock_entry_detail.item_code, stock_entry_detail.idx)
 	).run(as_dict=1)
+
+
+def audit_incorrect_valuation_entries():
+	# Audit of stock transfer entries having incorrect valuation
+	from erpnext.controllers.stock_controller import create_repost_item_valuation_entry
+
+	stock_entries = get_incorrect_stock_entries()
+
+	for stock_entry, values in stock_entries.items():
+		reposting_data = frappe._dict(
+			{
+				"posting_date": values.posting_date,
+				"posting_time": values.posting_time,
+				"voucher_type": "Stock Entry",
+				"voucher_no": stock_entry,
+				"company": values.company,
+			}
+		)
+
+		create_repost_item_valuation_entry(reposting_data)
+
+
+def get_incorrect_stock_entries() -> Dict:
+	stock_entry = frappe.qb.DocType("Stock Entry")
+	stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry")
+	transfer_purposes = [
+		"Material Transfer",
+		"Material Transfer for Manufacture",
+		"Send to Subcontractor",
+	]
+
+	query = (
+		frappe.qb.from_(stock_entry)
+		.inner_join(stock_ledger_entry)
+		.on(stock_entry.name == stock_ledger_entry.voucher_no)
+		.select(
+			stock_entry.name,
+			stock_entry.company,
+			stock_entry.posting_date,
+			stock_entry.posting_time,
+			Sum(stock_ledger_entry.stock_value_difference).as_("stock_value"),
+		)
+		.where(
+			(stock_entry.docstatus == 1)
+			& (stock_entry.purpose.isin(transfer_purposes))
+			& (stock_ledger_entry.modified > add_days(today(), -2))
+		)
+		.groupby(stock_ledger_entry.voucher_detail_no)
+		.having(Sum(stock_ledger_entry.stock_value_difference) != 0)
+	)
+
+	data = query.run(as_dict=True)
+	stock_entries = {}
+
+	for row in data:
+		if abs(row.stock_value) > 0.1 and row.name not in stock_entries:
+			stock_entries.setdefault(row.name, row)
+
+	return stock_entries
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index b574b71..680d209 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe.permissions import add_user_permission, remove_user_permission
 from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import add_days, flt, nowdate, nowtime, today
+from frappe.utils import add_days, flt, now, nowdate, nowtime, today
 
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
 from erpnext.stock.doctype.item.test_item import (
@@ -17,6 +17,8 @@
 from erpnext.stock.doctype.serial_no.serial_no import *  # noqa
 from erpnext.stock.doctype.stock_entry.stock_entry import (
 	FinishedGoodError,
+	audit_incorrect_valuation_entries,
+	get_incorrect_stock_entries,
 	move_sample_to_retention_warehouse,
 )
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
@@ -1614,6 +1616,44 @@
 
 		self.assertRaises(BatchExpiredError, se.save)
 
+	def test_audit_incorrect_stock_entries(self):
+		item_code = "Test Incorrect Valuation Rate Item - 001"
+		create_item(item_code=item_code, is_stock_item=1)
+
+		make_stock_entry(
+			item_code=item_code,
+			purpose="Material Receipt",
+			posting_date=add_days(nowdate(), -10),
+			qty=2,
+			rate=500,
+			to_warehouse="_Test Warehouse - _TC",
+		)
+
+		transfer_entry = make_stock_entry(
+			item_code=item_code,
+			purpose="Material Transfer",
+			qty=2,
+			rate=500,
+			from_warehouse="_Test Warehouse - _TC",
+			to_warehouse="_Test Warehouse 1 - _TC",
+		)
+
+		sle_name = frappe.db.get_value(
+			"Stock Ledger Entry", {"voucher_no": transfer_entry.name, "actual_qty": (">", 0)}, "name"
+		)
+
+		frappe.db.set_value(
+			"Stock Ledger Entry", sle_name, {"modified": add_days(now(), -1), "stock_value_difference": 10}
+		)
+
+		stock_entries = get_incorrect_stock_entries()
+		self.assertTrue(transfer_entry.name in stock_entries)
+
+		audit_incorrect_valuation_entries()
+
+		stock_entries = get_incorrect_stock_entries()
+		self.assertFalse(transfer_entry.name in stock_entries)
+
 
 def make_serialized_item(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 3a0b38a..398b3c9 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -715,8 +715,8 @@
 def get_stock_balance_for(
 	item_code: str,
 	warehouse: str,
-	posting_date: str,
-	posting_time: str,
+	posting_date,
+	posting_time,
 	batch_no: Optional[str] = None,
 	with_valuation_rate: bool = True,
 ):
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 31dccf6..1741d65 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -828,9 +828,9 @@
 	):
 		if frappe.has_permission("Item Price", "write"):
 			price_list_rate = (
-				(args.rate + args.discount_amount) / args.get("conversion_factor")
+				(flt(args.rate) + flt(args.discount_amount)) / args.get("conversion_factor")
 				if args.get("conversion_factor")
-				else (args.rate + args.discount_amount)
+				else (flt(args.rate) + flt(args.discount_amount))
 			)
 
 			item_price = frappe.db.get_value(
diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
index a6fc049..c4358b8 100644
--- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
+++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
@@ -82,7 +82,7 @@
 			item.safety_stock,
 			item.lead_time_days,
 		)
-		.where(item.is_stock_item == 1)
+		.where((item.is_stock_item == 1) & (item.disabled == 0))
 	)
 
 	if brand := filters.get("brand"):
diff --git a/erpnext/www/book-appointment/__init__.py b/erpnext/www/book-appointment/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/www/book-appointment/__init__.py
+++ /dev/null
diff --git a/erpnext/www/book-appointment/verify/__init__.py b/erpnext/www/book-appointment/verify/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/www/book-appointment/verify/__init__.py
+++ /dev/null
diff --git a/erpnext/www/book_appointment/index.js b/erpnext/www/book_appointment/index.js
index 46ac155..d02cdad 100644
--- a/erpnext/www/book_appointment/index.js
+++ b/erpnext/www/book_appointment/index.js
@@ -2,8 +2,6 @@
     initialise_select_date();
 })
 
-window.holiday_list = [];
-
 async function initialise_select_date() {
     navigate_to_page(1);
     await get_global_variables();
@@ -20,7 +18,6 @@
     window.timezones = (await frappe.call({
         method:'erpnext.www.book_appointment.index.get_timezones'
     })).message;
-    window.holiday_list = window.appointment_settings.holiday_list;
 }
 
 function setup_timezone_selector() {
diff --git a/erpnext/www/book_appointment/index.py b/erpnext/www/book_appointment/index.py
index 06e99da..dfca946 100644
--- a/erpnext/www/book_appointment/index.py
+++ b/erpnext/www/book_appointment/index.py
@@ -26,8 +26,12 @@
 
 @frappe.whitelist(allow_guest=True)
 def get_appointment_settings():
-	settings = frappe.get_doc("Appointment Booking Settings")
-	settings.holiday_list = frappe.get_doc("Holiday List", settings.holiday_list)
+	settings = frappe.get_cached_value(
+		"Appointment Booking Settings",
+		None,
+		["advance_booking_days", "appointment_duration", "success_redirect_url"],
+		as_dict=True,
+	)
 	return settings
 
 
@@ -106,7 +110,7 @@
 	appointment.customer_details = contact.get("notes", None)
 	appointment.customer_email = contact.get("email", None)
 	appointment.status = "Open"
-	appointment.insert()
+	appointment.insert(ignore_permissions=True)
 	return appointment
 
 
diff --git a/erpnext/www/book_appointment/verify/index.py b/erpnext/www/book_appointment/verify/index.py
index 1a5ba9d..3beb866 100644
--- a/erpnext/www/book_appointment/verify/index.py
+++ b/erpnext/www/book_appointment/verify/index.py
@@ -2,7 +2,6 @@
 from frappe.utils.verified_command import verify_request
 
 
-@frappe.whitelist(allow_guest=True)
 def get_context(context):
 	if not verify_request():
 		context.success = False