Merge branch 'develop' into payment-reco-company-field
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/in_standard_chart_of_accounts.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/in_standard_chart_of_accounts.json
index 2ec0b7f..56b22a6 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/in_standard_chart_of_accounts.json
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/in_standard_chart_of_accounts.json
@@ -36,16 +36,16 @@
                 }
             },
             "Fixed Assets": {
-                "Capital Equipments": {
+                "Capital Equipment": {
                     "account_type": "Fixed Asset"
                 },
-                "Electronic Equipments": {
+                "Electronic Equipment": {
                     "account_type": "Fixed Asset"
                 },
-                "Furnitures and Fixtures": {
+                "Furniture and Fixtures": {
                     "account_type": "Fixed Asset"
                 },
-                "Office Equipments": {
+                "Office Equipment": {
                     "account_type": "Fixed Asset"
                 },
                 "Plants and Machineries": {
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py
index e30ad24..0699932 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py
@@ -23,13 +23,13 @@
 				_("Tax Assets"): {"is_group": 1},
 			},
 			_("Fixed Assets"): {
-				_("Capital Equipments"): {"account_type": "Fixed Asset"},
-				_("Electronic Equipments"): {"account_type": "Fixed Asset"},
-				_("Furnitures and Fixtures"): {"account_type": "Fixed Asset"},
-				_("Office Equipments"): {"account_type": "Fixed Asset"},
+				_("Capital Equipment"): {"account_type": "Fixed Asset"},
+				_("Electronic Equipment"): {"account_type": "Fixed Asset"},
+				_("Furniture and Fixtures"): {"account_type": "Fixed Asset"},
+				_("Office Equipment"): {"account_type": "Fixed Asset"},
 				_("Plants and Machineries"): {"account_type": "Fixed Asset"},
 				_("Buildings"): {"account_type": "Fixed Asset"},
-				_("Softwares"): {"account_type": "Fixed Asset"},
+				_("Software"): {"account_type": "Fixed Asset"},
 				_("Accumulated Depreciation"): {"account_type": "Accumulated Depreciation"},
 				_("CWIP Account"): {
 					"account_type": "Capital Work in Progress",
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py
index 0e46f1e..ee4da73 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py
@@ -36,13 +36,13 @@
 				"account_number": "1100-1600",
 			},
 			_("Fixed Assets"): {
-				_("Capital Equipments"): {"account_type": "Fixed Asset", "account_number": "1710"},
-				_("Electronic Equipments"): {"account_type": "Fixed Asset", "account_number": "1720"},
-				_("Furnitures and Fixtures"): {"account_type": "Fixed Asset", "account_number": "1730"},
-				_("Office Equipments"): {"account_type": "Fixed Asset", "account_number": "1740"},
+				_("Capital Equipment"): {"account_type": "Fixed Asset", "account_number": "1710"},
+				_("Electronic Equipment"): {"account_type": "Fixed Asset", "account_number": "1720"},
+				_("Furniture and Fixtures"): {"account_type": "Fixed Asset", "account_number": "1730"},
+				_("Office Equipment"): {"account_type": "Fixed Asset", "account_number": "1740"},
 				_("Plants and Machineries"): {"account_type": "Fixed Asset", "account_number": "1750"},
 				_("Buildings"): {"account_type": "Fixed Asset", "account_number": "1760"},
-				_("Softwares"): {"account_type": "Fixed Asset", "account_number": "1770"},
+				_("Software"): {"account_type": "Fixed Asset", "account_number": "1770"},
 				_("Accumulated Depreciation"): {
 					"account_type": "Accumulated Depreciation",
 					"account_number": "1780",
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index 30eebef..eb3e00b 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -119,7 +119,7 @@
 			InvalidAccountMergeError,
 			merge_account,
 			"Capital Stock - _TC",
-			"Softwares - _TC",
+			"Software - _TC",
 		)
 
 		# Raise error as currency doesn't match
diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py
index 4b99b19..ace4bb1 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account.py
@@ -55,7 +55,7 @@
 
 	def validate_company(self):
 		if self.is_company_account and not self.company:
-			frappe.throw(_("Company is manadatory for company account"))
+			frappe.throw(_("Company is mandatory for company account"))
 
 	def validate_iban(self):
 		"""
diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
index 0af2caf..4326c40 100644
--- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
+++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
@@ -48,11 +48,11 @@
 
 	def on_submit(self):
 		if not self.bank_guarantee_number:
-			frappe.throw(_("Enter the Bank Guarantee Number before submittting."))
+			frappe.throw(_("Enter the Bank Guarantee Number before submitting."))
 		if not self.name_of_beneficiary:
-			frappe.throw(_("Enter the name of the Beneficiary before submittting."))
+			frappe.throw(_("Enter the name of the Beneficiary before submitting."))
 		if not self.bank:
-			frappe.throw(_("Enter the name of the bank or lending institution before submittting."))
+			frappe.throw(_("Enter the name of the bank or lending institution before submitting."))
 
 
 @frappe.whitelist()
diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.json b/erpnext/accounts/doctype/coupon_code/coupon_code.json
index 7dc5e9d..c6b1477 100644
--- a/erpnext/accounts/doctype/coupon_code/coupon_code.json
+++ b/erpnext/accounts/doctype/coupon_code/coupon_code.json
@@ -80,7 +80,7 @@
   {
    "fieldname": "valid_upto",
    "fieldtype": "Date",
-   "label": "Valid Upto"
+   "label": "Valid Up To"
   },
   {
    "depends_on": "eval: doc.coupon_type == \"Promotional\"",
@@ -115,7 +115,7 @@
    "read_only": 1
   }
  ],
- "modified": "2019-10-19 14:48:14.602481",
+ "modified": "2024-01-24 02:20:26.145996",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Coupon Code",
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
index 5ab91f2..bd2bfbd 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
@@ -82,11 +82,11 @@
  "icon": "fa fa-calendar",
  "idx": 1,
  "links": [],
- "modified": "2020-11-05 12:16:53.081573",
+ "modified": "2024-01-17 13:06:01.608953",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Fiscal Year",
- "owner": "Administrator",
+  "owner": "Administrator",
  "permissions": [
   {
    "create": 1,
@@ -118,6 +118,14 @@
   {
    "read": 1,
    "role": "Employee"
+  },
+  {
+   "read": 1,
+   "role": "Accounts Manager"
+  },
+  {
+   "read": 1,
+   "role": "Stock Manager"
   }
  ],
  "show_name_in_global_search": 1,
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js
index db4f7c4..c80bf62 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js
@@ -154,7 +154,7 @@
 					}
 				});
 			},
-			primary_action_label: __('Get Invocies')
+			primary_action_label: __('Get Invoices')
 		});
 		d.show();
 	},
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 40d552b..7579da8 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -186,9 +186,12 @@
 
 	def update_advance_paid(self):
 		advance_paid = frappe._dict()
+		advance_payment_doctypes = frappe.get_hooks(
+			"advance_payment_customer_doctypes"
+		) + frappe.get_hooks("advance_payment_supplier_doctypes")
 		for d in self.get("accounts"):
 			if d.is_advance:
-				if d.reference_type in frappe.get_hooks("advance_payment_doctypes"):
+				if d.reference_type in advance_payment_doctypes:
 					advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
 
 		for voucher_type, order_list in advance_paid.items():
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
index f5f8f8a..acd9933 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
@@ -270,7 +270,7 @@
 				errors, "<a href='/app/List/Error Log' class='variant-click'>Error Log</a>"
 			),
 			indicator="red",
-			title=_("Error Occured"),
+			title=_("Error Occurred"),
 		)
 	return names
 
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 2954d2f..62e2181 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -640,7 +640,7 @@
 
 	get_outstanding_invoices_or_orders: function(frm, get_outstanding_invoices, get_orders_to_be_billed) {
 		const today = frappe.datetime.get_today();
-		const fields = [
+		let fields = [
 			{fieldtype:"Section Break", label: __("Posting Date")},
 			{fieldtype:"Date", label: __("From Date"),
 				fieldname:"from_posting_date", default:frappe.datetime.add_days(today, -30)},
@@ -655,18 +655,29 @@
 				fieldname:"outstanding_amt_greater_than", default: 0},
 			{fieldtype:"Column Break"},
 			{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
-			{fieldtype:"Section Break"},
-			{fieldtype:"Link", label:__("Cost Center"), fieldname:"cost_center", options:"Cost Center",
-				"get_query": function() {
-					return {
-						"filters": {"company": frm.doc.company}
-					}
+		];
+
+		if (frm.dimension_filters) {
+			let column_break_insertion_point = Math.ceil((frm.dimension_filters.length)/2);
+
+			fields.push({fieldtype:"Section Break"});
+			frm.dimension_filters.map((elem, idx)=>{
+				fields.push({
+					fieldtype: "Link",
+					label: elem.document_type == "Cost Center" ? "Cost Center" : elem.label,
+					options: elem.document_type,
+					fieldname: elem.fieldname || elem.document_type
+				});
+				if(idx+1 == column_break_insertion_point) {
+					fields.push({fieldtype:"Column Break"});
 				}
-			},
-			{fieldtype:"Column Break"},
+			});
+		}
+
+		fields = fields.concat([
 			{fieldtype:"Section Break"},
 			{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
-		];
+		]);
 
 		let btn_text = "";
 
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index dbebbb0..cfe0d9c 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -13,6 +13,7 @@
 from pypika.functions import Coalesce, Sum
 
 import erpnext
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
 from erpnext.accounts.doctype.bank_account.bank_account import (
 	get_bank_account_details,
 	get_party_bank_account,
@@ -189,7 +190,7 @@
 
 	def set_liability_account(self):
 		# Auto setting liability account should only be done during 'draft' status
-		if self.docstatus > 0:
+		if self.docstatus > 0 or self.payment_type == "Internal Transfer":
 			return
 
 		if not frappe.db.get_value(
@@ -925,7 +926,10 @@
 
 	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"):
+		advance_payment_doctypes = frappe.get_hooks(
+			"advance_payment_customer_doctypes"
+		) + frappe.get_hooks("advance_payment_supplier_doctypes")
+		if d.reference_doctype in 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
 
@@ -1423,8 +1427,11 @@
 
 	def update_advance_paid(self):
 		if self.payment_type in ("Receive", "Pay") and self.party:
+			advance_payment_doctypes = frappe.get_hooks(
+				"advance_payment_customer_doctypes"
+			) + frappe.get_hooks("advance_payment_supplier_doctypes")
 			for d in self.get("references"):
-				if d.allocated_amount and d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"):
+				if d.allocated_amount and d.reference_doctype in advance_payment_doctypes:
 					frappe.get_doc(
 						d.reference_doctype, d.reference_name, for_update=True
 					).set_total_advance_paid()
@@ -1671,6 +1678,13 @@
 		condition += " and cost_center='%s'" % args.get("cost_center")
 		accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center"))
 
+	# dynamic dimension filters
+	active_dimensions = get_dimensions()[0]
+	for dim in active_dimensions:
+		if args.get(dim.fieldname):
+			condition += " and {0}='{1}'".format(dim.fieldname, args.get(dim.fieldname))
+			accounting_dimensions_filter.append(ple[dim.fieldname] == args.get(dim.fieldname))
+
 	date_fields_dict = {
 		"posting_date": ["from_posting_date", "to_posting_date"],
 		"due_date": ["from_due_date", "to_due_date"],
@@ -1904,6 +1918,12 @@
 	if doc and hasattr(doc, "cost_center") and doc.cost_center:
 		condition = " and cost_center='%s'" % cost_center
 
+	# dynamic dimension filters
+	active_dimensions = get_dimensions()[0]
+	for dim in active_dimensions:
+		if filters.get(dim.fieldname):
+			condition += " and {0}='{1}'".format(dim.fieldname, filters.get(dim.fieldname))
+
 	if party_account_currency == company_currency:
 		grand_total_field = "base_grand_total"
 		rounded_total_field = "base_rounded_total"
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index fc90c3d..99593de 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -95,6 +95,8 @@
 			this.frm.change_custom_button_type(__('Allocate'), null, 'default');
 		}
 
+		this.frm.trigger("set_query_for_dimension_filters");
+
 		// check for any running reconciliation jobs
 		if (this.frm.doc.receivable_payable_account) {
 			this.frm.call({
@@ -125,6 +127,25 @@
 		}
 
 	}
+	set_query_for_dimension_filters() {
+		frappe.call({
+			method: "erpnext.accounts.doctype.payment_reconciliation.payment_reconciliation.get_queries_for_dimension_filters",
+			args: {
+				company: this.frm.doc.company,
+			},
+			callback: (r) => {
+				if (!r.exc && r.message) {
+					r.message.forEach(x => {
+						this.frm.set_query(x.fieldname, () => {
+							return {
+								'filters': x.filters
+							};
+						});
+					});
+				}
+			}
+		});
+	}
 
 	company() {
 		this.frm.set_value('party', '');
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
index 93ed5c8..ff2aa6d 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
@@ -25,7 +25,9 @@
   "invoice_limit",
   "payment_limit",
   "bank_cash_account",
+  "accounting_dimensions_section",
   "cost_center",
+  "dimension_col_break",
   "sec_break1",
   "invoice_name",
   "invoices",
@@ -209,6 +211,18 @@
    "fieldname": "payment_name",
    "fieldtype": "Data",
    "label": "Filter on Payment"
+  },
+  {
+   "collapsible": 1,
+   "collapsible_depends_on": "eval: doc.invoices.length == 0",
+   "depends_on": "eval:doc.receivable_payable_account",
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions Filter"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
   }
  ],
  "hide_toolbar": 1,
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index ed0921b..b2716c9 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -10,6 +10,7 @@
 from frappe.utils import flt, fmt_money, get_link_to_form, getdate, nowdate, today
 
 import erpnext
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
 from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import (
 	is_any_doc_running,
 )
@@ -70,6 +71,7 @@
 		self.common_filter_conditions = []
 		self.accounting_dimension_filter_conditions = []
 		self.ple_posting_date_filter = []
+		self.dimensions = get_dimensions()[0]
 
 	def load_from_db(self):
 		# 'modified' attribute is required for `run_doc_method` to work properly.
@@ -172,6 +174,14 @@
 		if self.payment_name:
 			condition.update({"name": self.payment_name})
 
+		# pass dynamic dimension filter values to query builder
+		dimensions = {}
+		for x in self.dimensions:
+			dimension = x.fieldname
+			if self.get(dimension):
+				dimensions.update({dimension: self.get(dimension)})
+		condition.update({"accounting_dimensions": dimensions})
+
 		payment_entries = get_advance_payment_entries_for_regional(
 			self.party_type,
 			self.party,
@@ -185,66 +195,67 @@
 		return payment_entries
 
 	def get_jv_entries(self):
-		condition = self.get_conditions()
+		je = qb.DocType("Journal Entry")
+		jea = qb.DocType("Journal Entry Account")
+		conditions = self.get_journal_filter_conditions()
+
+		# Dimension filters
+		for x in self.dimensions:
+			dimension = x.fieldname
+			if self.get(dimension):
+				conditions.append(jea[dimension] == self.get(dimension))
 
 		if self.payment_name:
-			condition += f" and t1.name like '%%{self.payment_name}%%'"
+			conditions.append(je.name.like(f"%%{self.payment_name}%%"))
 
 		if self.get("cost_center"):
-			condition += f" and t2.cost_center = '{self.cost_center}' "
+			conditions.append(jea.cost_center == self.cost_center)
 
 		dr_or_cr = (
 			"credit_in_account_currency"
 			if erpnext.get_party_account_type(self.party_type) == "Receivable"
 			else "debit_in_account_currency"
 		)
+		conditions.append(jea[dr_or_cr].gt(0))
 
-		bank_account_condition = (
-			"t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1"
+		if self.bank_cash_account:
+			conditions.append(jea.against_account.like(f"%%{self.bank_cash_account}%%"))
+
+		journal_query = (
+			qb.from_(je)
+			.inner_join(jea)
+			.on(jea.parent == je.name)
+			.select(
+				ConstantColumn("Journal Entry").as_("reference_type"),
+				je.name.as_("reference_name"),
+				je.posting_date,
+				je.remark.as_("remarks"),
+				jea.name.as_("reference_row"),
+				jea[dr_or_cr].as_("amount"),
+				jea.is_advance,
+				jea.exchange_rate,
+				jea.account_currency.as_("currency"),
+				jea.cost_center.as_("cost_center"),
+			)
+			.where(
+				(je.docstatus == 1)
+				& (jea.party_type == self.party_type)
+				& (jea.party == self.party)
+				& (jea.account == self.receivable_payable_account)
+				& (
+					(jea.reference_type == "")
+					| (jea.reference_type.isnull())
+					| (jea.reference_type.isin(("Sales Order", "Purchase Order")))
+				)
+			)
+			.where(Criterion.all(conditions))
+			.orderby(je.posting_date)
 		)
 
-		limit = f"limit {self.payment_limit}" if self.payment_limit else " "
+		if self.payment_limit:
+			journal_query = journal_query.limit(self.payment_limit)
 
-		# nosemgrep
-		journal_entries = frappe.db.sql(
-			"""
-			select
-				"Journal Entry" as reference_type, t1.name as reference_name,
-				t1.posting_date, t1.remark as remarks, t2.name as reference_row,
-				{dr_or_cr} as amount, t2.is_advance, t2.exchange_rate,
-				t2.account_currency as currency, t2.cost_center as cost_center
-			from
-				`tabJournal Entry` t1, `tabJournal Entry Account` t2
-			where
-				t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
-				and t2.party_type = %(party_type)s and t2.party = %(party)s
-				and t2.account = %(account)s and {dr_or_cr} > 0 {condition}
-				and (t2.reference_type is null or t2.reference_type = '' or
-					(t2.reference_type in ('Sales Order', 'Purchase Order')
-						and t2.reference_name is not null and t2.reference_name != ''))
-				and (CASE
-					WHEN t1.voucher_type in ('Debit Note', 'Credit Note')
-					THEN 1=1
-					ELSE {bank_account_condition}
-				END)
-			order by t1.posting_date
-			{limit}
-			""".format(
-				**{
-					"dr_or_cr": dr_or_cr,
-					"bank_account_condition": bank_account_condition,
-					"condition": condition,
-					"limit": limit,
-				}
-			),
-			{
-				"party_type": self.party_type,
-				"party": self.party,
-				"account": self.receivable_payable_account,
-				"bank_cash_account": "%%%s%%" % self.bank_cash_account,
-			},
-			as_dict=1,
-		)
+		journal_entries = journal_query.run(as_dict=True)
 
 		return list(journal_entries)
 
@@ -298,6 +309,7 @@
 				min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None,
 				max_outstanding=-(self.maximum_payment_amount) if self.maximum_payment_amount else None,
 				get_payments=True,
+				accounting_dimensions=self.accounting_dimension_filter_conditions,
 			)
 
 			for inv in return_outstanding:
@@ -447,8 +459,15 @@
 				row = self.append("allocation", {})
 				row.update(entry)
 
+	def update_dimension_values_in_allocated_entries(self, res):
+		for x in self.dimensions:
+			dimension = x.fieldname
+			if self.get(dimension):
+				res[dimension] = self.get(dimension)
+		return res
+
 	def get_allocated_entry(self, pay, inv, allocated_amount):
-		return frappe._dict(
+		res = frappe._dict(
 			{
 				"reference_type": pay.get("reference_type"),
 				"reference_name": pay.get("reference_name"),
@@ -464,6 +483,9 @@
 			}
 		)
 
+		res = self.update_dimension_values_in_allocated_entries(res)
+		return res
+
 	def reconcile_allocations(self, skip_ref_details_update_for_pe=False):
 		adjust_allocations_for_taxes(self)
 		dr_or_cr = (
@@ -486,10 +508,10 @@
 				reconciled_entry.append(payment_details)
 
 		if entry_list:
-			reconcile_against_document(entry_list, skip_ref_details_update_for_pe)
+			reconcile_against_document(entry_list, skip_ref_details_update_for_pe, self.dimensions)
 
 		if dr_or_cr_notes:
-			reconcile_dr_cr_note(dr_or_cr_notes, self.company)
+			reconcile_dr_cr_note(dr_or_cr_notes, self.company, self.dimensions)
 
 	@frappe.whitelist()
 	def reconcile(self):
@@ -518,7 +540,7 @@
 		self.get_unreconciled_entries()
 
 	def get_payment_details(self, row, dr_or_cr):
-		return frappe._dict(
+		payment_details = frappe._dict(
 			{
 				"voucher_type": row.get("reference_type"),
 				"voucher_no": row.get("reference_name"),
@@ -541,6 +563,12 @@
 			}
 		)
 
+		for x in self.dimensions:
+			if row.get(x.fieldname):
+				payment_details[x.fieldname] = row.get(x.fieldname)
+
+		return payment_details
+
 	def check_mandatory_to_fetch(self):
 		for fieldname in ["company", "party_type", "party", "receivable_payable_account"]:
 			if not self.get(fieldname):
@@ -648,6 +676,13 @@
 		if not invoices_to_reconcile:
 			frappe.throw(_("No records found in Allocation table"))
 
+	def build_dimensions_filter_conditions(self):
+		ple = qb.DocType("Payment Ledger Entry")
+		for x in self.dimensions:
+			dimension = x.fieldname
+			if self.get(dimension):
+				self.accounting_dimension_filter_conditions.append(ple[dimension] == self.get(dimension))
+
 	def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):
 		self.common_filter_conditions.clear()
 		self.accounting_dimension_filter_conditions.clear()
@@ -671,40 +706,30 @@
 			if self.to_payment_date:
 				self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_payment_date))
 
-	def get_conditions(self, get_payments=False):
-		condition = " and company = '{0}' ".format(self.company)
+		self.build_dimensions_filter_conditions()
 
-		if self.get("cost_center") and get_payments:
-			condition = " and cost_center = '{0}' ".format(self.cost_center)
+	def get_journal_filter_conditions(self):
+		conditions = []
+		je = qb.DocType("Journal Entry")
+		jea = qb.DocType("Journal Entry Account")
+		conditions.append(je.company == self.company)
 
-		condition += (
-			" and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date))
-			if self.from_payment_date
-			else ""
-		)
-		condition += (
-			" and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date))
-			if self.to_payment_date
-			else ""
-		)
+		if self.from_payment_date:
+			conditions.append(je.posting_date.gte(self.from_payment_date))
+
+		if self.to_payment_date:
+			conditions.append(je.posting_date.lte(self.to_payment_date))
 
 		if self.minimum_payment_amount:
-			condition += (
-				" and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount))
-				if get_payments
-				else " and total_debit >= {0}".format(flt(self.minimum_payment_amount))
-			)
+			conditions.append(je.total_debit.gte(self.minimum_payment_amount))
+
 		if self.maximum_payment_amount:
-			condition += (
-				" and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount))
-				if get_payments
-				else " and total_debit <= {0}".format(flt(self.maximum_payment_amount))
-			)
+			conditions.append(je.total_debit.lte(self.maximum_payment_amount))
 
-		return condition
+		return conditions
 
 
-def reconcile_dr_cr_note(dr_cr_notes, company):
+def reconcile_dr_cr_note(dr_cr_notes, company, active_dimensions=None):
 	for inv in dr_cr_notes:
 		voucher_type = "Credit Note" if inv.voucher_type == "Sales Invoice" else "Debit Note"
 
@@ -754,6 +779,15 @@
 			}
 		)
 
+		# Credit Note(JE) will inherit the same dimension values as payment
+		dimensions_dict = frappe._dict()
+		if active_dimensions:
+			for dim in active_dimensions:
+				dimensions_dict[dim.fieldname] = inv.get(dim.fieldname)
+
+		jv.accounts[0].update(dimensions_dict)
+		jv.accounts[1].update(dimensions_dict)
+
 		jv.flags.ignore_mandatory = True
 		jv.flags.ignore_exchange_rate = True
 		jv.remark = None
@@ -787,9 +821,27 @@
 				inv.against_voucher,
 				None,
 				inv.cost_center,
+				dimensions_dict,
 			)
 
 
 @erpnext.allow_regional
 def adjust_allocations_for_taxes(doc):
 	pass
+
+
+@frappe.whitelist()
+def get_queries_for_dimension_filters(company: str = None):
+	dimensions_with_filters = []
+	for d in get_dimensions()[0]:
+		filters = {}
+		meta = frappe.get_meta(d.document_type)
+		if meta.has_field("company") and company:
+			filters.update({"company": company})
+
+		if meta.is_tree:
+			filters.update({"is_group": 0})
+
+		dimensions_with_filters.append({"fieldname": d.fieldname, "filters": filters})
+
+	return dimensions_with_filters
diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
index 491c678..3f85b21 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
@@ -24,7 +24,9 @@
   "difference_account",
   "exchange_rate",
   "currency",
-  "cost_center"
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break"
  ],
  "fields": [
   {
@@ -157,12 +159,21 @@
    "fieldname": "gain_loss_posting_date",
    "fieldtype": "Date",
    "label": "Difference Posting Date"
+  },
+  {
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
   }
  ],
  "is_virtual": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-17 17:33:38.612615",
+ "modified": "2023-12-14 13:38:26.104150",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Reconciliation Allocation",
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index 9772b94..839348a 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -169,6 +169,13 @@
 		elif self.payment_channel == "Phone":
 			self.request_phone_payment()
 
+		advance_payment_doctypes = frappe.get_hooks(
+			"advance_payment_customer_doctypes"
+		) + frappe.get_hooks("advance_payment_supplier_doctypes")
+		if self.reference_doctype in advance_payment_doctypes:
+			# set advance payment status
+			ref_doc.set_total_advance_paid()
+
 	def request_phone_payment(self):
 		controller = _get_payment_gateway_controller(self.payment_gateway)
 		request_amount = self.get_request_amount()
@@ -207,6 +214,14 @@
 		self.check_if_payment_entry_exists()
 		self.set_as_cancelled()
 
+		ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
+		advance_payment_doctypes = frappe.get_hooks(
+			"advance_payment_customer_doctypes"
+		) + frappe.get_hooks("advance_payment_supplier_doctypes")
+		if self.reference_doctype in advance_payment_doctypes:
+			# set advance payment status
+			ref_doc.set_total_advance_paid()
+
 	def make_invoice(self):
 		ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
 		if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart":
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index e542d3c..ca031f0 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -371,7 +371,7 @@
 			if d.get("qty") > 0:
 				frappe.throw(
 					_(
-						"Row #{}: You cannot add postive quantities in a return invoice. Please remove item {} to complete the return."
+						"Row #{}: You cannot add positive quantities in a return invoice. Please remove item {} to complete the return."
 					).format(d.idx, frappe.bold(d.item_code)),
 					title=_("Invalid Item"),
 				)
@@ -793,7 +793,7 @@
 		invoices = json.loads(invoices)
 
 	if len(invoices) == 0:
-		frappe.throw(_("Atleast one invoice has to be selected."))
+		frappe.throw(_("At least one invoice has to be selected."))
 
 	merge_log = frappe.new_doc("POS Invoice Merge Log")
 	merge_log.posting_date = getdate(nowdate())
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py
index 30f3e0c..c1add57 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py
@@ -132,7 +132,7 @@
 
 		if len(customer_groups) != len(set(customer_groups)):
 			frappe.throw(
-				_("Duplicate customer group found in the cutomer group table"),
+				_("Duplicate customer group found in the customer group table"),
 				title=_("Duplicate Customer Group"),
 			)
 
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
index e8e8044..61c01a4 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
@@ -339,7 +339,7 @@
   {
    "fieldname": "valid_upto",
    "fieldtype": "Date",
-   "label": "Valid Upto"
+   "label": "Valid Up To"
   },
   {
    "fieldname": "col_break1",
@@ -608,7 +608,7 @@
  "icon": "fa fa-gift",
  "idx": 1,
  "links": [],
- "modified": "2023-02-14 04:53:34.887358",
+ "modified": "2024-01-24 02:20:26.145996",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Pricing Rule",
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index ca70490..300692f 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -193,7 +193,7 @@
 
 	def validate_applicable_for_selling_or_buying(self):
 		if not self.selling and not self.buying:
-			throw(_("Atleast one of the Selling or Buying must be selected"))
+			throw(_("At least one of the Selling or Buying must be selected"))
 
 		if not self.selling and self.applicable_for in [
 			"Customer",
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json
index 1d68b23..7fdfdcd 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json
@@ -232,7 +232,7 @@
   {
    "fieldname": "valid_upto",
    "fieldtype": "Date",
-   "label": "Valid Upto"
+   "label": "Valid Up To"
   },
   {
    "fieldname": "column_break_26",
@@ -278,7 +278,7 @@
   }
  ],
  "links": [],
- "modified": "2021-05-06 16:20:22.039078",
+ "modified": "2024-01-24 02:20:26.145996",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Promotional Scheme",
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 9cf4e4f..26984d9 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -64,6 +64,7 @@
   "warehouse",
   "from_warehouse",
   "quality_inspection",
+  "add_serial_batch_bundle",
   "serial_and_batch_bundle",
   "serial_no",
   "col_br_wh",
@@ -913,12 +914,18 @@
    "fieldtype": "Link",
    "label": "WIP Composite Asset",
    "options": "Asset"
+  },
+  {
+   "depends_on": "eval:parent.update_stock === 1",
+   "fieldname": "add_serial_batch_bundle",
+   "fieldtype": "Button",
+   "label": "Add Serial / Batch No"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-12-25 22:00:28.043555",
+ "modified": "2024-01-21 19:46:25.537861",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json
index 97fd4d0..afa8bcb 100644
--- a/erpnext/accounts/doctype/subscription/subscription.json
+++ b/erpnext/accounts/doctype/subscription/subscription.json
@@ -51,7 +51,7 @@
    "fieldtype": "Select",
    "label": "Status",
    "no_copy": 1,
-   "options": "\nTrialling\nActive\nPast Due Date\nCancelled\nUnpaid\nCompleted",
+   "options": "\nTrialing\nActive\nPast Due Date\nCancelled\nUnpaid\nCompleted",
    "read_only": 1
   },
   {
@@ -267,7 +267,7 @@
    "link_fieldname": "subscription"
   }
  ],
- "modified": "2023-12-28 17:20:42.687789",
+ "modified": "2024-01-24 02:20:26.145996",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Subscription",
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index 6d27806..9f19366 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -78,9 +78,7 @@
 		purchase_tax_template: DF.Link | None
 		sales_tax_template: DF.Link | None
 		start_date: DF.Date | None
-		status: DF.Literal[
-			"", "Trialling", "Active", "Past Due Date", "Cancelled", "Unpaid", "Completed"
-		]
+		status: DF.Literal["", "Trialing", "Active", "Past Due Date", "Cancelled", "Unpaid", "Completed"]
 		submit_invoice: DF.Check
 		trial_period_end: DF.Date | None
 		trial_period_start: DF.Date | None
@@ -233,7 +231,7 @@
 		Sets the status of the `Subscription`
 		"""
 		if self.is_trialling():
-			self.status = "Trialling"
+			self.status = "Trialing"
 		elif (
 			self.status == "Active" and self.end_date and getdate(posting_date) > getdate(self.end_date)
 		):
diff --git a/erpnext/accounts/doctype/subscription/subscription_list.js b/erpnext/accounts/doctype/subscription/subscription_list.js
index 6490ff3..ea48b53 100644
--- a/erpnext/accounts/doctype/subscription/subscription_list.js
+++ b/erpnext/accounts/doctype/subscription/subscription_list.js
@@ -1,7 +1,7 @@
 frappe.listview_settings['Subscription'] = {
 	get_indicator: function(doc) {
-		if(doc.status === 'Trialling') {
-			return [__("Trialling"), "green"];
+		if(doc.status === 'Trialing') {
+			return [__("Trialing"), "green"];
 		} else if(doc.status === 'Active') {
 			return [__("Active"), "green"];
 		} else if(doc.status === 'Completed') {
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index a46642a..89be543 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -46,7 +46,7 @@
 			get_date_str(subscription.current_invoice_end),
 		)
 		self.assertEqual(subscription.invoices, [])
-		self.assertEqual(subscription.status, "Trialling")
+		self.assertEqual(subscription.status, "Trialing")
 
 	def test_create_subscription_without_trial_with_correct_period(self):
 		subscription = create_subscription()
diff --git a/erpnext/accounts/form_tour/accounts_settings/accounts_settings.json b/erpnext/accounts/form_tour/accounts_settings/accounts_settings.json
index e2bf50d..b41012c 100644
--- a/erpnext/accounts/form_tour/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/form_tour/accounts_settings/accounts_settings.json
@@ -4,7 +4,7 @@
  "doctype": "Form Tour",
  "idx": 0,
  "is_standard": 1,
- "modified": "2021-06-29 17:00:26.145996",
+ "modified": "2024-01-24 02:20:26.145996",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
@@ -82,7 +82,7 @@
    "label": "Accounts Frozen Till Date",
    "parent_field": "",
    "position": "Right",
-   "title": "Accounts Frozen Upto"
+   "title": "Accounts Frozen Up To"
   },
   {
    "description": "Users with this Role are allowed to set frozen accounts and create/modify accounting entries against frozen accounts.",
diff --git a/erpnext/accounts/report/account_balance/account_balance.js b/erpnext/accounts/report/account_balance/account_balance.js
index 5681be9..ab5dce8 100644
--- a/erpnext/accounts/report/account_balance/account_balance.js
+++ b/erpnext/accounts/report/account_balance/account_balance.js
@@ -39,7 +39,7 @@
 				{ "value": "Asset Received But Not Billed", "label": __("Asset Received But Not Billed") },
 				{ "value": "Bank", "label": __("Bank") },
 				{ "value": "Cash", "label": __("Cash") },
-				{ "value": "Chargeble", "label": __("Chargeble") },
+				{ "value": "Chargeable", "label": __("Chargeable") },
 				{ "value": "Capital Work in Progress", "label": __("Capital Work in Progress") },
 				{ "value": "Cost of Goods Sold", "label": __("Cost of Goods Sold") },
 				{ "value": "Depreciation", "label": __("Depreciation") },
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
index ed3b991..7d8d33c 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
@@ -10,10 +10,8 @@
 
 	<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
 	<h4 class="text-center">
-		{% if (filters.customer_name) { %}
-			{%= filters.customer_name %}
-		{% } else { %}
-			{%= filters.customer || filters.supplier %}
+		{% if (filters.party) { %}
+			{%= __(filters.party) %}
 		{% } %}
 	</h4>
 	<h6 class="text-center">
@@ -141,7 +139,7 @@
 						<th style="width: 24%">{%= __("Reference") %}</th>
 					{% } %}
 					{% if(!filters.show_future_payments) { %}
-						<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
+						<th style="width: 20%">{%= (filters.party) ? __("Remarks"): __("Party") %}</th>
 					{% } %}
 					<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
 					{% if(!filters.show_future_payments) { %}
@@ -158,7 +156,7 @@
 						<th style="width: 10%">{%= __("Remaining Balance") %}</th>
 					{% } %}
 				{% } else { %}
-					<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
+					<th style="width: 40%">{%= (filters.party) ? __("Remarks"): __("Party") %}</th>
 					<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
 					<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
 					<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
@@ -187,7 +185,7 @@
 
 						{% if(!filters.show_future_payments) { %}
 						<td>
-							{% if(!(filters.customer || filters.supplier)) { %}
+							{% if(!(filters.party)) { %}
 								{%= data[i]["party"] %}
 								{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
 									<br> {%= data[i]["customer_name"] %}
@@ -260,7 +258,7 @@
 					{% if(data[i]["party"]|| "&nbsp;") { %}
 						{% if(!data[i]["is_total_row"]) { %}
 							<td>
-								{% if(!(filters.customer || filters.supplier)) { %}
+								{% if(!(filters.party)) { %}
 									{%= data[i]["party"] %}
 									{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
 										<br> {%= data[i]["customer_name"] %}
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 004a929..aadd873 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -8,17 +8,7 @@
 
 import frappe
 from frappe import _
-from frappe.utils import (
-	add_days,
-	add_months,
-	cint,
-	cstr,
-	flt,
-	formatdate,
-	get_first_day,
-	getdate,
-	today,
-)
+from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate
 
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_accounting_dimensions,
@@ -53,8 +43,6 @@
 		year_start_date = getdate(period_start_date)
 		year_end_date = getdate(period_end_date)
 
-	year_end_date = getdate(today()) if year_end_date > getdate(today()) else year_end_date
-
 	months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
 
 	period_list = []
diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
index d045d91..4a80dd0 100644
--- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
+++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
@@ -46,12 +46,10 @@
 
 	out = []
 	for name, details in gle_map.items():
-		tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
-		bill_no, bill_date = "", ""
-		tax_withholding_category = tax_category_map.get(name)
-		rate = tax_rate_map.get(tax_withholding_category)
-
 		for entry in details:
+			tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
+			tax_withholding_category, rate = None, None
+			bill_no, bill_date = "", ""
 			party = entry.party or entry.against
 			posting_date = entry.posting_date
 			voucher_type = entry.voucher_type
@@ -61,12 +59,19 @@
 				if party_list:
 					party = party_list[0]
 
-			if not tax_withholding_category:
-				tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
-				rate = tax_rate_map.get(tax_withholding_category)
-
-			if entry.account in tds_accounts:
+			if entry.account in tds_accounts.keys():
 				tax_amount += entry.credit - entry.debit
+				# infer tax withholding category from the account if it's the single account for this category
+				tax_withholding_category = tds_accounts.get(entry.account)
+				rate = tax_rate_map.get(tax_withholding_category)
+				# or else the consolidated value from the voucher document
+				if not tax_withholding_category:
+					# or else from the party default
+					tax_withholding_category = tax_category_map.get(name)
+					rate = tax_rate_map.get(tax_withholding_category)
+				if not tax_withholding_category:
+					tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
+					rate = tax_rate_map.get(tax_withholding_category)
 
 			if net_total_map.get(name):
 				if voucher_type == "Journal Entry" and tax_amount and rate:
@@ -80,41 +85,41 @@
 			else:
 				total_amount += entry.credit
 
-		if tax_amount:
-			if party_map.get(party, {}).get("party_type") == "Supplier":
-				party_name = "supplier_name"
-				party_type = "supplier_type"
-			else:
-				party_name = "customer_name"
-				party_type = "customer_type"
+			if tax_amount:
+				if party_map.get(party, {}).get("party_type") == "Supplier":
+					party_name = "supplier_name"
+					party_type = "supplier_type"
+				else:
+					party_name = "customer_name"
+					party_type = "customer_type"
 
-			row = {
-				"pan"
-				if frappe.db.has_column(filters.party_type, "pan")
-				else "tax_id": party_map.get(party, {}).get("pan"),
-				"party": party_map.get(party, {}).get("name"),
-			}
-
-			if filters.naming_series == "Naming Series":
-				row.update({"party_name": party_map.get(party, {}).get(party_name)})
-
-			row.update(
-				{
-					"section_code": tax_withholding_category or "",
-					"entity_type": party_map.get(party, {}).get(party_type),
-					"rate": rate,
-					"total_amount": total_amount,
-					"grand_total": grand_total,
-					"base_total": base_total,
-					"tax_amount": tax_amount,
-					"transaction_date": posting_date,
-					"transaction_type": voucher_type,
-					"ref_no": name,
-					"supplier_invoice_no": bill_no,
-					"supplier_invoice_date": bill_date,
+				row = {
+					"pan"
+					if frappe.db.has_column(filters.party_type, "pan")
+					else "tax_id": party_map.get(party, {}).get("pan"),
+					"party": party_map.get(party, {}).get("name"),
 				}
-			)
-			out.append(row)
+
+				if filters.naming_series == "Naming Series":
+					row.update({"party_name": party_map.get(party, {}).get(party_name)})
+
+				row.update(
+					{
+						"section_code": tax_withholding_category or "",
+						"entity_type": party_map.get(party, {}).get(party_type),
+						"rate": rate,
+						"total_amount": total_amount,
+						"grand_total": grand_total,
+						"base_total": base_total,
+						"tax_amount": tax_amount,
+						"transaction_date": posting_date,
+						"transaction_type": voucher_type,
+						"ref_no": name,
+						"supplier_invoice_no": bill_no,
+						"supplier_invoice_date": bill_date,
+					}
+				)
+				out.append(row)
 
 	out.sort(key=lambda x: x["section_code"])
 
@@ -282,11 +287,20 @@
 	journal_entry_party_map = frappe._dict()
 	bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")
 
-	tds_accounts = frappe.get_all(
-		"Tax Withholding Account", {"company": filters.get("company")}, pluck="account"
+	_tds_accounts = frappe.get_all(
+		"Tax Withholding Account",
+		{"company": filters.get("company")},
+		["account", "parent"],
 	)
+	tds_accounts = {}
+	for tds_acc in _tds_accounts:
+		# if it turns out not to be the only tax withholding category, then don't include in the map
+		if tds_accounts.get(tds_acc["account"]):
+			tds_accounts[tds_acc["account"]] = None
+		else:
+			tds_accounts[tds_acc["account"]] = tds_acc["parent"]
 
-	tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True)
+	tds_docs = get_tds_docs_query(filters, bank_accounts, list(tds_accounts.keys())).run(as_dict=True)
 
 	for d in tds_docs:
 		if d.voucher_type == "Purchase Invoice":
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 19095bc..65b3aba 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -453,7 +453,19 @@
 	return cc.name
 
 
-def reconcile_against_document(args, skip_ref_details_update_for_pe=False):  # nosemgrep
+def _build_dimensions_dict_for_exc_gain_loss(
+	entry: dict | object = None, active_dimensions: list = None
+):
+	dimensions_dict = frappe._dict()
+	if entry and active_dimensions:
+		for dim in active_dimensions:
+			dimensions_dict[dim.fieldname] = entry.get(dim.fieldname)
+	return dimensions_dict
+
+
+def reconcile_against_document(
+	args, skip_ref_details_update_for_pe=False, active_dimensions=None
+):  # nosemgrep
 	"""
 	Cancel PE or JV, Update against document, split if required and resubmit
 	"""
@@ -482,6 +494,8 @@
 			check_if_advance_entry_modified(entry)
 			validate_allocated_amount(entry)
 
+			dimensions_dict = _build_dimensions_dict_for_exc_gain_loss(entry, active_dimensions)
+
 			# update ref in advance entry
 			if voucher_type == "Journal Entry":
 				referenced_row = update_reference_in_journal_entry(entry, doc, do_not_save=False)
@@ -489,10 +503,14 @@
 				# amount and account in args
 				# referenced_row is used to deduplicate gain/loss journal
 				entry.update({"referenced_row": referenced_row})
-				doc.make_exchange_gain_loss_journal([entry])
+				doc.make_exchange_gain_loss_journal([entry], dimensions_dict)
 			else:
 				referenced_row = update_reference_in_payment_entry(
-					entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe
+					entry,
+					doc,
+					do_not_save=True,
+					skip_ref_details_update_for_pe=skip_ref_details_update_for_pe,
+					dimensions_dict=dimensions_dict,
 				)
 
 		doc.save(ignore_permissions=True)
@@ -600,7 +618,10 @@
 	jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
 
 	# Update Advance Paid in SO/PO since they might be getting unlinked
-	if jv_detail.get("reference_type") in ("Sales Order", "Purchase Order"):
+	advance_payment_doctypes = frappe.get_hooks(
+		"advance_payment_customer_doctypes"
+	) + frappe.get_hooks("advance_payment_supplier_doctypes")
+	if jv_detail.get("reference_type") in advance_payment_doctypes:
 		frappe.get_doc(jv_detail.reference_type, jv_detail.reference_name).set_total_advance_paid()
 
 	if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0:
@@ -654,7 +675,7 @@
 
 
 def update_reference_in_payment_entry(
-	d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False
+	d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False, dimensions_dict=None
 ):
 	reference_details = {
 		"reference_doctype": d.against_voucher_type,
@@ -667,13 +688,17 @@
 		else payment_entry.get_exchange_rate(),
 		"exchange_gain_loss": d.difference_amount,
 		"account": d.account,
+		"dimensions": d.dimensions,
 	}
 
 	if d.voucher_detail_no:
 		existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
 
 		# Update Advance Paid in SO/PO since they are getting unlinked
-		if existing_row.get("reference_doctype") in ("Sales Order", "Purchase Order"):
+		advance_payment_doctypes = frappe.get_hooks(
+			"advance_payment_customer_doctypes"
+		) + frappe.get_hooks("advance_payment_supplier_doctypes")
+		if existing_row.get("reference_doctype") in advance_payment_doctypes:
 			frappe.get_doc(
 				existing_row.reference_doctype, existing_row.reference_name
 			).set_total_advance_paid()
@@ -699,8 +724,9 @@
 	if not skip_ref_details_update_for_pe:
 		payment_entry.set_missing_ref_details()
 	payment_entry.set_amounts()
+
 	payment_entry.make_exchange_gain_loss_journal(
-		frappe._dict({"difference_posting_date": d.difference_posting_date})
+		frappe._dict({"difference_posting_date": d.difference_posting_date}), dimensions_dict
 	)
 
 	if not do_not_save:
@@ -2042,6 +2068,7 @@
 	ref2_dn,
 	ref2_detail_no,
 	cost_center,
+	dimensions,
 ) -> str:
 	journal_entry = frappe.new_doc("Journal Entry")
 	journal_entry.voucher_type = "Exchange Gain Or Loss"
@@ -2075,7 +2102,8 @@
 			dr_or_cr + "_in_account_currency": 0,
 		}
 	)
-
+	if dimensions:
+		journal_account.update(dimensions)
 	journal_entry.append("accounts", journal_account)
 
 	journal_account = frappe._dict(
@@ -2091,7 +2119,8 @@
 			reverse_dr_or_cr: abs(exc_gain_loss),
 		}
 	)
-
+	if dimensions:
+		journal_account.update(dimensions)
 	journal_entry.append("accounts", journal_account)
 
 	journal_entry.save()
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 7357249..166e8c4 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -519,14 +519,11 @@
 			movement.cancel()
 
 	def cancel_capitalization(self):
-		asset_capitalization = frappe.db.get_value(
-			"Asset Capitalization",
-			{"target_asset": self.name, "docstatus": 1, "entry_type": "Capitalization"},
-		)
-
-		if asset_capitalization:
-			asset_capitalization = frappe.get_doc("Asset Capitalization", asset_capitalization)
-			asset_capitalization.cancel()
+		if self.capitalized_in:
+			self.db_set("capitalized_in", None)
+			asset_capitalization = frappe.get_doc("Asset Capitalization", self.capitalized_in)
+			if asset_capitalization.docstatus == 1:
+				asset_capitalization.cancel()
 
 	def delete_depreciation_entries(self):
 		if self.calculate_depreciation:
@@ -1011,7 +1008,7 @@
 		assets = json.loads(assets)
 
 	if len(assets) == 0:
-		frappe.throw(_("Atleast one asset has to be selected."))
+		frappe.throw(_("At least one asset has to be selected."))
 
 	asset_movement = frappe.new_doc("Asset Movement")
 	asset_movement.quantity = len(assets)
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index a93af94..df4593b 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -561,6 +561,8 @@
 def reverse_depreciation_entry_made_after_disposal(asset, date):
 	for row in asset.get("finance_books"):
 		asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active", row.finance_book)
+		if not asset_depr_schedule_doc:
+			continue
 
 		for schedule_idx, schedule in enumerate(asset_depr_schedule_doc.get("depreciation_schedule")):
 			if schedule.schedule_date == date:
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index cad74df..5e251a5 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -146,6 +146,7 @@
 	def cancel_target_asset(self):
 		if self.entry_type == "Capitalization" and self.target_asset:
 			asset_doc = frappe.get_doc("Asset", self.target_asset)
+			frappe.db.set_value("Asset", self.target_asset, "capitalized_in", None)
 			if asset_doc.docstatus == 1:
 				asset_doc.cancel()
 
diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
index 063fe99..780f61f 100644
--- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
@@ -40,7 +40,7 @@
 			if getdate(task.next_due_date) < getdate(nowdate()):
 				task.maintenance_status = "Overdue"
 			if not task.assign_to and self.docstatus == 0:
-				throw(_("Row #{}: Please asign task to a member.").format(task.idx))
+				throw(_("Row #{}: Please assign task to a member.").format(task.idx))
 
 	def on_update(self):
 		for task in self.get("asset_maintenance_tasks"):
diff --git a/erpnext/assets/onboarding_step/asset_category/asset_category.json b/erpnext/assets/onboarding_step/asset_category/asset_category.json
index 58f322e..a1b68ba 100644
--- a/erpnext/assets/onboarding_step/asset_category/asset_category.json
+++ b/erpnext/assets/onboarding_step/asset_category/asset_category.json
@@ -2,14 +2,14 @@
  "action": "Show Form Tour",
  "action_label": "Let's review existing Asset Category",
  "creation": "2021-08-13 14:26:18.656303",
- "description": "# Asset Category\n\nAn Asset Category classifies different assets of a Company.\n\nYou can create an Asset Category based on the type of assets. For example, all your desktops and laptops can be part of an Asset Category named \"Electronic Equipments\". Create a separate category for furniture. Also, you can update default properties for each category, like:\n - Depreciation type and duration\n - Fixed asset account\n - Depreciation account\n",
+ "description": "# Asset Category\n\nAn Asset Category classifies different assets of a Company.\n\nYou can create an Asset Category based on the type of assets. For example, all your desktops and laptops can be part of an Asset Category named \"Electronic Equipment\". Create a separate category for furniture. Also, you can update default properties for each category, like:\n - Depreciation type and duration\n - Fixed asset account\n - Depreciation account\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-11-23 10:02:03.242127",
+ "modified": "2024-01-24 02:20:26.145996",
  "modified_by": "Administrator",
  "name": "Asset Category",
  "owner": "Administrator",
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 45811a9..e689b05 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -202,7 +202,7 @@
 					"values": [flt(d.get("asset_value"), 2) for d in labels_values_map.values()],
 				},
 				{
-					"name": _("Depreciatied Amount"),
+					"name": _("Depreciated Amount"),
 					"values": [flt(d.get("depreciated_amount"), 2) for d in labels_values_map.values()],
 				},
 			],
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index f74df66..9da49a7 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -134,6 +134,7 @@
   "more_info_tab",
   "tracking_section",
   "status",
+  "advance_payment_status",
   "column_break_75",
   "per_billed",
   "per_received",
@@ -1269,13 +1270,25 @@
    "fieldtype": "Tab Break",
    "label": "Connections",
    "show_dashboard": 1
+  },
+  {
+   "fieldname": "advance_payment_status",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "in_standard_filter": 1,
+   "label": "Advance Payment Status",
+   "no_copy": 1,
+   "oldfieldname": "status",
+   "oldfieldtype": "Select",
+   "options": "Not Initiated\nInitiated\nPartially Paid\nFully Paid",
+   "print_hide": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-10-01 20:58:07.851037",
+ "modified": "2023-10-10 13:37:40.158761",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order",
@@ -1330,4 +1343,4 @@
  "timeline_field": "supplier",
  "title_field": "supplier_name",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index b830e7d..4efbb27 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -215,6 +215,10 @@
 
 		self.validate_fg_item_for_subcontracting()
 		self.set_received_qty_for_drop_ship_items()
+
+		if not self.advance_payment_status:
+			self.advance_payment_status = "Not Initiated"
+
 		validate_inter_company_party(
 			self.doctype, self.supplier, self.company, self.inter_company_order_reference
 		)
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_list.js b/erpnext/buying/doctype/purchase_order/purchase_order_list.js
index 6594746..d39d7f9 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order_list.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order_list.js
@@ -1,6 +1,6 @@
 frappe.listview_settings['Purchase Order'] = {
 	add_fields: ["base_grand_total", "company", "currency", "supplier",
-		"supplier_name", "per_received", "per_billed", "status"],
+		"supplier_name", "per_received", "per_billed", "status", "advance_payment_status"],
 	get_indicator: function (doc) {
 		if (doc.status === "Closed") {
 			return [__("Closed"), "green", "status,=,Closed"];
@@ -8,6 +8,8 @@
 			return [__("On Hold"), "orange", "status,=,On Hold"];
 		} else if (doc.status === "Delivered") {
 			return [__("Delivered"), "green", "status,=,Closed"];
+		} else if (doc.advance_payment_status == "Initiated") {
+			return [__("To Pay"), "gray", "advance_payment_status,=,Initiated"];
 		} else if (flt(doc.per_received, 2) < 100 && doc.status !== "Closed") {
 			if (flt(doc.per_billed, 2) < 100) {
 				return [__("To Receive and Bill"), "orange",
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 9b382bb..5405799 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -1021,6 +1021,33 @@
 
 		self.assertTrue(frappe.db.get_value("Subcontracting Order", {"purchase_order": po.name}))
 
+	def test_purchase_order_advance_payment_status(self):
+		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+		from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
+
+		po = create_purchase_order()
+		self.assertEqual(
+			frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Not Initiated"
+		)
+
+		pr = make_payment_request(dt=po.doctype, dn=po.name, submit_doc=True, return_doc=True)
+		self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Initiated")
+
+		pe = get_payment_entry(po.doctype, po.name).save().submit()
+		self.assertEqual(
+			frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Fully Paid"
+		)
+
+		pe.reload()
+		pe.cancel()
+		self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Initiated")
+
+		pr.reload()
+		pr.cancel()
+		self.assertEqual(
+			frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Not Initiated"
+		)
+
 
 def prepare_data_for_internal_transfer():
 	from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
index 91506c0..3bf4f2b 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
@@ -54,7 +54,7 @@
 			"fieldtype": "MultiSelectList",
 			"width": "80",
 			get_data: function(txt) {
-				let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"]
+				let status = ["To Pay", "To Bill", "To Receive", "To Receive and Bill", "Completed"]
 				let options = []
 				for (let option of status){
 					options.push({
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 0c554f2..7dfcb30 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -7,6 +7,7 @@
 import frappe
 from frappe import _, bold, qb, throw
 from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied
+from frappe.query_builder import Criterion
 from frappe.query_builder.custom import ConstantColumn
 from frappe.query_builder.functions import Abs, Sum
 from frappe.utils import (
@@ -21,12 +22,14 @@
 	get_link_to_form,
 	getdate,
 	nowdate,
+	parse_json,
 	today,
 )
 
 import erpnext
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_accounting_dimensions,
+	get_dimensions,
 )
 from erpnext.accounts.doctype.pricing_rule.utils import (
 	apply_pricing_rule_for_free_items,
@@ -831,6 +834,37 @@
 
 			self.extend("taxes", get_taxes_and_charges(tax_master_doctype, self.get("taxes_and_charges")))
 
+	def append_taxes_from_item_tax_template(self):
+		if not frappe.db.get_single_value("Accounts Settings", "add_taxes_from_item_tax_template"):
+			return
+
+		for row in self.items:
+			item_tax_rate = row.get("item_tax_rate")
+			if not item_tax_rate:
+				continue
+
+			if isinstance(item_tax_rate, str):
+				item_tax_rate = parse_json(item_tax_rate)
+
+			for account_head, rate in item_tax_rate.items():
+				row = self.get_tax_row(account_head)
+
+				if not row:
+					self.append(
+						"taxes",
+						{
+							"charge_type": "On Net Total",
+							"account_head": account_head,
+							"rate": 0,
+							"description": account_head,
+						},
+					)
+
+	def get_tax_row(self, account_head):
+		for row in self.taxes:
+			if row.account_head == account_head:
+				return row
+
 	def set_other_charges(self):
 		self.set("taxes", [])
 		self.set_taxes()
@@ -1216,7 +1250,9 @@
 					return True
 		return False
 
-	def make_exchange_gain_loss_journal(self, args: dict = None) -> None:
+	def make_exchange_gain_loss_journal(
+		self, args: dict = None, dimensions_dict: dict = None
+	) -> None:
 		"""
 		Make Exchange Gain/Loss journal for Invoices and Payments
 		"""
@@ -1271,6 +1307,7 @@
 									self.name,
 									arg.get("referenced_row"),
 									arg.get("cost_center"),
+									dimensions_dict,
 								)
 								frappe.msgprint(
 									_("Exchange Gain/Loss amount has been booked through {0}").format(
@@ -1351,6 +1388,7 @@
 							self.name,
 							d.idx,
 							self.cost_center,
+							dimensions_dict,
 						)
 						frappe.msgprint(
 							_("Exchange Gain/Loss amount has been booked through {0}").format(
@@ -1415,7 +1453,13 @@
 		if lst:
 			from erpnext.accounts.utils import reconcile_against_document
 
-			reconcile_against_document(lst)
+			# pass dimension values to utility method
+			active_dimensions = get_dimensions()[0]
+			for x in lst:
+				for dim in active_dimensions:
+					if self.get(dim.fieldname):
+						x.update({dim.fieldname: self.get(dim.fieldname)})
+			reconcile_against_document(lst, active_dimensions=active_dimensions)
 
 	def on_cancel(self):
 		from erpnext.accounts.doctype.bank_transaction.bank_transaction import (
@@ -1749,7 +1793,10 @@
 
 	def set_total_advance_paid(self):
 		ple = frappe.qb.DocType("Payment Ledger Entry")
-		party = self.customer if self.doctype == "Sales Order" else self.supplier
+		if self.doctype in frappe.get_hooks("advance_payment_customer_doctypes"):
+			party = self.customer
+		if self.doctype in frappe.get_hooks("advance_payment_supplier_doctypes"):
+			party = self.supplier
 		advance = (
 			frappe.qb.from_(ple)
 			.select(ple.account_currency, Abs(Sum(ple.amount_in_account_currency)).as_("amount"))
@@ -1763,6 +1810,8 @@
 			.run(as_dict=True)
 		)
 
+		advance_paid, order_total = None, None
+
 		if advance:
 			advance = advance[0]
 
@@ -1791,7 +1840,38 @@
 					).format(formatted_advance_paid, self.name, formatted_order_total)
 				)
 
-			frappe.db.set_value(self.doctype, self.name, "advance_paid", advance_paid)
+			self.db_set("advance_paid", advance_paid)
+
+		self.set_advance_payment_status(advance_paid, order_total)
+
+	def set_advance_payment_status(
+		self, advance_paid: float | None = None, order_total: float | None = None
+	):
+		new_status = None
+		# if money is paid set the paid states
+		if advance_paid:
+			new_status = "Partially Paid" if advance_paid < order_total else "Fully Paid"
+
+		if not new_status:
+			prs = frappe.db.count(
+				"Payment Request",
+				{
+					"reference_doctype": self.doctype,
+					"reference_name": self.name,
+					"docstatus": 1,
+				},
+			)
+			if self.doctype in frappe.get_hooks("advance_payment_customer_doctypes"):
+				new_status = "Requested" if prs else "Not Requested"
+			if self.doctype in frappe.get_hooks("advance_payment_supplier_doctypes"):
+				new_status = "Initiated" if prs else "Not Initiated"
+
+		if new_status == self.advance_payment_status:
+			return
+
+		self.db_set("advance_payment_status", new_status)
+		self.set_status(update=True)
+		self.notify_update()
 
 	@property
 	def company_abbr(self):
@@ -2684,47 +2764,37 @@
 		q = q.select((payment_entry.target_exchange_rate).as_("exchange_rate"))
 
 	if condition:
-		if condition.get("name", None):
-			q = q.where(payment_entry.name.like(f"%{condition.get('name')}%"))
+		# conditions should be built as an array and passed as Criterion
+		common_filter_conditions = []
 
-		q = q.where(payment_entry.company == condition["company"])
-		q = (
-			q.where(payment_entry.posting_date >= condition["from_payment_date"])
-			if condition.get("from_payment_date")
-			else q
-		)
-		q = (
-			q.where(payment_entry.posting_date <= condition["to_payment_date"])
-			if condition.get("to_payment_date")
-			else q
-		)
+		common_filter_conditions.append(payment_entry.company == condition["company"])
+		if condition.get("name", None):
+			common_filter_conditions.append(payment_entry.name.like(f"%{condition.get('name')}%"))
+
+		if condition.get("from_payment_date"):
+			common_filter_conditions.append(payment_entry.posting_date.gte(condition["from_payment_date"]))
+
+		if condition.get("to_payment_date"):
+			common_filter_conditions.append(payment_entry.posting_date.lte(condition["to_payment_date"]))
+
 		if condition.get("get_payments") == True:
-			q = (
-				q.where(payment_entry.cost_center == condition["cost_center"])
-				if condition.get("cost_center")
-				else q
-			)
-			q = (
-				q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"])
-				if condition.get("minimum_payment_amount")
-				else q
-			)
-			q = (
-				q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"])
-				if condition.get("maximum_payment_amount")
-				else q
-			)
-		else:
-			q = (
-				q.where(payment_entry.total_debit >= condition["minimum_payment_amount"])
-				if condition.get("minimum_payment_amount")
-				else q
-			)
-			q = (
-				q.where(payment_entry.total_debit <= condition["maximum_payment_amount"])
-				if condition.get("maximum_payment_amount")
-				else q
-			)
+			if condition.get("cost_center"):
+				common_filter_conditions.append(payment_entry.cost_center == condition["cost_center"])
+
+			if condition.get("accounting_dimensions"):
+				for field, val in condition.get("accounting_dimensions").items():
+					common_filter_conditions.append(payment_entry[field] == val)
+
+			if condition.get("minimum_payment_amount"):
+				common_filter_conditions.append(
+					payment_entry.unallocated_amount.gte(condition["minimum_payment_amount"])
+				)
+
+			if condition.get("maximum_payment_amount"):
+				common_filter_conditions.append(
+					payment_entry.unallocated_amount.lte(condition["maximum_payment_amount"])
+				)
+		q = q.where(Criterion.all(common_filter_conditions))
 
 	q = q.orderby(payment_entry.posting_date)
 	q = q.limit(limit) if limit else q
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 6e50279..800e756 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -141,7 +141,7 @@
 			items_returned = True
 
 	if not items_returned:
-		frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
+		frappe.throw(_("At least one item should be entered with negative quantity in return document"))
 
 
 def validate_quantity(doc, args, ref, valid_items, already_returned_items):
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 297f8c2..ac8c88f 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -54,6 +54,10 @@
 			"eval:self.per_delivered < 100 and self.per_billed >= 100 and self.docstatus == 1 and not self.skip_delivery_note",
 		],
 		[
+			"To Pay",
+			"eval:self.advance_payment_status == 'Requested' and self.docstatus == 1",
+		],
+		[
 			"Completed",
 			"eval:(self.per_delivered >= 100 or self.skip_delivery_note) and self.per_billed >= 100 and self.docstatus == 1",
 		],
@@ -63,16 +67,20 @@
 	],
 	"Purchase Order": [
 		["Draft", None],
-		[
-			"To Receive and Bill",
-			"eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1",
-		],
 		["To Bill", "eval:self.per_received >= 100 and self.per_billed < 100 and self.docstatus == 1"],
 		[
 			"To Receive",
 			"eval:self.per_received < 100 and self.per_billed == 100 and self.docstatus == 1",
 		],
 		[
+			"To Receive and Bill",
+			"eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1",
+		],
+		[
+			"To Pay",
+			"eval:self.advance_payment_status == 'Initiated' and self.docstatus == 1",
+		],
+		[
 			"Completed",
 			"eval:self.per_received >= 100 and self.per_billed == 100 and self.docstatus == 1",
 		],
diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py
index 97d3c5c..fad216d 100644
--- a/erpnext/controllers/tests/test_accounts_controller.py
+++ b/erpnext/controllers/tests/test_accounts_controller.py
@@ -56,6 +56,7 @@
 	20 series - Sales Invoice against Journals
 	30 series - Sales Invoice against Credit Notes
 	40 series - Company default Cost center is unset
+	50 series - Dimension inheritence
 	"""
 
 	def setUp(self):
@@ -1255,3 +1256,214 @@
 				)
 
 		frappe.db.set_value("Company", self.company, "cost_center", cc)
+
+	def setup_dimensions(self):
+		# create dimension
+		from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import (
+			create_dimension,
+		)
+
+		create_dimension()
+		# make it non-mandatory
+		loc = frappe.get_doc("Accounting Dimension", "Location")
+		for x in loc.dimension_defaults:
+			x.mandatory_for_bs = False
+			x.mandatory_for_pl = False
+		loc.save()
+
+	def test_50_dimensions_filter(self):
+		"""
+		Test workings of dimension filters
+		"""
+		self.setup_dimensions()
+		rate_in_account_currency = 1
+
+		# Invoices
+		si1 = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True)
+		si1.department = "Management"
+		si1.save().submit()
+
+		si2 = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True)
+		si2.department = "Operations"
+		si2.save().submit()
+
+		# Payments
+		cr_note1 = self.create_sales_invoice(qty=-1, conversion_rate=75, rate=1, do_not_save=True)
+		cr_note1.department = "Management"
+		cr_note1.is_return = 1
+		cr_note1.save().submit()
+
+		cr_note2 = self.create_sales_invoice(qty=-1, conversion_rate=75, rate=1, do_not_save=True)
+		cr_note2.department = "Legal"
+		cr_note2.is_return = 1
+		cr_note2.save().submit()
+
+		pe1 = get_payment_entry(si1.doctype, si1.name)
+		pe1.references = []
+		pe1.department = "Research & Development"
+		pe1.save().submit()
+
+		pe2 = get_payment_entry(si1.doctype, si1.name)
+		pe2.references = []
+		pe2.department = "Management"
+		pe2.save().submit()
+
+		je1 = self.create_journal_entry(
+			acc1=self.debit_usd,
+			acc1_exc_rate=75,
+			acc2=self.cash,
+			acc1_amount=-1,
+			acc2_amount=-75,
+			acc2_exc_rate=1,
+		)
+		je1.accounts[0].party_type = "Customer"
+		je1.accounts[0].party = self.customer
+		je1.accounts[0].department = "Management"
+		je1.save().submit()
+
+		# assert dimension filter's result
+		pr = self.create_payment_reconciliation()
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.invoices), 2)
+		self.assertEqual(len(pr.payments), 5)
+
+		pr.department = "Legal"
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.invoices), 0)
+		self.assertEqual(len(pr.payments), 1)
+
+		pr.department = "Management"
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.invoices), 1)
+		self.assertEqual(len(pr.payments), 3)
+
+		pr.department = "Research & Development"
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.invoices), 0)
+		self.assertEqual(len(pr.payments), 1)
+
+	def test_51_cr_note_should_inherit_dimension(self):
+		self.setup_dimensions()
+		rate_in_account_currency = 1
+
+		# Invoice
+		si = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True)
+		si.department = "Management"
+		si.save().submit()
+
+		# Payment
+		cr_note = self.create_sales_invoice(qty=-1, conversion_rate=75, rate=1, do_not_save=True)
+		cr_note.department = "Management"
+		cr_note.is_return = 1
+		cr_note.save().submit()
+
+		pr = self.create_payment_reconciliation()
+		pr.department = "Management"
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.invoices), 1)
+		self.assertEqual(len(pr.payments), 1)
+		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}))
+		pr.reconcile()
+		self.assertEqual(len(pr.invoices), 0)
+		self.assertEqual(len(pr.payments), 0)
+
+		# There should be 2 journals, JE(Cr Note) and JE(Exchange Gain/Loss)
+		exc_je_for_si = self.get_journals_for(si.doctype, si.name)
+		exc_je_for_cr_note = self.get_journals_for(cr_note.doctype, cr_note.name)
+		self.assertNotEqual(exc_je_for_si, [])
+		self.assertEqual(len(exc_je_for_si), 2)
+		self.assertEqual(len(exc_je_for_cr_note), 2)
+		self.assertEqual(exc_je_for_si, exc_je_for_cr_note)
+
+		for x in exc_je_for_si + exc_je_for_cr_note:
+			with self.subTest(x=x):
+				self.assertEqual(
+					[cr_note.department, cr_note.department],
+					frappe.db.get_all("Journal Entry Account", filters={"parent": x.parent}, pluck="department"),
+				)
+
+	def test_52_dimension_inhertiance_exc_gain_loss(self):
+		# Sales Invoice in Foreign Currency
+		self.setup_dimensions()
+		rate = 80
+		rate_in_account_currency = 1
+		dpt = "Research & Development"
+
+		si = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_save=True)
+		si.department = dpt
+		si.save().submit()
+
+		pe = self.create_payment_entry(amount=1, source_exc_rate=82).save()
+		pe.department = dpt
+		pe = pe.save().submit()
+
+		pr = self.create_payment_reconciliation()
+		pr.department = dpt
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.invoices), 1)
+		self.assertEqual(len(pr.payments), 1)
+		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}))
+		pr.reconcile()
+		self.assertEqual(len(pr.invoices), 0)
+		self.assertEqual(len(pr.payments), 0)
+
+		# Exc Gain/Loss journals should inherit dimension from parent
+		journals = self.get_journals_for(si.doctype, si.name)
+		self.assertEqual(
+			[dpt, dpt],
+			frappe.db.get_all(
+				"Journal Entry Account",
+				filters={"parent": ("in", [x.parent for x in journals])},
+				pluck="department",
+			),
+		)
+
+	def test_53_dimension_inheritance_on_advance(self):
+		self.setup_dimensions()
+		dpt = "Research & Development"
+
+		adv = self.create_payment_entry(amount=1, source_exc_rate=85)
+		adv.department = dpt
+		adv.save().submit()
+		adv.reload()
+
+		# Sales Invoices in different exchange rates
+		si = self.create_sales_invoice(qty=1, conversion_rate=82, rate=1, do_not_submit=True)
+		si.department = dpt
+		advances = si.get_advance_entries()
+		self.assertEqual(len(advances), 1)
+		self.assertEqual(advances[0].reference_name, adv.name)
+		si.append(
+			"advances",
+			{
+				"doctype": "Sales Invoice Advance",
+				"reference_type": advances[0].reference_type,
+				"reference_name": advances[0].reference_name,
+				"reference_row": advances[0].reference_row,
+				"advance_amount": 1,
+				"allocated_amount": 1,
+				"ref_exchange_rate": advances[0].exchange_rate,
+				"remarks": advances[0].remarks,
+			},
+		)
+		si = si.save().submit()
+
+		# Outstanding in both currencies should be '0'
+		adv.reload()
+		self.assertEqual(si.outstanding_amount, 0)
+		self.assert_ledger_outstanding(si.doctype, si.name, 0.0, 0.0)
+
+		# Exc Gain/Loss journals should inherit dimension from parent
+		journals = self.get_journals_for(si.doctype, si.name)
+		self.assertEqual(
+			[dpt, dpt],
+			frappe.db.get_all(
+				"Journal Entry Account",
+				filters={"parent": ("in", [x.parent for x in journals])},
+				pluck="department",
+			),
+		)
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
index ba1fae9..8cba24a 100644
--- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
@@ -155,7 +155,7 @@
 			except RecursionError:
 				self.log(
 					_(
-						"Error occured while parsing Chart of Accounts: Please make sure that no two accounts have the same name"
+						"Error occurred while parsing Chart of Accounts: Please make sure that no two accounts have the same name"
 					)
 				)
 
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 6efb893..f33fff0 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -481,7 +481,8 @@
 
 communication_doctypes = ["Customer", "Supplier"]
 
-advance_payment_doctypes = ["Sales Order", "Purchase Order"]
+advance_payment_customer_doctypes = ["Sales Order"]
+advance_payment_supplier_doctypes = ["Purchase Order"]
 
 invoice_doctypes = ["Sales Invoice", "Purchase Invoice"]
 
@@ -538,6 +539,8 @@
 	"Account Closing Balance",
 	"Supplier Quotation",
 	"Supplier Quotation Item",
+	"Payment Reconciliation",
+	"Payment Reconciliation Allocation",
 ]
 
 get_matching_queries = (
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
index ceb4406..75d890c 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -222,7 +222,7 @@
 
 	def validate_maintenance_detail(self):
 		if not self.get("items"):
-			throw(_("Please enter Maintaince Details first"))
+			throw(_("Please enter Maintenance Details first"))
 
 		for d in self.get("items"):
 			if not d.item_code:
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index aa5db57..f6e9a07 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -998,12 +998,6 @@
 
 		make_job_card(wo_order.name, operations)
 		job_card = frappe.db.get_value("Job Card", {"work_order": wo_order.name, "docstatus": 0}, "name")
-		update_job_card(job_card, 10, 2)
-
-		stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
-		for row in stock_entry.items:
-			if row.is_scrap_item:
-				self.assertEqual(row.qty, 2)
 
 	def test_close_work_order(self):
 		items = [
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 0acc2b1..aa7bc5b 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -1520,7 +1520,7 @@
 
 	if row.get("qty") > row.get("pending_qty"):
 		frappe.throw(
-			_("For operation {0}: Quantity ({1}) can not be greter than pending quantity({2})").format(
+			_("For operation {0}: Quantity ({1}) can not be greater than pending quantity({2})").format(
 				frappe.bold(row.get("operation")),
 				frappe.bold(row.get("qty")),
 				frappe.bold(row.get("pending_qty")),
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 1110617..4ead7e7 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -352,6 +352,7 @@
 execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction")
 execute:frappe.db.set_default("date_format", frappe.db.get_single_value("System Settings", "date_format"))
 erpnext.patches.v14_0.update_total_asset_cost_field
+erpnext.patches.v15_0.create_advance_payment_status
 # below migration patch should always run last
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
 erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
diff --git a/erpnext/patches/v14_0/migrate_gl_to_payment_ledger.py b/erpnext/patches/v14_0/migrate_gl_to_payment_ledger.py
index 72c8c07..95b5bc5 100644
--- a/erpnext/patches/v14_0/migrate_gl_to_payment_ledger.py
+++ b/erpnext/patches/v14_0/migrate_gl_to_payment_ledger.py
@@ -188,4 +188,4 @@
 						raise err
 				else:
 					break
-			print(f"{processed} records have been sucessfully migrated")
+			print(f"{processed} records have been successfully migrated")
diff --git a/erpnext/patches/v15_0/create_advance_payment_status.py b/erpnext/patches/v15_0/create_advance_payment_status.py
new file mode 100644
index 0000000..18ab9fa
--- /dev/null
+++ b/erpnext/patches/v15_0/create_advance_payment_status.py
@@ -0,0 +1,54 @@
+import frappe
+
+
+def execute():
+	"""
+	Description:
+	Calculate the new Advance Payment Statuse column in SO & PO
+	"""
+
+	if frappe.reload_doc("selling", "doctype", "Sales Order"):
+		so = frappe.qb.DocType("Sales Order")
+		frappe.qb.update(so).set(so.advance_payment_status, "Not Requested").where(
+			so.docstatus == 1
+		).where(so.advance_paid == 0.0).run()
+
+		frappe.qb.update(so).set(so.advance_payment_status, "Partially Paid").where(
+			so.docstatus == 1
+		).where(so.advance_payment_status.isnull()).where(
+			so.advance_paid < (so.rounded_total or so.grand_total)
+		).run()
+
+		frappe.qb.update(so).set(so.advance_payment_status, "Fully Paid").where(so.docstatus == 1).where(
+			so.advance_payment_status.isnull()
+		).where(so.advance_paid == (so.rounded_total or so.grand_total)).run()
+
+		pr = frappe.qb.DocType("Payment Request")
+		frappe.qb.update(so).join(pr).on(so.name == pr.reference_name).set(
+			so.advance_payment_status, "Requested"
+		).where(so.docstatus == 1).where(pr.docstatus == 1).where(
+			so.advance_payment_status == "Not Requested"
+		).run()
+
+	if frappe.reload_doc("buying", "doctype", "Purchase Order"):
+		po = frappe.qb.DocType("Purchase Order")
+		frappe.qb.update(po).set(po.advance_payment_status, "Not Initiated").where(
+			po.docstatus == 1
+		).where(po.advance_paid == 0.0).run()
+
+		frappe.qb.update(po).set(po.advance_payment_status, "Partially Paid").where(
+			po.docstatus == 1
+		).where(po.advance_payment_status.isnull()).where(
+			po.advance_paid < (po.rounded_total or po.grand_total)
+		).run()
+
+		frappe.qb.update(po).set(po.advance_payment_status, "Fully Paid").where(po.docstatus == 1).where(
+			po.advance_payment_status.isnull()
+		).where(po.advance_paid == (po.rounded_total or po.grand_total)).run()
+
+		pr = frappe.qb.DocType("Payment Request")
+		frappe.qb.update(po).join(pr).on(po.name == pr.reference_name).set(
+			po.advance_payment_status, "Initiated"
+		).where(po.docstatus == 1).where(pr.docstatus == 1).where(
+			po.advance_payment_status == "Not Initiated"
+		).run()
diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js
index cf7fab8..aacab0f 100644
--- a/erpnext/public/js/utils/barcode_scanner.js
+++ b/erpnext/public/js/utils/barcode_scanner.js
@@ -105,32 +105,47 @@
 				this.frm.has_items = false;
 			}
 
-			if (serial_no && this.is_duplicate_serial_no(row, item_code, serial_no)) {
-				this.clean_up();
-				reject();
-				return;
+			if (serial_no) {
+				this.is_duplicate_serial_no(row, item_code, serial_no)
+					.then((is_duplicate) => {
+						if (!is_duplicate) {
+							this.run_serially_tasks(row, data, resolve);
+						} else {
+							this.clean_up();
+							reject();
+							return;
+						}
+					});
+			} else {
+				this.run_serially_tasks(row, data, resolve);
 			}
 
-			frappe.run_serially([
-				() => this.set_serial_and_batch(row, item_code, serial_no, batch_no),
-				() => this.set_barcode(row, barcode),
-				() => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => {
-					this.show_scan_message(row.idx, row.item_code, qty);
-				}),
-				() => this.set_barcode_uom(row, uom),
-				() => this.clean_up(),
-				() => resolve(row),
-				() => {
-					if (row.serial_and_batch_bundle && !this.frm.is_new()) {
-						this.frm.save();
-					}
 
-					frappe.flags.trigger_from_barcode_scanner = false;
-				}
-			]);
 		});
 	}
 
+	run_serially_tasks(row, data, resolve) {
+		const {item_code, barcode, batch_no, serial_no, uom} = data;
+
+		frappe.run_serially([
+			() => this.set_serial_and_batch(row, item_code, serial_no, batch_no),
+			() => this.set_barcode(row, barcode),
+			() => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => {
+				this.show_scan_message(row.idx, row.item_code, qty);
+			}),
+			() => this.set_barcode_uom(row, uom),
+			() => this.clean_up(),
+			() => {
+				if (row.serial_and_batch_bundle && !this.frm.is_new()) {
+					this.frm.save();
+				}
+
+				frappe.flags.trigger_from_barcode_scanner = false;
+			},
+			() => resolve(row),
+		]);
+	}
+
 	set_item(row, item_code, barcode, batch_no, serial_no) {
 		return new Promise(resolve => {
 			const increment = async (value = 1) => {
@@ -475,26 +490,32 @@
 		}
 	}
 
-	is_duplicate_serial_no(row, item_code, serial_no) {
-		if (this.frm.is_new() || !row.serial_and_batch_bundle) {
-			let is_duplicate = this.check_duplicate_serial_no_in_localstorage(item_code, serial_no);
-			if (is_duplicate) {
-				this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
-			}
-
-			return is_duplicate;
-		} else if (row.serial_and_batch_bundle) {
-			this.check_duplicate_serial_no_in_db(row, serial_no, (r) => {
-				if (r.message) {
+	async is_duplicate_serial_no(row, item_code, serial_no) {
+		let is_duplicate = false;
+		const promise = new Promise((resolve, reject) => {
+			if (this.frm.is_new() || !row.serial_and_batch_bundle) {
+				is_duplicate = this.check_duplicate_serial_no_in_localstorage(item_code, serial_no);
+				if (is_duplicate) {
 					this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
 				}
 
-				return r.message;
-			})
-		}
+				resolve(is_duplicate);
+			} else if (row.serial_and_batch_bundle) {
+				this.check_duplicate_serial_no_in_db(row, serial_no, (r) => {
+					if (r.message) {
+						this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
+					}
+
+					is_duplicate = r.message;
+					resolve(is_duplicate);
+				})
+			}
+		});
+
+		return await promise;
 	}
 
-	async check_duplicate_serial_no_in_db(row, serial_no, response) {
+	check_duplicate_serial_no_in_db(row, serial_no, response) {
 		frappe.call({
 			method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.is_duplicate_serial_no",
 			args: {
@@ -504,7 +525,7 @@
 			callback(r) {
 				response(r);
 			}
-		})
+		});
 	}
 
 	check_duplicate_serial_no_in_localstorage(item_code, serial_no) {
diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js
index 3f70c09..27d00ba 100644
--- a/erpnext/public/js/utils/dimension_tree_filter.js
+++ b/erpnext/public/js/utils/dimension_tree_filter.js
@@ -25,6 +25,10 @@
 	},
 
 	setup_filters(frm, doctype) {
+		if (doctype == 'Payment Entry' && this.accounting_dimensions) {
+			frm.dimension_filters = this.accounting_dimensions
+		}
+
 		if (this.accounting_dimensions) {
 			this.accounting_dimensions.forEach((dimension) => {
 				frappe.model.with_doctype(dimension['document_type'], () => {
diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js
index b92b02e..b8ec77f 100644
--- a/erpnext/public/js/utils/sales_common.js
+++ b/erpnext/public/js/utils/sales_common.js
@@ -22,6 +22,15 @@
 						}
 					};
 				});
+
+				this.frm.set_query('project', function(doc) {
+					return {
+						query: "erpnext.controllers.queries.get_project_name",
+						filters: {
+							'customer': doc.customer
+						}
+					}
+				});
 			}
 
 			setup_queries() {
@@ -439,4 +448,4 @@
 			}
 		});
 	}
-}
\ No newline at end of file
+}
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index bf362e3..80ade70 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -71,6 +71,10 @@
 		let warehouse = this.item?.type_of_transaction === "Outward" ?
 			(this.item.warehouse || this.item.s_warehouse) : "";
 
+		if (!warehouse && this.frm.doc.doctype === 'Stock Reconciliation') {
+			warehouse = this.get_warehouse();
+		}
+
 		return {
 			'item_code': this.item.item_code,
 			'warehouse': ["=", warehouse]
@@ -135,7 +139,7 @@
 						filters: this.get_serial_no_filters()
 					};
 				},
-				onchange: () => this.update_serial_batch_no()
+				onchange: () => this.scan_barcode_data()
 			});
 		}
 
@@ -145,7 +149,7 @@
 				options: 'Barcode',
 				fieldname: 'scan_batch_no',
 				label: __('Scan Batch No'),
-				onchange: () => this.update_serial_batch_no()
+				onchange: () => this.scan_barcode_data()
 			});
 		}
 
@@ -179,11 +183,54 @@
 			label = __('Serial Nos / Batch Nos');
 		}
 
-		return [
+		let fields = [
 			{
 				fieldtype: 'Section Break',
 				label: __('{0} {1} via CSV File', [primary_label, label])
-			},
+			}
+		]
+
+		if (this.item?.has_serial_no) {
+			fields = [...fields,
+				{
+					fieldtype: 'Check',
+					label: __('Import Using CSV file'),
+					fieldname: 'import_using_csv_file',
+					default: 0,
+				},
+				{
+					fieldtype: 'Section Break',
+					label: __('{0} {1} Manually', [primary_label, label]),
+					depends_on: 'eval:doc.import_using_csv_file === 0',
+				},
+				{
+					fieldtype: 'Small Text',
+					label: __('Enter Serial Nos'),
+					fieldname: 'upload_serial_nos',
+					depends_on: 'eval:doc.import_using_csv_file === 0',
+					description: __('Enter each serial no in a new line'),
+				},
+				{
+					fieldtype: 'Column Break',
+					depends_on: 'eval:doc.import_using_csv_file === 0',
+				},
+				{
+					fieldtype: 'Button',
+					fieldname: 'make_serial_nos',
+					label: __('Create Serial Nos'),
+					depends_on: 'eval:doc.import_using_csv_file === 0',
+					click: () => {
+						this.create_serial_nos();
+					}
+				},
+				{
+					fieldtype: 'Section Break',
+					depends_on: 'eval:doc.import_using_csv_file === 1',
+				}
+			];
+		}
+
+		fields = [...fields,
 			{
 				fieldtype: 'Button',
 				fieldname: 'download_csv',
@@ -199,7 +246,32 @@
 				label: __('Attach CSV File'),
 				onchange: () => this.upload_csv_file()
 			}
-		]
+		];
+
+		return fields;
+	}
+
+	create_serial_nos() {
+		let {upload_serial_nos} = this.dialog.get_values();
+
+		if (!upload_serial_nos) {
+			frappe.throw(__('Please enter Serial Nos'));
+		}
+
+		frappe.call({
+			method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.create_serial_nos',
+			args: {
+				item_code: this.item.item_code,
+				serial_nos: upload_serial_nos
+			},
+			callback: (r) => {
+				if (r.message) {
+					this.dialog.fields_dict.entries.df.data = [];
+					this.set_data(r.message);
+					this.update_bundle_entries();
+				}
+			}
+		});
 	}
 
 	download_csv_file() {
@@ -374,6 +446,26 @@
 		}
 	}
 
+	scan_barcode_data() {
+		const { scan_serial_no, scan_batch_no } = this.dialog.get_values();
+
+		if (scan_serial_no || scan_batch_no) {
+			frappe.call({
+				method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.is_serial_batch_no_exists',
+				args: {
+					item_code: this.item.item_code,
+					type_of_transaction: this.item.type_of_transaction,
+					serial_no: scan_serial_no,
+					batch_no: scan_batch_no,
+				},
+				callback: (r) => {
+					this.update_serial_batch_no();
+				}
+
+			})
+		}
+	}
+
 	update_serial_batch_no() {
 		const { scan_serial_no, scan_batch_no } = this.dialog.get_values();
 
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 d332b4e..ecc198a 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
@@ -64,7 +64,7 @@
   {
    "fieldname": "valid_upto",
    "fieldtype": "Date",
-   "label": "Valid Upto",
+   "label": "Valid Up To",
    "reqd": 1
   },
   {
@@ -135,7 +135,7 @@
  ],
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2023-04-18 08:25:35.302081",
+ "modified": "2024-01-24 02:20:26.145996",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "Lower Deduction Certificate",
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
index 72b3a49..6982ad1 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
@@ -37,7 +37,7 @@
 
 	def validate_dates(self):
 		if getdate(self.valid_upto) < getdate(self.valid_from):
-			frappe.throw(_("Valid Upto date cannot be before Valid From date"))
+			frappe.throw(_("Valid Up To date cannot be before Valid From date"))
 
 		fiscal_year = get_fiscal_year(fiscal_year=self.fiscal_year, as_dict=True)
 
@@ -45,7 +45,7 @@
 			frappe.throw(_("Valid From date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
 
 		if not (fiscal_year.year_start_date <= getdate(self.valid_upto) <= fiscal_year.year_end_date):
-			frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
+			frappe.throw(_("Valid Up To date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
 
 	def validate_supplier_against_tax_category(self):
 		duplicate_certificate = frappe.db.get_value(
diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py
index 29dbd4f..47153a8 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -427,11 +427,11 @@
 	if not allowed_to_interact_with:
 		allowed_to_interact_with = represents_company
 
-	exisiting_representative = frappe.db.get_value(
+	existing_representative = frappe.db.get_value(
 		"Customer", {"represents_company": represents_company}
 	)
-	if exisiting_representative:
-		return exisiting_representative
+	if existing_representative:
+		return existing_representative
 
 	if not frappe.db.exists("Customer", customer_name):
 		customer = frappe.get_doc(
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index ab74f7f..654f297 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -127,7 +127,8 @@
 	def validate(self):
 		super(Quotation, self).validate()
 		self.set_status()
-		self.validate_uom_is_integer("stock_uom", "qty")
+		self.validate_uom_is_integer("stock_uom", "stock_qty")
+		self.validate_uom_is_integer("uom", "qty")
 		self.validate_valid_till()
 		self.set_customer_name()
 		if self.items:
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index ecb7d09..86c7a72 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -593,6 +593,77 @@
 		quotation.reload()
 		self.assertEqual(quotation.status, "Ordered")
 
+	def test_uom_validation(self):
+		from erpnext.stock.doctype.item.test_item import make_item
+
+		item = "_Test Item FOR UOM Validation"
+		make_item(item, {"is_stock_item": 1})
+
+		if not frappe.db.exists("UOM", "lbs"):
+			frappe.get_doc({"doctype": "UOM", "uom_name": "lbs", "must_be_whole_number": 1}).insert()
+		else:
+			frappe.db.set_value("UOM", "lbs", "must_be_whole_number", 1)
+
+		quotation = make_quotation(item_code=item, qty=1, rate=100, do_not_submit=1)
+		quotation.items[0].uom = "lbs"
+		quotation.items[0].conversion_factor = 2.23
+		self.assertRaises(frappe.ValidationError, quotation.save)
+
+	def test_item_tax_template_for_quotation(self):
+		from erpnext.stock.doctype.item.test_item import make_item
+
+		if not frappe.db.exists("Account", {"account_name": "_Test Vat", "company": "_Test Company"}):
+			frappe.get_doc(
+				{
+					"doctype": "Account",
+					"account_name": "_Test Vat",
+					"company": "_Test Company",
+					"account_type": "Tax",
+					"root_type": "Asset",
+					"is_group": 0,
+					"parent_account": "Tax Assets - _TC",
+					"tax_rate": 10,
+				}
+			).insert()
+
+		if not frappe.db.exists("Item Tax Template", "Vat Template - _TC"):
+			doc = frappe.get_doc(
+				{
+					"doctype": "Item Tax Template",
+					"name": "Vat Template",
+					"title": "Vat Template",
+					"company": "_Test Company",
+					"taxes": [
+						{
+							"tax_type": "_Test Vat - _TC",
+							"tax_rate": 5,
+						}
+					],
+				}
+			).insert()
+
+		item_doc = make_item("_Test Item Tax Template QTN", {"is_stock_item": 1})
+		if not frappe.db.exists(
+			"Item Tax", {"parent": item_doc.name, "item_tax_template": "Vat Template - _TC"}
+		):
+			item_doc.append("taxes", {"item_tax_template": "Vat Template - _TC"})
+			item_doc.save()
+
+		quotation = make_quotation(
+			item_code="_Test Item Tax Template QTN", qty=1, rate=100, do_not_submit=1
+		)
+		self.assertFalse(quotation.taxes)
+
+		quotation.append_taxes_from_item_tax_template()
+		quotation.save()
+		self.assertTrue(quotation.taxes)
+		for row in quotation.taxes:
+			self.assertEqual(row.account_head, "_Test Vat - _TC")
+			self.assertAlmostEqual(row.base_tax_amount, quotation.total * 5 / 100)
+
+		item_doc.taxes = []
+		item_doc.save()
+
 
 test_records = frappe.get_test_records("Quotation")
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 56c745c..2bb093d 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -144,15 +144,6 @@
 			};
 		});
 
-		frm.set_query('project', function(doc, cdt, cdn) {
-			return {
-				query: "erpnext.controllers.queries.get_project_name",
-				filters: {
-					'customer': doc.customer
-				}
-			}
-		});
-
 		frm.set_query('warehouse', 'items', function(doc, cdt, cdn) {
 			let row  = locals[cdt][cdn];
 			let query = {
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 01d047c..3c516d0 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -131,6 +131,7 @@
   "per_billed",
   "per_picked",
   "billing_status",
+  "advance_payment_status",
   "sales_team_section_break",
   "sales_partner",
   "column_break7",
@@ -1269,7 +1270,7 @@
    "no_copy": 1,
    "oldfieldname": "status",
    "oldfieldtype": "Select",
-   "options": "\nDraft\nOn Hold\nTo Deliver and Bill\nTo Bill\nTo Deliver\nCompleted\nCancelled\nClosed",
+   "options": "\nDraft\nOn Hold\nTo Pay\nTo Deliver and Bill\nTo Bill\nTo Deliver\nCompleted\nCancelled\nClosed",
    "print_hide": 1,
    "read_only": 1,
    "reqd": 1,
@@ -1638,6 +1639,18 @@
    "no_copy": 1,
    "print_hide": 1,
    "report_hide": 1
+  },
+  {
+   "fieldname": "advance_payment_status",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "hide_days": 1,
+   "hide_seconds": 1,
+   "in_standard_filter": 1,
+   "label": "Advance Payment Status",
+   "no_copy": 1,
+   "options": "Not Requested\nRequested\nPartially Paid\nFully Paid",
+   "print_hide": 1
   }
  ],
  "icon": "fa fa-file-text",
@@ -1722,4 +1735,4 @@
  "title_field": "customer_name",
  "track_changes": 1,
  "track_seen": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 5ef2c50..79f24d1 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -223,6 +223,8 @@
 			self.billing_status = "Not Billed"
 		if not self.delivery_status:
 			self.delivery_status = "Not Delivered"
+		if not self.advance_payment_status:
+			self.advance_payment_status = "Not Requested"
 
 		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 
@@ -641,7 +643,7 @@
 					if not frappe.get_cached_value("Item", item.item_code, "has_serial_no"):
 						frappe.throw(
 							_(
-								"Item {0} has no Serial No. Only serilialized items can have delivery based on Serial No"
+								"Item {0} has no Serial No. Only serialized items can have delivery based on Serial No"
 							).format(item.item_code)
 						)
 					if not frappe.db.exists("BOM", {"item": item.item_code, "is_active": 1}):
diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js
index 518f018..37686a8 100644
--- a/erpnext/selling/doctype/sales_order/sales_order_list.js
+++ b/erpnext/selling/doctype/sales_order/sales_order_list.js
@@ -1,6 +1,6 @@
 frappe.listview_settings['Sales Order'] = {
 	add_fields: ["base_grand_total", "customer_name", "currency", "delivery_date",
-		"per_delivered", "per_billed", "status", "order_type", "name", "skip_delivery_note"],
+		"per_delivered", "per_billed", "status", "advance_payment_status", "order_type", "name", "skip_delivery_note"],
 	get_indicator: function (doc) {
 		if (doc.status === "Closed") {
 			// Closed
@@ -10,6 +10,8 @@
 			return [__("On Hold"), "orange", "status,=,On Hold"];
 		} else if (doc.status === "Completed") {
 			return [__("Completed"), "green", "status,=,Completed"];
+		} else if (doc.advance_payment_status === "Requested") {
+			return [__("To Pay"), "gray", "advance_payment_status,=,Requested"];
 		} else if (!doc.skip_delivery_note && flt(doc.per_delivered, 2) < 100) {
 			if (frappe.datetime.get_diff(doc.delivery_date) < 0) {
 			// not delivered & overdue
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index ac7fdb1..5ae48ee 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1996,6 +1996,33 @@
 				self.assertEqual(so.items[0].rate, scenario.get("expected_rate"))
 				self.assertEqual(so.packed_items[0].rate, scenario.get("expected_rate"))
 
+	def test_sales_order_advance_payment_status(self):
+		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+		from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
+
+		so = make_sales_order(qty=1, rate=100)
+		self.assertEqual(
+			frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested"
+		)
+
+		pr = make_payment_request(dt=so.doctype, dn=so.name, submit_doc=True, return_doc=True)
+		self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested")
+
+		pe = get_payment_entry(so.doctype, so.name).save().submit()
+		self.assertEqual(
+			frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Fully Paid"
+		)
+
+		pe.reload()
+		pe.cancel()
+		self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested")
+
+		pr.reload()
+		pr.cancel()
+		self.assertEqual(
+			frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested"
+		)
+
 
 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 feecd9c..80e1c20 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -360,7 +360,7 @@
 							this.order_summary.load_summary_of(this.frm.doc, true);
 							frappe.show_alert({
 								indicator: 'green',
-								message: __('POS invoice {0} created succesfully', [r.doc.name])
+								message: __('POS invoice {0} created successfully', [r.doc.name])
 							});
 						});
 				}
diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
index 3682c5f..72b7fa2 100644
--- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
+++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
@@ -209,8 +209,7 @@
 		)
 		.where(
 			(so.docstatus == 1)
-			& (so.status.isin(["To Deliver and Bill", "To Bill"]))
-			& (so.payment_terms_template != "NULL")
+			& (so.status.isin(["To Deliver and Bill", "To Bill", "To Pay"]))
 			& (so.company == conditions.company)
 			& (so.transaction_date[conditions.start_date : conditions.end_date])
 		)
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 ac3d3db..fc685e0 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
@@ -56,7 +56,7 @@
 			"fieldtype": "MultiSelectList",
 			"width": "80",
 			get_data: function(txt) {
-				let status = ["To Bill", "To Deliver", "To Deliver and Bill", "Completed"]
+				let status = ["To Pay", "To Bill", "To Deliver", "To Deliver and Bill", "Completed"]
 				let options = []
 				for (let option of status){
 					options.push({
diff --git a/erpnext/setup/doctype/authorization_control/authorization_control.py b/erpnext/setup/doctype/authorization_control/authorization_control.py
index 9446fb4..27313ae 100644
--- a/erpnext/setup/doctype/authorization_control/authorization_control.py
+++ b/erpnext/setup/doctype/authorization_control/authorization_control.py
@@ -54,7 +54,7 @@
 			if not has_common(appr_roles, frappe.get_roles()) and not has_common(
 				appr_users, [session["user"]]
 			):
-				frappe.msgprint(_("Not authroized since {0} exceeds limits").format(_(based_on)))
+				frappe.msgprint(_("Not authorized since {0} exceeds limits").format(_(based_on)))
 				frappe.throw(_("Can be approved by {0}").format(comma_or(appr_roles + appr_users)))
 
 	def validate_auth_rule(self, doctype_name, total, based_on, cond, company, master_name=""):
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 1bd469b..340a917 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -140,38 +140,48 @@
 	},
 
 	delete_company_transactions: function(frm) {
-		frappe.verify_password(function() {
-			var d = frappe.prompt({
-				fieldtype:"Data",
-				fieldname: "company_name",
-				label: __("Please enter the company name to confirm"),
-				reqd: 1,
-				description: __("Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone.")
+		frappe.call({
+			method: "erpnext.setup.doctype.company.company.is_deletion_job_running",
+			args: {
+				company: frm.doc.name
 			},
-			function(data) {
-				if(data.company_name !== frm.doc.name) {
-					frappe.msgprint(__("Company name not same"));
-					return;
+			freeze: true,
+			callback: function(r) {
+				if(!r.exc) {
+					frappe.verify_password(function() {
+						var d = frappe.prompt({
+							fieldtype:"Data",
+							fieldname: "company_name",
+							label: __("Please enter the company name to confirm"),
+							reqd: 1,
+							description: __("Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone.")
+						},
+								      function(data) {
+									      if(data.company_name !== frm.doc.name) {
+										      frappe.msgprint(__("Company name not same"));
+										      return;
+									      }
+									      frappe.call({
+										      method: "erpnext.setup.doctype.company.company.create_transaction_deletion_request",
+										      args: {
+											      company: data.company_name
+										      },
+										      freeze: true,
+										      callback: function(r, rt) { },
+										      onerror: function() {
+											      frappe.msgprint(__("Wrong Password"));
+										      }
+									      });
+								      },
+								      __("Delete all the Transactions for this Company"), __("Delete")
+								     );
+						d.get_primary_btn().addClass("btn-danger");
+					});
 				}
-				frappe.call({
-					method: "erpnext.setup.doctype.company.company.create_transaction_deletion_request",
-					args: {
-						company: data.company_name
-					},
-					freeze: true,
-					callback: function(r, rt) {
-						if(!r.exc)
-							frappe.msgprint(__("Successfully deleted all transactions related to this company!"));
-					},
-					onerror: function() {
-						frappe.msgprint(__("Wrong Password"));
-					}
-				});
+
 			},
-			__("Delete all the Transactions for this Company"), __("Delete")
-			);
-			d.get_primary_btn().addClass("btn-danger");
 		});
+
 	}
 });
 
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index ec953b8..876b6a4 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -11,7 +11,8 @@
 from frappe.contacts.address_and_contact import load_address_and_contact
 from frappe.custom.doctype.property_setter.property_setter import make_property_setter
 from frappe.desk.page.setup_wizard.setup_wizard import make_records
-from frappe.utils import cint, formatdate, get_timestamp, today
+from frappe.utils import cint, formatdate, get_link_to_form, get_timestamp, today
+from frappe.utils.background_jobs import get_job, is_job_enqueued
 from frappe.utils.nestedset import NestedSet, rebuild_tree
 
 from erpnext.accounts.doctype.account.account import get_account_currency
@@ -900,8 +901,37 @@
 		return None
 
 
+def generate_id_for_deletion_job(company):
+	return "delete_company_transactions_" + company
+
+
+@frappe.whitelist()
+def is_deletion_job_running(company):
+	job_id = generate_id_for_deletion_job(company)
+	if is_job_enqueued(job_id):
+		job_name = get_job(job_id).get_id()  # job name will have site prefix
+		frappe.throw(
+			_("A Transaction Deletion Job: {0} is already running for {1}").format(
+				frappe.bold(get_link_to_form("RQ Job", job_name)), frappe.bold(company)
+			)
+		)
+
+
 @frappe.whitelist()
 def create_transaction_deletion_request(company):
+	is_deletion_job_running(company)
+	job_id = generate_id_for_deletion_job(company)
+
 	tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company})
 	tdr.insert()
-	tdr.submit()
+
+	frappe.enqueue(
+		"frappe.utils.background_jobs.run_doc_method",
+		doctype=tdr.doctype,
+		name=tdr.name,
+		doc_method="submit",
+		job_id=job_id,
+		queue="long",
+		enqueue_after_commit=True,
+	)
+	frappe.msgprint(_("A Transaction Deletion Job is triggered for {0}").format(frappe.bold(company)))
diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json
index daf2df5..fc1fc9b 100644
--- a/erpnext/setup/doctype/employee/employee.json
+++ b/erpnext/setup/doctype/employee/employee.json
@@ -441,13 +441,13 @@
   {
    "fieldname": "prefered_contact_email",
    "fieldtype": "Select",
-   "label": "Prefered Contact Email",
+   "label": "Preferred Contact Email",
    "options": "\nCompany Email\nPersonal Email\nUser ID"
   },
   {
    "fieldname": "prefered_email",
    "fieldtype": "Data",
-   "label": "Prefered Email",
+   "label": "Preferred Email",
    "options": "Email",
    "read_only": 1
   },
@@ -524,7 +524,7 @@
   {
    "fieldname": "valid_upto",
    "fieldtype": "Date",
-   "label": "Valid Upto"
+   "label": "Valid Up To"
   },
   {
    "fieldname": "place_of_issue",
@@ -824,7 +824,7 @@
  "image_field": "image",
  "is_tree": 1,
  "links": [],
- "modified": "2024-01-03 17:36:20.984421",
+ "modified": "2024-01-24 02:20:26.145996",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Employee",
diff --git a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
index 319d435..844e786 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
@@ -4,9 +4,10 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 
-class TestTransactionDeletionRecord(unittest.TestCase):
+class TestTransactionDeletionRecord(FrappeTestCase):
 	def setUp(self):
 		create_company("Dunder Mifflin Paper Co")
 
@@ -14,7 +15,7 @@
 		frappe.db.rollback()
 
 	def test_doctypes_contain_company_field(self):
-		tdr = create_transaction_deletion_request("Dunder Mifflin Paper Co")
+		tdr = create_transaction_deletion_doc("Dunder Mifflin Paper Co")
 		for doctype in tdr.doctypes:
 			contains_company = False
 			doctype_fields = frappe.get_meta(doctype.doctype_name).as_dict()["fields"]
@@ -27,17 +28,27 @@
 	def test_no_of_docs_is_correct(self):
 		for i in range(5):
 			create_task("Dunder Mifflin Paper Co")
-		tdr = create_transaction_deletion_request("Dunder Mifflin Paper Co")
+		tdr = create_transaction_deletion_doc("Dunder Mifflin Paper Co")
 		for doctype in tdr.doctypes:
 			if doctype.doctype_name == "Task":
 				self.assertEqual(doctype.no_of_docs, 5)
 
 	def test_deletion_is_successful(self):
 		create_task("Dunder Mifflin Paper Co")
-		create_transaction_deletion_request("Dunder Mifflin Paper Co")
+		create_transaction_deletion_doc("Dunder Mifflin Paper Co")
 		tasks_containing_company = frappe.get_all("Task", filters={"company": "Dunder Mifflin Paper Co"})
 		self.assertEqual(tasks_containing_company, [])
 
+	def test_company_transaction_deletion_request(self):
+		from erpnext.setup.doctype.company.company import create_transaction_deletion_request
+
+		# don't reuse below company for other test cases
+		company = "Deep Space Exploration"
+		create_company(company)
+
+		# below call should not raise any exceptions or throw errors
+		create_transaction_deletion_request(company)
+
 
 def create_company(company_name):
 	company = frappe.get_doc(
@@ -46,7 +57,7 @@
 	company.insert(ignore_if_duplicate=True)
 
 
-def create_transaction_deletion_request(company):
+def create_transaction_deletion_doc(company):
 	tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company})
 	tdr.insert()
 	tdr.submit()
diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js
index 7bf7a1f..f4a935a 100644
--- a/erpnext/stock/doctype/batch/batch.js
+++ b/erpnext/stock/doctype/batch/batch.js
@@ -52,7 +52,7 @@
 					// sort by qty
 					r.message.sort(function(a, b) { a.qty > b.qty ? 1 : -1 });
 
-					var rows = $('<div></div>').appendTo(section);
+					const rows = $('<div></div>').appendTo(section);
 
 					// show
 					(r.message || []).forEach(function(d) {
@@ -76,7 +76,7 @@
 
 					// move - ask for target warehouse and make stock entry
 					rows.find('.btn-move').on('click', function() {
-						var $btn = $(this);
+						const $btn = $(this);
 						const fields = [
 							{
 								fieldname: 'to_warehouse',
@@ -115,7 +115,7 @@
 					// split - ask for new qty and batch ID (optional)
 					// and make stock entry via batch.batch_split
 					rows.find('.btn-split').on('click', function() {
-						var $btn = $(this);
+						const $btn = $(this);
 						frappe.prompt([{
 							fieldname: 'qty',
 							label: __('New Batch Qty'),
@@ -128,19 +128,16 @@
 							fieldtype: 'Data',
 						}],
 						(data) => {
-							frappe.call({
-								method: 'erpnext.stock.doctype.batch.batch.split_batch',
-								args: {
+							frappe.xcall(
+								'erpnext.stock.doctype.batch.batch.split_batch',
+								{
 									item_code: frm.doc.item,
 									batch_no: frm.doc.name,
 									qty: data.qty,
 									warehouse: $btn.attr('data-warehouse'),
 									new_batch_id: data.new_batch_id
-								},
-								callback: (r) => {
-									frm.refresh();
-								},
-							});
+								}
+							).then(() => frm.reload_doc());
 						},
 						__('Split Batch'),
 						__('Split')
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index 7b23f9e..e8e94fd 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -9,7 +9,7 @@
 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
-from frappe.utils import cint, flt, get_link_to_form, nowtime, today
+from frappe.utils import cint, flt, get_link_to_form
 from frappe.utils.data import add_days
 from frappe.utils.jinja import render_template
 
@@ -248,8 +248,9 @@
 
 
 @frappe.whitelist()
-def split_batch(batch_no, item_code, warehouse, qty, new_batch_id=None):
-
+def split_batch(
+	batch_no: str, item_code: str, warehouse: str, qty: float, new_batch_id: str | None = None
+):
 	"""Split the batch into a new batch"""
 	batch = frappe.get_doc(dict(doctype="Batch", item=item_code, batch_id=new_batch_id)).insert()
 	qty = flt(qty)
@@ -257,29 +258,21 @@
 	company = frappe.db.get_value("Warehouse", warehouse, "company")
 
 	from_bundle_id = make_batch_bundle(
-		frappe._dict(
-			{
-				"item_code": item_code,
-				"warehouse": warehouse,
-				"batches": frappe._dict({batch_no: qty}),
-				"company": company,
-				"type_of_transaction": "Outward",
-				"qty": qty,
-			}
-		)
+		item_code=item_code,
+		warehouse=warehouse,
+		batches=frappe._dict({batch_no: qty}),
+		company=company,
+		type_of_transaction="Outward",
+		qty=qty,
 	)
 
 	to_bundle_id = make_batch_bundle(
-		frappe._dict(
-			{
-				"item_code": item_code,
-				"warehouse": warehouse,
-				"batches": frappe._dict({batch.name: qty}),
-				"company": company,
-				"type_of_transaction": "Inward",
-				"qty": qty,
-			}
-		)
+		item_code=item_code,
+		warehouse=warehouse,
+		batches=frappe._dict({batch.name: qty}),
+		company=company,
+		type_of_transaction="Inward",
+		qty=qty,
 	)
 
 	stock_entry = frappe.get_doc(
@@ -304,21 +297,30 @@
 	return batch.name
 
 
-def make_batch_bundle(kwargs):
+def make_batch_bundle(
+	item_code: str,
+	warehouse: str,
+	batches: dict[str, float],
+	company: str,
+	type_of_transaction: str,
+	qty: float,
+):
+	from frappe.utils import nowtime, today
+
 	from erpnext.stock.serial_batch_bundle import SerialBatchCreation
 
 	return (
 		SerialBatchCreation(
 			{
-				"item_code": kwargs.item_code,
-				"warehouse": kwargs.warehouse,
+				"item_code": item_code,
+				"warehouse": warehouse,
 				"posting_date": today(),
 				"posting_time": nowtime(),
 				"voucher_type": "Stock Entry",
-				"qty": flt(kwargs.qty),
-				"type_of_transaction": kwargs.type_of_transaction,
-				"company": kwargs.company,
-				"batches": kwargs.batches,
+				"qty": qty,
+				"type_of_transaction": type_of_transaction,
+				"company": company,
+				"batches": batches,
 				"do_not_submit": True,
 			}
 		)
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index ec68549..14aedca 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -31,15 +31,6 @@
 		});
 		erpnext.queries.setup_warehouse_query(frm);
 
-		frm.set_query('project', function(doc) {
-			return {
-				query: "erpnext.controllers.queries.get_project_name",
-				filters: {
-					'customer': doc.customer
-				}
-			}
-		})
-
 		frm.set_query('transporter', function() {
 			return {
 				filters: {
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 7d7b0cd..58990d4 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -796,36 +796,36 @@
 
 	updated_dn = []
 	for dnd in dn_details:
-		billed_amt_agianst_dn = 0
+		billed_amt_against_dn = 0
 
 		# If delivered against Sales Invoice
 		if dnd.si_detail:
-			billed_amt_agianst_dn = flt(dnd.amount)
-			billed_against_so -= billed_amt_agianst_dn
+			billed_amt_against_dn = flt(dnd.amount)
+			billed_against_so -= billed_amt_against_dn
 		else:
 			# Get billed amount directly against Delivery Note
-			billed_amt_agianst_dn = frappe.db.sql(
+			billed_amt_against_dn = frappe.db.sql(
 				"""select sum(amount) from `tabSales Invoice Item`
 				where dn_detail=%s and docstatus=1""",
 				dnd.name,
 			)
-			billed_amt_agianst_dn = billed_amt_agianst_dn and billed_amt_agianst_dn[0][0] or 0
+			billed_amt_against_dn = billed_amt_against_dn and billed_amt_against_dn[0][0] or 0
 
 		# Distribute billed amount directly against SO between DNs based on FIFO
-		if billed_against_so and billed_amt_agianst_dn < dnd.amount:
-			pending_to_bill = flt(dnd.amount) - billed_amt_agianst_dn
+		if billed_against_so and billed_amt_against_dn < dnd.amount:
+			pending_to_bill = flt(dnd.amount) - billed_amt_against_dn
 			if pending_to_bill <= billed_against_so:
-				billed_amt_agianst_dn += pending_to_bill
+				billed_amt_against_dn += pending_to_bill
 				billed_against_so -= pending_to_bill
 			else:
-				billed_amt_agianst_dn += billed_against_so
+				billed_amt_against_dn += billed_against_so
 				billed_against_so = 0
 
 		frappe.db.set_value(
 			"Delivery Note Item",
 			dnd.name,
 			"billed_amt",
-			billed_amt_agianst_dn,
+			billed_amt_against_dn,
 			update_modified=update_modified,
 		)
 
diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json
index f4d9bb0..2390ee2 100644
--- a/erpnext/stock/doctype/item_price/item_price.json
+++ b/erpnext/stock/doctype/item_price/item_price.json
@@ -191,7 +191,7 @@
   {
    "fieldname": "valid_upto",
    "fieldtype": "Date",
-   "label": "Valid Upto"
+   "label": "Valid Up To"
   },
   {
    "fieldname": "section_break_24",
@@ -220,7 +220,7 @@
  "idx": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2022-11-15 08:26:04.041861",
+ "modified": "2024-01-24 02:20:26.145996",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item Price",
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index 89a130a..de2add6 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -59,7 +59,7 @@
 	def validate_dates(self):
 		if self.valid_from and self.valid_upto:
 			if getdate(self.valid_from) > getdate(self.valid_upto):
-				frappe.throw(_("Valid From Date must be lesser than Valid Upto Date."))
+				frappe.throw(_("Valid From Date must be lesser than Valid Up To Date."))
 
 	def update_price_list_details(self):
 		if self.price_list:
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index 8fd4938..63d717c 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -64,7 +64,7 @@
 		# Enter invalid dates valid_from  >= valid_upto
 		doc.valid_from = "2017-04-20"
 		doc.valid_upto = "2017-04-17"
-		# Valid Upto Date can not be less/equal than Valid From Date
+		# Valid Up To Date can not be less/equal than Valid From Date
 		self.assertRaises(frappe.ValidationError, doc.save)
 
 	def test_price_in_a_qty(self):
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index ad9b34c..e784b70 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -776,7 +776,7 @@
 			)
 		else:
 			msgprint(
-				_("The {0} {1} created sucessfully").format(frappe.bold(_("Work Order")), work_orders_list[0])
+				_("The {0} {1} created successfully").format(frappe.bold(_("Work Order")), work_orders_list[0])
 			)
 
 	if errors:
diff --git a/erpnext/stock/doctype/material_request/material_request_list.js b/erpnext/stock/doctype/material_request/material_request_list.js
index de7a3d0..c85bd71 100644
--- a/erpnext/stock/doctype/material_request/material_request_list.js
+++ b/erpnext/stock/doctype/material_request/material_request_list.js
@@ -24,7 +24,7 @@
 			} else if (doc.material_request_type == "Purchase") {
 				return [__("Ordered"), "green", "per_ordered,=,100"];
 			} else if (doc.material_request_type == "Material Transfer") {
-				return [__("Transfered"), "green", "per_ordered,=,100"];
+				return [__("Transferred"), "green", "per_ordered,=,100"];
 			} else if (doc.material_request_type == "Material Issue") {
 				return [__("Issued"), "green", "per_ordered,=,100"];
 			} else if (doc.material_request_type == "Customer Provided") {
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 3e44049..48397a3 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -774,6 +774,62 @@
 		self.assertEqual(mr.per_ordered, 100)
 		self.assertEqual(existing_requested_qty, current_requested_qty)
 
+	def test_auto_email_users_with_company_user_permissions(self):
+		from erpnext.stock.reorder_item import get_email_list
+
+		comapnywise_users = {
+			"_Test Company": "test_auto_email_@example.com",
+			"_Test Company 1": "test_auto_email_1@example.com",
+		}
+
+		permissions = []
+
+		for company, user in comapnywise_users.items():
+			if not frappe.db.exists("User", user):
+				frappe.get_doc(
+					{
+						"doctype": "User",
+						"email": user,
+						"first_name": user,
+						"send_notifications": 0,
+						"enabled": 1,
+						"user_type": "System User",
+						"roles": [{"role": "Purchase Manager"}],
+					}
+				).insert(ignore_permissions=True)
+
+			if not frappe.db.exists(
+				"User Permission", {"user": user, "allow": "Company", "for_value": company}
+			):
+				perm_doc = frappe.get_doc(
+					{
+						"doctype": "User Permission",
+						"user": user,
+						"allow": "Company",
+						"for_value": company,
+						"apply_to_all_doctypes": 1,
+					}
+				).insert(ignore_permissions=True)
+
+				permissions.append(perm_doc)
+
+		comapnywise_mr_list = frappe._dict({})
+		mr1 = make_material_request()
+		comapnywise_mr_list.setdefault(mr1.company, []).append(mr1.name)
+
+		mr2 = make_material_request(
+			company="_Test Company 1", warehouse="Stores - _TC1", cost_center="Main - _TC1"
+		)
+		comapnywise_mr_list.setdefault(mr2.company, []).append(mr2.name)
+
+		for company, mr_list in comapnywise_mr_list.items():
+			emails = get_email_list(company)
+
+			self.assertTrue(comapnywise_users[company] in emails)
+
+		for perm in permissions:
+			perm.delete()
+
 
 def get_in_transit_warehouse(company):
 	if not frappe.db.exists("Warehouse Type", "Transit"):
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index fcb7a6d..bf6080b 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -951,32 +951,32 @@
 		billed_against_po = flt(po_billed_amt_details.get(pr_item.purchase_order_item))
 
 		# Get billed amount directly against Purchase Receipt
-		billed_amt_agianst_pr = flt(pr_items_billed_amount.get(pr_item.name, 0))
+		billed_amt_against_pr = flt(pr_items_billed_amount.get(pr_item.name, 0))
 
 		# Distribute billed amount directly against PO between PRs based on FIFO
-		if billed_against_po and billed_amt_agianst_pr < pr_item.amount:
-			pending_to_bill = flt(pr_item.amount) - billed_amt_agianst_pr
+		if billed_against_po and billed_amt_against_pr < pr_item.amount:
+			pending_to_bill = flt(pr_item.amount) - billed_amt_against_pr
 			if pending_to_bill <= billed_against_po:
-				billed_amt_agianst_pr += pending_to_bill
+				billed_amt_against_pr += pending_to_bill
 				billed_against_po -= pending_to_bill
 			else:
-				billed_amt_agianst_pr += billed_against_po
+				billed_amt_against_pr += billed_against_po
 				billed_against_po = 0
 
 		po_billed_amt_details[pr_item.purchase_order_item] = billed_against_po
 
-		if pr_item.billed_amt != billed_amt_agianst_pr:
+		if pr_item.billed_amt != billed_amt_against_pr:
 			# update existing doc if possible
 			if pr_doc and pr_item.parent == pr_doc.name:
 				pr_item = next((item for item in pr_doc.items if item.name == pr_item.name), None)
-				pr_item.db_set("billed_amt", billed_amt_agianst_pr, update_modified=update_modified)
+				pr_item.db_set("billed_amt", billed_amt_against_pr, update_modified=update_modified)
 
 			else:
 				frappe.db.set_value(
 					"Purchase Receipt Item",
 					pr_item.name,
 					"billed_amt",
-					billed_amt_agianst_pr,
+					billed_amt_against_pr,
 					update_modified=update_modified,
 				)
 
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js
index 9f01ee9..91b7430 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js
@@ -74,7 +74,7 @@
 
 		let fields = [
 			{
-				"label": __("Using CSV File"),
+				"label": __("Import Using CSV file"),
 				"fieldname": "using_csv_file",
 				"default": 1,
 				"fieldtype": "Check",
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 2b87fcd..9cad8f6 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -250,6 +250,7 @@
 
 		for d in self.entries:
 			available_qty = 0
+
 			if self.has_serial_no:
 				d.incoming_rate = abs(sn_obj.serial_no_incoming_rate.get(d.serial_no, 0.0))
 			else:
@@ -892,6 +893,13 @@
 		elif batch_nos:
 			self.set("entries", batch_nos)
 
+	def delete_serial_batch_entries(self):
+		SBBE = frappe.qb.DocType("Serial and Batch Entry")
+
+		frappe.qb.from_(SBBE).delete().where(SBBE.parent == self.name).run()
+
+		self.set("entries", [])
+
 
 @frappe.whitelist()
 def download_blank_csv_template(content):
@@ -999,9 +1007,25 @@
 
 		make_serial_nos(item_code, serial_nos)
 
+	if kwargs.get("_has_serial_nos"):
+		return serial_nos
+
 	return serial_nos, batch_nos
 
 
+@frappe.whitelist()
+def create_serial_nos(item_code, serial_nos):
+	serial_nos = get_serial_batch_from_data(
+		item_code,
+		{
+			"serial_nos": serial_nos,
+			"_has_serial_nos": True,
+		},
+	)
+
+	return serial_nos
+
+
 def make_serial_nos(item_code, serial_nos):
 	item = frappe.get_cached_value("Item", item_code, ["description", "item_code"], as_dict=1)
 
@@ -1358,10 +1382,12 @@
 	elif kwargs.based_on == "Expiry":
 		order_by = "amc_expiry_date asc"
 
-	filters = {"item_code": kwargs.item_code, "warehouse": ("is", "set")}
+	filters = {"item_code": kwargs.item_code}
 
-	if kwargs.warehouse:
-		filters["warehouse"] = kwargs.warehouse
+	if not kwargs.get("ignore_warehouse"):
+		filters["warehouse"] = ("is", "set")
+		if kwargs.warehouse:
+			filters["warehouse"] = kwargs.warehouse
 
 	# Since SLEs are not present against Reserved Stock [POS invoices, SRE], need to ignore reserved serial nos.
 	ignore_serial_nos = get_reserved_serial_nos(kwargs)
@@ -2080,5 +2106,34 @@
 
 
 @frappe.whitelist()
+def is_serial_batch_no_exists(item_code, type_of_transaction, serial_no=None, batch_no=None):
+	if serial_no and not frappe.db.exists("Serial No", serial_no):
+		if type_of_transaction != "Inward":
+			frappe.throw(_("Serial No {0} does not exists").format(serial_no))
+
+		make_serial_no(serial_no, item_code)
+
+	if batch_no and frappe.db.exists("Batch", batch_no):
+		if type_of_transaction != "Inward":
+			frappe.throw(_("Batch No {0} does not exists").format(batch_no))
+
+		make_batch_no(batch_no, item_code)
+
+
+def make_serial_no(serial_no, item_code):
+	serial_no_doc = frappe.new_doc("Serial No")
+	serial_no_doc.serial_no = serial_no
+	serial_no_doc.item_code = item_code
+	serial_no_doc.save(ignore_permissions=True)
+
+
+def make_batch_no(batch_no, item_code):
+	batch_doc = frappe.new_doc("Batch")
+	batch_doc.batch_id = batch_no
+	batch_doc.item = item_code
+	batch_doc.save(ignore_permissions=True)
+
+
+@frappe.whitelist()
 def is_duplicate_serial_no(bundle_id, serial_no):
 	return frappe.db.exists("Serial and Batch Entry", {"parent": bundle_id, "serial_no": serial_no})
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 96c249f..c371b70 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -640,7 +640,7 @@
 				frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx))
 
 			if not (d.s_warehouse or d.t_warehouse):
-				frappe.throw(_("Atleast one warehouse is mandatory"))
+				frappe.throw(_("At least one warehouse is mandatory"))
 
 	def validate_work_order(self):
 		if self.purpose in (
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 6819968..788ae0d 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -156,6 +156,7 @@
 							"warehouse": item.warehouse,
 							"posting_date": self.posting_date,
 							"posting_time": self.posting_time,
+							"ignore_warehouse": 1,
 						}
 					)
 				)
@@ -780,7 +781,20 @@
 
 			current_qty = 0.0
 			if row.current_serial_and_batch_bundle:
-				current_qty = self.get_qty_for_serial_and_batch_bundle(row)
+				current_qty = self.get_current_qty_for_serial_or_batch(row)
+			elif row.serial_no:
+				item_dict = get_stock_balance_for(
+					row.item_code,
+					row.warehouse,
+					self.posting_date,
+					self.posting_time,
+					voucher_no=self.name,
+				)
+
+				current_qty = item_dict.get("qty")
+				row.current_serial_no = item_dict.get("serial_nos")
+				row.current_valuation_rate = item_dict.get("rate")
+				val_rate = item_dict.get("rate")
 			elif row.batch_no:
 				current_qty = get_batch_qty_for_stock_reco(
 					row.item_code, row.warehouse, row.batch_no, self.posting_date, self.posting_time, self.name
@@ -788,15 +802,16 @@
 
 			precesion = row.precision("current_qty")
 			if flt(current_qty, precesion) != flt(row.current_qty, precesion):
-				val_rate = get_valuation_rate(
-					row.item_code,
-					row.warehouse,
-					self.doctype,
-					self.name,
-					company=self.company,
-					batch_no=row.batch_no,
-					serial_and_batch_bundle=row.current_serial_and_batch_bundle,
-				)
+				if not row.serial_no:
+					val_rate = get_valuation_rate(
+						row.item_code,
+						row.warehouse,
+						self.doctype,
+						self.name,
+						company=self.company,
+						batch_no=row.batch_no,
+						serial_and_batch_bundle=row.current_serial_and_batch_bundle,
+					)
 
 				row.current_valuation_rate = val_rate
 				row.current_qty = current_qty
@@ -842,11 +857,56 @@
 
 		return allow_negative_stock
 
-	def get_qty_for_serial_and_batch_bundle(self, row):
+	def get_current_qty_for_serial_or_batch(self, row):
 		doc = frappe.get_doc("Serial and Batch Bundle", row.current_serial_and_batch_bundle)
-		precision = doc.entries[0].precision("qty")
+		current_qty = 0.0
+		if doc.has_serial_no:
+			current_qty = self.get_current_qty_for_serial_nos(doc)
+		elif doc.has_batch_no:
+			current_qty = self.get_current_qty_for_batch_nos(doc)
 
-		current_qty = 0
+		return abs(current_qty)
+
+	def get_current_qty_for_serial_nos(self, doc):
+		serial_nos_details = get_available_serial_nos(
+			frappe._dict(
+				{
+					"item_code": doc.item_code,
+					"warehouse": doc.warehouse,
+					"posting_date": self.posting_date,
+					"posting_time": self.posting_time,
+					"voucher_no": self.name,
+					"ignore_warehouse": 1,
+				}
+			)
+		)
+
+		if not serial_nos_details:
+			return 0.0
+
+		doc.delete_serial_batch_entries()
+		current_qty = 0.0
+		for serial_no_row in serial_nos_details:
+			current_qty += 1
+			doc.append(
+				"entries",
+				{
+					"serial_no": serial_no_row.serial_no,
+					"qty": -1,
+					"warehouse": doc.warehouse,
+					"batch_no": serial_no_row.batch_no,
+				},
+			)
+
+		doc.set_incoming_rate(save=True)
+		doc.calculate_qty_and_amount(save=True)
+		doc.db_update_all()
+
+		return current_qty
+
+	def get_current_qty_for_batch_nos(self, doc):
+		current_qty = 0.0
+		precision = doc.entries[0].precision("qty")
 		for d in doc.entries:
 			qty = (
 				get_batch_qty(
@@ -864,7 +924,7 @@
 
 			current_qty += qty
 
-		return abs(current_qty)
+		return current_qty
 
 
 def get_batch_qty_for_stock_reco(
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 70e9fb2..0bbfed4 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -925,6 +925,74 @@
 
 			self.assertEqual(len(serial_batch_bundle), 0)
 
+	def test_backdated_purchase_receipt_with_stock_reco(self):
+		item_code = self.make_item(
+			properties={
+				"is_stock_item": 1,
+				"has_serial_no": 1,
+				"serial_no_series": "TEST-SERIAL-.###",
+			}
+		).name
+
+		warehouse = "_Test Warehouse - _TC"
+
+		# Step - 1: Create a Backdated Purchase Receipt
+
+		pr1 = make_purchase_receipt(
+			item_code=item_code, warehouse=warehouse, qty=10, rate=100, posting_date=add_days(nowdate(), -3)
+		)
+		pr1.reload()
+
+		serial_nos = sorted(get_serial_nos_from_bundle(pr1.items[0].serial_and_batch_bundle))[:5]
+
+		# Step - 2: Create a Stock Reconciliation
+		sr1 = create_stock_reconciliation(
+			item_code=item_code,
+			warehouse=warehouse,
+			qty=5,
+			serial_no=serial_nos,
+		)
+
+		data = frappe.get_all(
+			"Stock Ledger Entry",
+			fields=["serial_no", "actual_qty", "stock_value_difference"],
+			filters={"voucher_no": sr1.name, "is_cancelled": 0},
+			order_by="creation",
+		)
+
+		for d in data:
+			if d.actual_qty < 0:
+				self.assertEqual(d.actual_qty, -10.0)
+				self.assertAlmostEqual(d.stock_value_difference, -1000.0)
+			else:
+				self.assertEqual(d.actual_qty, 5.0)
+				self.assertAlmostEqual(d.stock_value_difference, 500.0)
+
+		# Step - 3: Create a Purchase Receipt before the first Purchase Receipt
+		make_purchase_receipt(
+			item_code=item_code, warehouse=warehouse, qty=10, rate=200, posting_date=add_days(nowdate(), -5)
+		)
+
+		data = frappe.get_all(
+			"Stock Ledger Entry",
+			fields=["serial_no", "actual_qty", "stock_value_difference"],
+			filters={"voucher_no": sr1.name, "is_cancelled": 0},
+			order_by="creation",
+		)
+
+		for d in data:
+			if d.actual_qty < 0:
+				self.assertEqual(d.actual_qty, -20.0)
+				self.assertAlmostEqual(d.stock_value_difference, -3000.0)
+			else:
+				self.assertEqual(d.actual_qty, 5.0)
+				self.assertAlmostEqual(d.stock_value_difference, 500.0)
+
+		active_serial_no = frappe.get_all(
+			"Serial No", filters={"status": "Active", "item_code": item_code}
+		)
+		self.assertEqual(len(active_serial_no), 5)
+
 
 def create_batch_item_with_batch(item_name, batch_id):
 	batch_item_doc = create_item(item_name, is_stock_item=1)
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index 1228290..f84456a 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -176,7 +176,7 @@
    "description": "No stock transactions can be created or modified before this date.",
    "fieldname": "stock_frozen_upto",
    "fieldtype": "Date",
-   "label": "Stock Frozen Upto"
+   "label": "Stock Frozen Up To"
   },
   {
    "description": "Stock transactions that are older than the mentioned days cannot be modified.",
@@ -427,7 +427,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-10-18 12:35:30.068799",
+ "modified": "2024-01-24 02:20:26.145996",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Settings",
diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py
index 4cd9cbb..276531a 100644
--- a/erpnext/stock/reorder_item.py
+++ b/erpnext/stock/reorder_item.py
@@ -145,6 +145,7 @@
 
 		mr.log_error("Unable to create material request")
 
+	company_wise_mr = frappe._dict({})
 	for request_type in material_requests:
 		for company in material_requests[request_type]:
 			try:
@@ -206,17 +207,19 @@
 				mr.submit()
 				mr_list.append(mr)
 
+				company_wise_mr.setdefault(company, []).append(mr)
+
 			except Exception:
 				_log_exception(mr)
 
-	if mr_list:
+	if company_wise_mr:
 		if getattr(frappe.local, "reorder_email_notify", None) is None:
 			frappe.local.reorder_email_notify = cint(
 				frappe.db.get_single_value("Stock Settings", "reorder_email_notify")
 			)
 
 		if frappe.local.reorder_email_notify:
-			send_email_notification(mr_list)
+			send_email_notification(company_wise_mr)
 
 	if exceptions_list:
 		notify_errors(exceptions_list)
@@ -224,20 +227,56 @@
 	return mr_list
 
 
-def send_email_notification(mr_list):
+def send_email_notification(company_wise_mr):
 	"""Notify user about auto creation of indent"""
 
-	email_list = frappe.db.sql_list(
-		"""select distinct r.parent
-		from `tabHas Role` r, tabUser p
-		where p.name = r.parent and p.enabled = 1 and p.docstatus < 2
-		and r.role in ('Purchase Manager','Stock Manager')
-		and p.name not in ('Administrator', 'All', 'Guest')"""
+	for company, mr_list in company_wise_mr.items():
+		email_list = get_email_list(company)
+
+		if not email_list:
+			continue
+
+		msg = frappe.render_template("templates/emails/reorder_item.html", {"mr_list": mr_list})
+
+		frappe.sendmail(
+			recipients=email_list, subject=_("Auto Material Requests Generated"), message=msg
+		)
+
+
+def get_email_list(company):
+	users = get_comapny_wise_users(company)
+	user_table = frappe.qb.DocType("User")
+	role_table = frappe.qb.DocType("Has Role")
+
+	query = (
+		frappe.qb.from_(user_table)
+		.inner_join(role_table)
+		.on(user_table.name == role_table.parent)
+		.select(user_table.email)
+		.where(
+			(role_table.role.isin(["Purchase Manager", "Stock Manager"]))
+			& (user_table.name.notin(["Administrator", "All", "Guest"]))
+			& (user_table.enabled == 1)
+			& (user_table.docstatus < 2)
+		)
 	)
 
-	msg = frappe.render_template("templates/emails/reorder_item.html", {"mr_list": mr_list})
+	if users:
+		query = query.where(user_table.name.isin(users))
 
-	frappe.sendmail(recipients=email_list, subject=_("Auto Material Requests Generated"), message=msg)
+	emails = query.run(as_dict=True)
+
+	return list(set([email.email for email in emails]))
+
+
+def get_comapny_wise_users(company):
+	users = frappe.get_all(
+		"User Permission",
+		filters={"allow": "Company", "for_value": company, "apply_to_all_doctypes": 1},
+		fields=["user"],
+	)
+
+	return [user.user for user in users]
 
 
 def notify_errors(exceptions_list):
@@ -246,7 +285,7 @@
 		_("Dear System Manager,")
 		+ "<br>"
 		+ _(
-			"An error occured for certain Items while creating Material Requests based on Re-order level. Please rectify these issues :"
+			"An error occurred for certain Items while creating Material Requests based on Re-order level. Please rectify these issues :"
 		)
 		+ "<br>"
 	)
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
index 810dc46..3f5216b 100644
--- a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
@@ -22,9 +22,8 @@
 		{"label": _("Posting Time"), "fieldtype": "Time", "fieldname": "posting_time", "width": 90},
 		{
 			"label": _("Voucher Type"),
-			"fieldtype": "Link",
+			"fieldtype": "Data",
 			"fieldname": "voucher_type",
-			"options": "DocType",
 			"width": 160,
 		},
 		{
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index a6206ac..45764f3 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -9,9 +9,18 @@
 import frappe
 from frappe import _, scrub
 from frappe.model.meta import get_field_precision
-from frappe.query_builder import Case
 from frappe.query_builder.functions import CombineDatetime, Sum
-from frappe.utils import cint, flt, get_link_to_form, getdate, now, nowdate, nowtime, parse_json
+from frappe.utils import (
+	cint,
+	cstr,
+	flt,
+	get_link_to_form,
+	getdate,
+	now,
+	nowdate,
+	nowtime,
+	parse_json,
+)
 
 import erpnext
 from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty
@@ -439,7 +448,7 @@
 		reposting_data = get_reposting_data(doc.reposting_data_file)
 
 	if reposting_data and reposting_data.distinct_item_and_warehouse:
-		return reposting_data.distinct_item_and_warehouse
+		return parse_distinct_items_and_warehouses(reposting_data.distinct_item_and_warehouse)
 
 	distinct_item_warehouses = {}
 
@@ -457,6 +466,16 @@
 	return distinct_item_warehouses
 
 
+def parse_distinct_items_and_warehouses(distinct_items_and_warehouses):
+	new_dict = frappe._dict({})
+
+	# convert string keys to tuple
+	for k, v in distinct_items_and_warehouses.items():
+		new_dict[frappe.safe_eval(k)] = frappe._dict(v)
+
+	return new_dict
+
+
 def get_affected_transactions(doc, reposting_data=None) -> Set[Tuple[str, str]]:
 	if not reposting_data and doc and doc.reposting_data_file:
 		reposting_data = get_reposting_data(doc.reposting_data_file)
@@ -702,11 +721,10 @@
 
 		if (
 			sle.voucher_type == "Stock Reconciliation"
-			and (
-				sle.batch_no or (sle.has_batch_no and sle.serial_and_batch_bundle and not sle.has_serial_no)
-			)
+			and (sle.batch_no or sle.serial_no or sle.serial_and_batch_bundle)
 			and sle.voucher_detail_no
 			and not self.args.get("sle_id")
+			and sle.is_cancelled == 0
 		):
 			self.reset_actual_qty_for_stock_reco(sle)
 
@@ -727,6 +745,23 @@
 
 		if sle.serial_and_batch_bundle:
 			self.calculate_valuation_for_serial_batch_bundle(sle)
+		elif sle.serial_no and not self.args.get("sle_id"):
+			# Only run in reposting
+			self.get_serialized_values(sle)
+			self.wh_data.qty_after_transaction += flt(sle.actual_qty)
+			if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no:
+				self.wh_data.qty_after_transaction = sle.qty_after_transaction
+
+			self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(
+				self.wh_data.valuation_rate
+			)
+		elif (
+			sle.batch_no
+			and frappe.db.get_value("Batch", sle.batch_no, "use_batchwise_valuation", cache=True)
+			and not self.args.get("sle_id")
+		):
+			# Only run in reposting
+			self.update_batched_values(sle)
 		else:
 			if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no and not has_dimensions:
 				# assert
@@ -772,6 +807,45 @@
 		):
 			self.update_outgoing_rate_on_transaction(sle)
 
+	def get_serialized_values(self, sle):
+		incoming_rate = flt(sle.incoming_rate)
+		actual_qty = flt(sle.actual_qty)
+		serial_nos = cstr(sle.serial_no).split("\n")
+
+		if incoming_rate < 0:
+			# wrong incoming rate
+			incoming_rate = self.wh_data.valuation_rate
+
+		stock_value_change = 0
+		if actual_qty > 0:
+			stock_value_change = actual_qty * incoming_rate
+		else:
+			# In case of delivery/stock issue, get average purchase rate
+			# of serial nos of current entry
+			if not sle.is_cancelled:
+				outgoing_value = self.get_incoming_value_for_serial_nos(sle, serial_nos)
+				stock_value_change = -1 * outgoing_value
+			else:
+				stock_value_change = actual_qty * sle.outgoing_rate
+
+		new_stock_qty = self.wh_data.qty_after_transaction + actual_qty
+
+		if new_stock_qty > 0:
+			new_stock_value = (
+				self.wh_data.qty_after_transaction * self.wh_data.valuation_rate
+			) + stock_value_change
+			if new_stock_value >= 0:
+				# calculate new valuation rate only if stock value is positive
+				# else it remains the same as that of previous entry
+				self.wh_data.valuation_rate = new_stock_value / new_stock_qty
+
+		if not self.wh_data.valuation_rate and sle.voucher_detail_no:
+			allow_zero_rate = self.check_if_allow_zero_valuation_rate(
+				sle.voucher_type, sle.voucher_detail_no
+			)
+			if not allow_zero_rate:
+				self.wh_data.valuation_rate = self.get_fallback_rate(sle)
+
 	def reset_actual_qty_for_stock_reco(self, sle):
 		doc = frappe.get_cached_doc("Stock Reconciliation", sle.voucher_no)
 		doc.recalculate_current_qty(sle.voucher_detail_no, sle.creation, sle.actual_qty > 0)
@@ -785,6 +859,36 @@
 			if abs(sle.actual_qty) == 0.0:
 				sle.is_cancelled = 1
 
+		if sle.serial_and_batch_bundle and frappe.get_cached_value(
+			"Item", sle.item_code, "has_serial_no"
+		):
+			self.update_serial_no_status(sle)
+
+	def update_serial_no_status(self, sle):
+		from erpnext.stock.serial_batch_bundle import get_serial_nos
+
+		serial_nos = get_serial_nos(sle.serial_and_batch_bundle)
+		if not serial_nos:
+			return
+
+		warehouse = None
+		status = "Inactive"
+
+		if sle.actual_qty > 0:
+			warehouse = sle.warehouse
+			status = "Active"
+
+		sn_table = frappe.qb.DocType("Serial No")
+
+		query = (
+			frappe.qb.update(sn_table)
+			.set(sn_table.warehouse, warehouse)
+			.set(sn_table.status, status)
+			.where(sn_table.name.isin(serial_nos))
+		)
+
+		query.run()
+
 	def calculate_valuation_for_serial_batch_bundle(self, sle):
 		doc = frappe.get_cached_doc("Serial and Batch Bundle", sle.serial_and_batch_bundle)
 
@@ -1161,11 +1265,12 @@
 			outgoing_rate = get_batch_incoming_rate(
 				item_code=sle.item_code,
 				warehouse=sle.warehouse,
-				serial_and_batch_bundle=sle.serial_and_batch_bundle,
+				batch_no=sle.batch_no,
 				posting_date=sle.posting_date,
 				posting_time=sle.posting_time,
 				creation=sle.creation,
 			)
+
 			if outgoing_rate is None:
 				# This can *only* happen if qty available for the batch is zero.
 				# in such case fall back various other rates.
@@ -1439,11 +1544,10 @@
 
 
 def get_batch_incoming_rate(
-	item_code, warehouse, serial_and_batch_bundle, posting_date, posting_time, creation=None
+	item_code, warehouse, batch_no, posting_date, posting_time, creation=None
 ):
 
 	sle = frappe.qb.DocType("Stock Ledger Entry")
-	batch_ledger = frappe.qb.DocType("Serial and Batch Entry")
 
 	timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
 		posting_date, posting_time
@@ -1454,28 +1558,13 @@
 			== CombineDatetime(posting_date, posting_time)
 		) & (sle.creation < creation)
 
-	batches = frappe.get_all(
-		"Serial and Batch Entry", fields=["batch_no"], filters={"parent": serial_and_batch_bundle}
-	)
-
 	batch_details = (
 		frappe.qb.from_(sle)
-		.inner_join(batch_ledger)
-		.on(sle.serial_and_batch_bundle == batch_ledger.parent)
-		.select(
-			Sum(
-				Case()
-				.when(sle.actual_qty > 0, batch_ledger.qty * batch_ledger.incoming_rate)
-				.else_(batch_ledger.qty * batch_ledger.outgoing_rate * -1)
-			).as_("batch_value"),
-			Sum(Case().when(sle.actual_qty > 0, batch_ledger.qty).else_(batch_ledger.qty * -1)).as_(
-				"batch_qty"
-			),
-		)
+		.select(Sum(sle.stock_value_difference).as_("batch_value"), Sum(sle.actual_qty).as_("batch_qty"))
 		.where(
 			(sle.item_code == item_code)
 			& (sle.warehouse == warehouse)
-			& (batch_ledger.batch_no.isin([row.batch_no for row in batches]))
+			& (sle.batch_no == batch_no)
 			& (sle.is_cancelled == 0)
 		)
 		.where(timestamp_condition)
diff --git a/erpnext/utilities/activation.py b/erpnext/utilities/activation.py
index 4c8379e..581b53d 100644
--- a/erpnext/utilities/activation.py
+++ b/erpnext/utilities/activation.py
@@ -124,7 +124,7 @@
 			doctype="Timesheet",
 			title=_("Add Timesheets"),
 			description=_(
-				"Timesheets help keep track of time, cost and billing for activites done by your team"
+				"Timesheets help keep track of time, cost and billing for activities done by your team"
 			),
 			action=_("Create Timesheet"),
 			route="List/Timesheet",
diff --git a/erpnext/utilities/web_form/addresses/addresses.json b/erpnext/utilities/web_form/addresses/addresses.json
index 2f5e180..4e2d8e3 100644
--- a/erpnext/utilities/web_form/addresses/addresses.json
+++ b/erpnext/utilities/web_form/addresses/addresses.json
@@ -8,26 +8,29 @@
  "allow_print": 0,
  "amount": 0.0,
  "amount_based_on_field": 0,
+ "anonymous": 0,
+ "apply_document_permissions": 1,
+ "condition_json": "[]",
  "creation": "2016-06-24 15:50:33.196990",
  "doc_type": "Address",
  "docstatus": 0,
  "doctype": "Web Form",
  "idx": 0,
  "is_standard": 1,
+ "list_columns": [],
+ "list_title": "",
  "login_required": 1,
  "max_attachment_size": 0,
- "modified": "2019-10-15 06:55:30.405119",
- "modified_by": "Administrator",
+ "modified": "2024-01-24 10:28:35.026064",
+ "modified_by": "rohitw1991@gmail.com",
  "module": "Utilities",
  "name": "addresses",
  "owner": "Administrator",
  "published": 1,
  "route": "address",
- "route_to_success_link": 0,
  "show_attachments": 0,
- "show_in_grid": 0,
+ "show_list": 1,
  "show_sidebar": 0,
- "sidebar_items": [],
  "success_url": "/addresses",
  "title": "Address",
  "web_form_fields": [