Merge branch 'develop' into skip_tcs
diff --git a/.github/workflows/release_notes.yml b/.github/workflows/release_notes.yml
new file mode 100644
index 0000000..e765a66
--- /dev/null
+++ b/.github/workflows/release_notes.yml
@@ -0,0 +1,38 @@
+# This action:
+#
+# 1. Generates release notes using github API.
+# 2. Strips unnecessary info like chore/style etc from notes.
+# 3. Updates release info.
+
+# This action needs to be maintained on all branches that do releases.
+
+name: 'Release Notes'
+
+on:
+  workflow_dispatch:
+    inputs:
+      tag_name:
+        description: 'Tag of release like v13.0.0'
+        required: true
+        type: string
+  release:
+    types: [released]
+
+permissions:
+  contents: read
+
+jobs:
+  regen-notes:
+    name: 'Regenerate release notes'
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Update notes
+      run: |
+        NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json"  /repos/frappe/erpnext/releases/generate-notes  -f tag_name=$RELEASE_TAG | jq -r '.body' | sed  -E '/^\* (chore|ci|test|docs|style)/d' )
+        RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id')
+        gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES"
+
+      env:
+        GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
+        RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }}
diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml
index 9b4db49..2ce1125 100644
--- a/.github/workflows/server-tests-mariadb.yml
+++ b/.github/workflows/server-tests-mariadb.yml
@@ -7,11 +7,9 @@
       - '**.css'
       - '**.md'
       - '**.html'
-  push:
-    branches: [ develop ]
-    paths-ignore:
-      - '**.js'
-      - '**.md'
+  schedule:
+    # Run everday at midnight UTC / 5:30 IST
+    - cron: "0 0 * * *"
   workflow_dispatch:
     inputs:
       user:
diff --git a/erpnext/accounts/dashboard_chart/budget_variance/budget_variance.json b/erpnext/accounts/dashboard_chart/budget_variance/budget_variance.json
index 8631d3d..4883106 100644
--- a/erpnext/accounts/dashboard_chart/budget_variance/budget_variance.json
+++ b/erpnext/accounts/dashboard_chart/budget_variance/budget_variance.json
@@ -4,18 +4,19 @@
  "creation": "2020-07-17 11:25:34.593061",
  "docstatus": 0,
  "doctype": "Dashboard Chart",
- "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"frappe.sys_defaults.fiscal_year\",\"to_fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
+ "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"erpnext.utils.get_fiscal_year()\",\"to_fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
  "filters_json": "{\"period\":\"Monthly\",\"budget_against\":\"Cost Center\",\"show_cumulative\":0}",
  "idx": 0,
  "is_public": 1,
  "is_standard": 1,
- "modified": "2020-07-22 12:24:49.144210",
+ "modified": "2023-07-19 13:13:13.307073",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Budget Variance",
  "number_of_groups": 0,
  "owner": "Administrator",
  "report_name": "Budget Variance Report",
+ "roles": [],
  "timeseries": 0,
  "type": "Bar",
  "use_report_chart": 1,
diff --git a/erpnext/accounts/dashboard_chart/profit_and_loss/profit_and_loss.json b/erpnext/accounts/dashboard_chart/profit_and_loss/profit_and_loss.json
index 3fa995b..25caa44 100644
--- a/erpnext/accounts/dashboard_chart/profit_and_loss/profit_and_loss.json
+++ b/erpnext/accounts/dashboard_chart/profit_and_loss/profit_and_loss.json
@@ -4,18 +4,19 @@
  "creation": "2020-07-17 11:25:34.448572",
  "docstatus": 0,
  "doctype": "Dashboard Chart",
- "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"frappe.sys_defaults.fiscal_year\",\"to_fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
+ "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"erpnext.utils.get_fiscal_year()\",\"to_fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
  "filters_json": "{\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"periodicity\":\"Yearly\",\"include_default_book_entries\":1}",
  "idx": 0,
  "is_public": 1,
  "is_standard": 1,
- "modified": "2020-07-22 12:33:48.888943",
+ "modified": "2023-07-19 13:08:56.470390",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Profit and Loss",
  "number_of_groups": 0,
  "owner": "Administrator",
  "report_name": "Profit and Loss Statement",
+ "roles": [],
  "timeseries": 0,
  "type": "Bar",
  "use_report_chart": 1,
diff --git a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py
index 9540084..e75af70 100644
--- a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py
+++ b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py
@@ -14,10 +14,8 @@
 	pass
 
 
-def make_closing_entries(closing_entries, voucher_name):
+def make_closing_entries(closing_entries, voucher_name, company, closing_date):
 	accounting_dimensions = get_accounting_dimensions()
-	company = closing_entries[0].get("company")
-	closing_date = closing_entries[0].get("closing_date")
 
 	previous_closing_entries = get_previous_closing_entries(
 		company, closing_date, accounting_dimensions
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 81ff6a5..15c84d4 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -271,6 +271,12 @@
 		as_dict=1,
 	)
 
+	if isinstance(with_cost_center_and_project, str):
+		if with_cost_center_and_project.lower().strip() == "true":
+			with_cost_center_and_project = True
+		else:
+			with_cost_center_and_project = False
+
 	if with_cost_center_and_project:
 		dimension_filters.extend(
 			[
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.js b/erpnext/accounts/doctype/accounting_period/accounting_period.js
index e3d805a..f17b6f9 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.js
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.js
@@ -20,5 +20,11 @@
 				}
 			});
 		}
+
+		frm.set_query("document_type", "closed_documents", () => {
+			return {
+				query: "erpnext.controllers.queries.get_doctypes_for_closing",
+			}
+		});
 	}
 });
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py
index 80c9715..d5f37a6 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py
@@ -11,6 +11,10 @@
 	pass
 
 
+class ClosedAccountingPeriod(frappe.ValidationError):
+	pass
+
+
 class AccountingPeriod(Document):
 	def validate(self):
 		self.validate_overlap()
@@ -65,3 +69,42 @@
 					"closed_documents",
 					{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
 				)
+
+
+def validate_accounting_period_on_doc_save(doc, method=None):
+	if doc.doctype == "Bank Clearance":
+		return
+	elif doc.doctype == "Asset":
+		if doc.is_existing_asset:
+			return
+		else:
+			date = doc.available_for_use_date
+	elif doc.doctype == "Asset Repair":
+		date = doc.completion_date
+	else:
+		date = doc.posting_date
+
+	ap = frappe.qb.DocType("Accounting Period")
+	cd = frappe.qb.DocType("Closed Document")
+
+	accounting_period = (
+		frappe.qb.from_(ap)
+		.from_(cd)
+		.select(ap.name)
+		.where(
+			(ap.name == cd.parent)
+			& (ap.company == doc.company)
+			& (cd.closed == 1)
+			& (cd.document_type == doc.doctype)
+			& (date >= ap.start_date)
+			& (date <= ap.end_date)
+		)
+	).run(as_dict=1)
+
+	if accounting_period:
+		frappe.throw(
+			_("You cannot create a {0} within the closed Accounting Period {1}").format(
+				doc.doctype, frappe.bold(accounting_period[0]["name"])
+			),
+			ClosedAccountingPeriod,
+		)
diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
index 85025d1..41d9479 100644
--- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
@@ -6,9 +6,11 @@
 import frappe
 from frappe.utils import add_months, nowdate
 
-from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError
+from erpnext.accounts.doctype.accounting_period.accounting_period import (
+	ClosedAccountingPeriod,
+	OverlapError,
+)
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.accounts.general_ledger import ClosedAccountingPeriod
 
 test_dependencies = ["Item"]
 
@@ -33,9 +35,9 @@
 		ap1.save()
 
 		doc = create_sales_invoice(
-			do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
+			do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
 		)
-		self.assertRaises(ClosedAccountingPeriod, doc.submit)
+		self.assertRaises(ClosedAccountingPeriod, doc.save)
 
 	def tearDown(self):
 		for d in frappe.get_all("Accounting Period"):
diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js
index 35d606b..6667193 100644
--- a/erpnext/accounts/doctype/bank/bank.js
+++ b/erpnext/accounts/doctype/bank/bank.js
@@ -8,9 +8,6 @@
 	},
 	refresh: function(frm) {
 		add_fields_to_mapping_table(frm);
-
-		frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank' };
-
 		frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
 
 		if (frm.doc.__islocal) {
diff --git a/erpnext/accounts/doctype/dunning/dunning.js b/erpnext/accounts/doctype/dunning/dunning.js
index 9909c6c..1ac909e 100644
--- a/erpnext/accounts/doctype/dunning/dunning.js
+++ b/erpnext/accounts/doctype/dunning/dunning.js
@@ -1,13 +1,14 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
 frappe.ui.form.on("Dunning", {
 	setup: function (frm) {
-		frm.set_query("sales_invoice", () => {
+		frm.set_query("sales_invoice", "overdue_payments", () => {
 			return {
 				filters: {
 					docstatus: 1,
 					company: frm.doc.company,
+					customer: frm.doc.customer,
 					outstanding_amount: [">", 0],
 					status: "Overdue"
 				},
@@ -22,14 +23,24 @@
 				}
 			};
 		});
+		frm.set_query("cost_center", () => {
+			return {
+				filters: {
+					company: frm.doc.company,
+					is_group: 0
+				}
+			};
+		});
+
+		frm.set_query("contact_person", erpnext.queries.contact_query);
+		frm.set_query("customer_address", erpnext.queries.address_query);
+		frm.set_query("company_address", erpnext.queries.company_address_query);
+
+		// cannot add rows manually, only via button "Fetch Overdue Payments"
+		frm.set_df_property("overdue_payments", "cannot_add_rows", true);
 	},
 	refresh: function (frm) {
 		frm.set_df_property("company", "read_only", frm.doc.__islocal ? 0 : 1);
-		frm.set_df_property(
-			"sales_invoice",
-			"read_only",
-			frm.doc.__islocal ? 0 : 1
-		);
 		if (frm.doc.docstatus === 1 && frm.doc.status === "Unresolved") {
 			frm.add_custom_button(__("Resolve"), () => {
 				frm.set_value("status", "Resolved");
@@ -40,42 +51,111 @@
 				__("Payment"),
 				function () {
 					frm.events.make_payment_entry(frm);
-				},__("Create")
+				}, __("Create")
 			);
 			frm.page.set_inner_btn_group_as_primary(__("Create"));
 		}
 
-		if(frm.doc.docstatus > 0) {
-			frm.add_custom_button(__('Ledger'), function() {
-				frappe.route_options = {
-					"voucher_no": frm.doc.name,
-					"from_date": frm.doc.posting_date,
-					"to_date": frm.doc.posting_date,
-					"company": frm.doc.company,
-					"show_cancelled_entries": frm.doc.docstatus === 2
-				};
-				frappe.set_route("query-report", "General Ledger");
-			}, __('View'));
+		if (frm.doc.docstatus === 0) {
+			frm.add_custom_button(__("Fetch Overdue Payments"), () => {
+				erpnext.utils.map_current_doc({
+					method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_dunning",
+					source_doctype: "Sales Invoice",
+					date_field: "due_date",
+					target: frm,
+					setters: {
+						customer: frm.doc.customer || undefined,
+					},
+					get_query_filters: {
+						docstatus: 1,
+						status: "Overdue",
+						company: frm.doc.company
+					},
+					allow_child_item_selection: true,
+					child_fieldname: "payment_schedule",
+					child_columns: ["due_date", "outstanding"],
+				});
+			});
 		}
+
+		frappe.dynamic_link = { doc: frm.doc, fieldname: 'customer', doctype: 'Customer' };
+
+		frm.toggle_display("customer_name", (frm.doc.customer_name && frm.doc.customer_name !== frm.doc.customer));
 	},
-	overdue_days: function (frm) {
-		frappe.db.get_value(
-			"Dunning Type",
-			{
-				start_day: ["<", frm.doc.overdue_days],
-				end_day: [">=", frm.doc.overdue_days],
-			},
-			"dunning_type",
-			(r) => {
-				if (r) {
-					frm.set_value("dunning_type", r.dunning_type);
-				} else {
-					frm.set_value("dunning_type", "");
-					frm.set_value("rate_of_interest", "");
-					frm.set_value("dunning_fee", "");
+	// When multiple companies are set up. in case company name is changed set default company address
+	company: function (frm) {
+		if (frm.doc.company) {
+			frappe.call({
+				method: "erpnext.setup.doctype.company.company.get_default_company_address",
+				args: { name: frm.doc.company, existing_address: frm.doc.company_address || "" },
+				debounce: 2000,
+				callback: function (r) {
+					frm.set_value("company_address", r && r.message || "");
+				}
+			});
+
+			if (frm.fields_dict.currency) {
+				const company_currency = erpnext.get_currency(frm.doc.company);
+
+				if (!frm.doc.currency) {
+					frm.set_value("currency", company_currency);
+				}
+
+				if (frm.doc.currency == company_currency) {
+					frm.set_value("conversion_rate", 1.0);
 				}
 			}
-		);
+
+			const company_doc = frappe.get_doc(":Company", frm.doc.company);
+			if (company_doc.default_letter_head) {
+				if (frm.fields_dict.letter_head) {
+					frm.set_value("letter_head", company_doc.default_letter_head);
+				}
+			}
+		}
+	},
+	currency: function (frm) {
+		// this.set_dynamic_labels();
+		const company_currency = erpnext.get_currency(frm.doc.company);
+		// Added `ignore_pricing_rule` to determine if document is loading after mapping from another doc
+		if (frm.doc.currency && frm.doc.currency !== company_currency) {
+			frappe.call({
+				method: "erpnext.setup.utils.get_exchange_rate",
+				args: {
+					transaction_date: frm.doc.posting_date,
+					from_currency: frm.doc.currency,
+					to_currency: company_currency,
+					args: "for_selling"
+				},
+				freeze: true,
+				freeze_message: __("Fetching exchange rates ..."),
+				callback: function(r) {
+					const exchange_rate = flt(r.message);
+					if (exchange_rate != frm.doc.conversion_rate) {
+						frm.set_value("conversion_rate", exchange_rate);
+					}
+				}
+			});
+		} else {
+			frm.trigger("conversion_rate");
+		}
+	},
+	customer: (frm) => {
+		erpnext.utils.get_party_details(frm);
+	},
+	conversion_rate: function (frm) {
+		if (frm.doc.currency === erpnext.get_currency(frm.doc.company)) {
+			frm.set_value("conversion_rate", 1.0);
+		}
+
+		// Make read only if Accounts Settings doesn't allow stale rates
+		frm.set_df_property("conversion_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1);
+	},
+	customer_address: function (frm) {
+		erpnext.utils.get_address_display(frm, "customer_address");
+	},
+	company_address: function (frm) {
+		erpnext.utils.get_address_display(frm, "company_address");
 	},
 	dunning_type: function (frm) {
 		frm.trigger("get_dunning_letter_text");
@@ -87,7 +167,7 @@
 		if (frm.doc.dunning_type) {
 			frappe.call({
 				method:
-				"erpnext.accounts.doctype.dunning.dunning.get_dunning_letter_text",
+					"erpnext.accounts.doctype.dunning.dunning.get_dunning_letter_text",
 				args: {
 					dunning_type: frm.doc.dunning_type,
 					language: frm.doc.language,
@@ -106,49 +186,62 @@
 			});
 		}
 	},
-	due_date: function (frm) {
-		frm.trigger("calculate_overdue_days");
-	},
 	posting_date: function (frm) {
 		frm.trigger("calculate_overdue_days");
 	},
 	rate_of_interest: function (frm) {
-		frm.trigger("calculate_interest_and_amount");
-	},
-	outstanding_amount: function (frm) {
-		frm.trigger("calculate_interest_and_amount");
-	},
-	interest_amount: function (frm) {
-		frm.trigger("calculate_interest_and_amount");
+		frm.trigger("calculate_interest");
 	},
 	dunning_fee: function (frm) {
-		frm.trigger("calculate_interest_and_amount");
+		frm.trigger("calculate_totals");
 	},
-	sales_invoice: function (frm) {
-		frm.trigger("calculate_overdue_days");
+	overdue_payments_add: function (frm) {
+		frm.trigger("calculate_totals");
+	},
+	overdue_payments_remove: function (frm) {
+		frm.trigger("calculate_totals");
 	},
 	calculate_overdue_days: function (frm) {
-		if (frm.doc.posting_date && frm.doc.due_date) {
-			const overdue_days = moment(frm.doc.posting_date).diff(
-				frm.doc.due_date,
-				"days"
-			);
-			frm.set_value("overdue_days", overdue_days);
-		}
+		frm.doc.overdue_payments.forEach((row) => {
+			if (frm.doc.posting_date && row.due_date) {
+				const overdue_days = moment(frm.doc.posting_date).diff(
+					row.due_date,
+					"days"
+				);
+				frappe.model.set_value(row.doctype, row.name, "overdue_days", overdue_days);
+			}
+		});
 	},
-	calculate_interest_and_amount: function (frm) {
-		const interest_per_year = frm.doc.outstanding_amount * frm.doc.rate_of_interest / 100;
-		const interest_amount = flt((interest_per_year * cint(frm.doc.overdue_days)) / 365 || 0, precision('interest_amount'));
-		const dunning_amount = flt(interest_amount + frm.doc.dunning_fee, precision('dunning_amount'));
-		const grand_total = flt(frm.doc.outstanding_amount + dunning_amount, precision('grand_total'));
-		frm.set_value("interest_amount", interest_amount);
-		frm.set_value("dunning_amount", dunning_amount);
-		frm.set_value("grand_total", grand_total);
+	calculate_interest: function (frm) {
+		frm.doc.overdue_payments.forEach((row) => {
+			const interest_per_day = frm.doc.rate_of_interest / 100 / 365;
+			const interest = flt((interest_per_day * row.overdue_days * row.outstanding), precision("interest", row));
+			frappe.model.set_value(row.doctype, row.name, "interest", interest);
+		});
+	},
+	calculate_totals: function (frm) {
+		const total_interest = frm.doc.overdue_payments
+			.reduce((prev, cur) => prev + cur.interest, 0);
+		const total_outstanding = frm.doc.overdue_payments
+			.reduce((prev, cur) => prev + cur.outstanding, 0);
+		const dunning_amount = total_interest + frm.doc.dunning_fee;
+		const base_dunning_amount = dunning_amount * frm.doc.conversion_rate;
+		const grand_total = total_outstanding + dunning_amount;
+
+		function setWithPrecison(field, value) {
+			frm.set_value(field, flt(value, precision(field)));
+		}
+
+		setWithPrecison("total_outstanding", total_outstanding);
+		setWithPrecison("total_interest", total_interest);
+		setWithPrecison("dunning_amount", dunning_amount);
+		setWithPrecison("base_dunning_amount", base_dunning_amount);
+		setWithPrecison("grand_total", grand_total);
 	},
 	make_payment_entry: function (frm) {
 		return frappe.call({
 			method:
-			"erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry",
+				"erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry",
 			args: {
 				dt: frm.doc.doctype,
 				dn: frm.doc.name,
@@ -160,3 +253,9 @@
 		});
 	},
 });
+
+frappe.ui.form.on("Overdue Payment", {
+	interest: function (frm) {
+		frm.trigger("calculate_totals");
+	}
+});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/dunning/dunning.json b/erpnext/accounts/doctype/dunning/dunning.json
index 2a32b99..b7e8aea 100644
--- a/erpnext/accounts/doctype/dunning/dunning.json
+++ b/erpnext/accounts/doctype/dunning/dunning.json
@@ -2,49 +2,60 @@
  "actions": [],
  "allow_events_in_timeline": 1,
  "autoname": "naming_series:",
+ "beta": 1,
  "creation": "2019-07-05 16:34:31.013238",
  "doctype": "DocType",
  "engine": "InnoDB",
  "field_order": [
-  "title",
   "naming_series",
-  "sales_invoice",
   "customer",
   "customer_name",
-  "outstanding_amount",
-  "currency",
-  "conversion_rate",
   "column_break_3",
   "company",
   "posting_date",
   "posting_time",
-  "due_date",
-  "overdue_days",
+  "status",
+  "section_break_9",
+  "currency",
+  "column_break_11",
+  "conversion_rate",
   "address_and_contact_section",
+  "customer_address",
   "address_display",
+  "contact_person",
   "contact_display",
+  "column_break_16",
+  "company_address",
+  "company_address_display",
   "contact_mobile",
   "contact_email",
-  "column_break_18",
-  "company_address_display",
   "section_break_6",
   "dunning_type",
-  "dunning_fee",
   "column_break_8",
   "rate_of_interest",
-  "interest_amount",
   "section_break_12",
-  "dunning_amount",
-  "grand_total",
-  "income_account",
+  "overdue_payments",
+  "section_break_28",
+  "total_interest",
+  "dunning_fee",
   "column_break_17",
-  "status",
-  "printing_setting_section",
+  "dunning_amount",
+  "base_dunning_amount",
+  "section_break_32",
+  "spacer",
+  "column_break_33",
+  "total_outstanding",
+  "grand_total",
+  "printing_settings_section",
   "language",
   "body_text",
   "column_break_22",
   "letter_head",
   "closing_text",
+  "accounting_details_section",
+  "income_account",
+  "column_break_48",
+  "cost_center",
   "amended_from"
  ],
  "fields": [
@@ -60,19 +71,11 @@
    "fieldname": "naming_series",
    "fieldtype": "Select",
    "label": "Series",
-   "options": "DUNN-.MM.-.YY.-"
+   "options": "DUNN-.MM.-.YY.-",
+   "print_hide": 1
   },
   {
-   "fieldname": "sales_invoice",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Sales Invoice",
-   "options": "Sales Invoice",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "sales_invoice.customer_name",
+   "fetch_from": "customer.customer_name",
    "fieldname": "customer_name",
    "fieldtype": "Data",
    "in_list_view": 1,
@@ -80,13 +83,6 @@
    "read_only": 1
   },
   {
-   "fetch_from": "sales_invoice.outstanding_amount",
-   "fieldname": "outstanding_amount",
-   "fieldtype": "Currency",
-   "label": "Outstanding Amount",
-   "read_only": 1
-  },
-  {
    "fieldname": "column_break_3",
    "fieldtype": "Column Break"
   },
@@ -94,13 +90,8 @@
    "default": "Today",
    "fieldname": "posting_date",
    "fieldtype": "Date",
-   "label": "Date"
-  },
-  {
-   "fieldname": "overdue_days",
-   "fieldtype": "Int",
-   "label": "Overdue Days",
-   "read_only": 1
+   "label": "Date",
+   "reqd": 1
   },
   {
    "fieldname": "section_break_6",
@@ -112,16 +103,7 @@
    "in_list_view": 1,
    "in_standard_filter": 1,
    "label": "Dunning Type",
-   "options": "Dunning Type",
-   "reqd": 1
-  },
-  {
-   "default": "0",
-   "fieldname": "interest_amount",
-   "fieldtype": "Currency",
-   "label": "Interest Amount",
-   "precision": "2",
-   "read_only": 1
+   "options": "Dunning Type"
   },
   {
    "fieldname": "column_break_8",
@@ -134,6 +116,7 @@
    "fieldname": "dunning_fee",
    "fieldtype": "Currency",
    "label": "Dunning Fee",
+   "options": "currency",
    "precision": "2"
   },
   {
@@ -145,36 +128,24 @@
    "fieldtype": "Column Break"
   },
   {
-   "fieldname": "printing_setting_section",
-   "fieldtype": "Section Break",
-   "label": "Printing Setting"
-  },
-  {
    "fieldname": "language",
    "fieldtype": "Link",
    "label": "Print Language",
-   "options": "Language"
+   "options": "Language",
+   "print_hide": 1
   },
   {
    "fieldname": "letter_head",
    "fieldtype": "Link",
    "label": "Letter Head",
-   "options": "Letter Head"
+   "options": "Letter Head",
+   "print_hide": 1
   },
   {
    "fieldname": "column_break_22",
    "fieldtype": "Column Break"
   },
   {
-   "fetch_from": "sales_invoice.currency",
-   "fieldname": "currency",
-   "fieldtype": "Link",
-   "hidden": 1,
-   "label": "Currency",
-   "options": "Currency",
-   "read_only": 1
-  },
-  {
    "fieldname": "amended_from",
    "fieldtype": "Link",
    "label": "Amended From",
@@ -184,14 +155,6 @@
    "read_only": 1
   },
   {
-   "allow_on_submit": 1,
-   "default": "{customer_name}",
-   "fieldname": "title",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "Title"
-  },
-  {
    "fieldname": "body_text",
    "fieldtype": "Text Editor",
    "label": "Body Text"
@@ -202,13 +165,6 @@
    "label": "Closing Text"
   },
   {
-   "fetch_from": "sales_invoice.due_date",
-   "fieldname": "due_date",
-   "fieldtype": "Date",
-   "label": "Due Date",
-   "read_only": 1
-  },
-  {
    "fieldname": "posting_time",
    "fieldtype": "Time",
    "label": "Posting Time"
@@ -222,26 +178,24 @@
    "label": "Rate of Interest (%) Yearly"
   },
   {
+   "collapsible": 1,
    "fieldname": "address_and_contact_section",
    "fieldtype": "Section Break",
    "label": "Address and Contact"
   },
   {
-   "fetch_from": "sales_invoice.address_display",
    "fieldname": "address_display",
    "fieldtype": "Small Text",
    "label": "Address",
    "read_only": 1
   },
   {
-   "fetch_from": "sales_invoice.contact_display",
    "fieldname": "contact_display",
    "fieldtype": "Small Text",
    "label": "Contact",
    "read_only": 1
   },
   {
-   "fetch_from": "sales_invoice.contact_mobile",
    "fieldname": "contact_mobile",
    "fieldtype": "Small Text",
    "label": "Mobile No",
@@ -249,18 +203,12 @@
    "read_only": 1
   },
   {
-   "fieldname": "column_break_18",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "sales_invoice.company_address_display",
    "fieldname": "company_address_display",
    "fieldtype": "Small Text",
-   "label": "Company Address",
+   "label": "Company Address Display",
    "read_only": 1
   },
   {
-   "fetch_from": "sales_invoice.contact_email",
    "fieldname": "contact_email",
    "fieldtype": "Data",
    "label": "Contact Email",
@@ -268,18 +216,18 @@
    "read_only": 1
   },
   {
-   "fetch_from": "sales_invoice.customer",
    "fieldname": "customer",
    "fieldtype": "Link",
    "label": "Customer",
    "options": "Customer",
-   "read_only": 1
+   "reqd": 1
   },
   {
    "default": "0",
    "fieldname": "grand_total",
    "fieldtype": "Currency",
    "label": "Grand Total",
+   "options": "currency",
    "precision": "2",
    "read_only": 1
   },
@@ -290,33 +238,150 @@
    "fieldtype": "Select",
    "in_standard_filter": 1,
    "label": "Status",
-   "options": "Draft\nResolved\nUnresolved\nCancelled"
-  },
-  {
-   "fieldname": "dunning_amount",
-   "fieldtype": "Currency",
-   "hidden": 1,
-   "label": "Dunning Amount",
+   "options": "Draft\nResolved\nUnresolved\nCancelled",
    "read_only": 1
   },
   {
+   "description": "For dunning fee and interest",
+   "fetch_from": "dunning_type.income_account",
    "fieldname": "income_account",
    "fieldtype": "Link",
    "label": "Income Account",
-   "options": "Account"
+   "options": "Account",
+   "print_hide": 1
   },
   {
-   "fetch_from": "sales_invoice.conversion_rate",
+   "fieldname": "overdue_payments",
+   "fieldtype": "Table",
+   "label": "Overdue Payments",
+   "options": "Overdue Payment"
+  },
+  {
+   "fieldname": "section_break_28",
+   "fieldtype": "Section Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "total_interest",
+   "fieldtype": "Currency",
+   "label": "Total Interest",
+   "options": "currency",
+   "precision": "2",
+   "read_only": 1
+  },
+  {
+   "fieldname": "total_outstanding",
+   "fieldtype": "Currency",
+   "label": "Total Outstanding",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "customer_address",
+   "fieldtype": "Link",
+   "label": "Customer Address",
+   "options": "Address",
+   "print_hide": 1
+  },
+  {
+   "fieldname": "contact_person",
+   "fieldtype": "Link",
+   "label": "Contact Person",
+   "options": "Contact",
+   "print_hide": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "dunning_amount",
+   "fieldtype": "Currency",
+   "label": "Dunning Amount",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_details_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Details"
+  },
+  {
+   "fetch_from": "dunning_type.cost_center",
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center",
+   "print_hide": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "printing_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Printing Settings"
+  },
+  {
+   "fieldname": "section_break_32",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_33",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "spacer",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Spacer",
+   "print_hide": 1,
+   "read_only": 1,
+   "report_hide": 1
+  },
+  {
+   "fieldname": "column_break_16",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "company_address",
+   "fieldtype": "Link",
+   "label": "Company Address",
+   "options": "Address",
+   "print_hide": 1
+  },
+  {
+   "fieldname": "section_break_9",
+   "fieldtype": "Section Break",
+   "label": "Currency"
+  },
+  {
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "label": "Currency",
+   "options": "Currency"
+  },
+  {
+   "fieldname": "column_break_11",
+   "fieldtype": "Column Break"
+  },
+  {
    "fieldname": "conversion_rate",
    "fieldtype": "Float",
-   "hidden": 1,
-   "label": "Conversion Rate",
+   "label": "Conversion Rate"
+  },
+  {
+   "default": "0",
+   "fieldname": "base_dunning_amount",
+   "fieldtype": "Currency",
+   "label": "Dunning Amount (Company Currency)",
+   "options": "Company:company:default_currency",
    "read_only": 1
+  },
+  {
+   "fieldname": "column_break_48",
+   "fieldtype": "Column Break"
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2023-06-03 16:24:01.677026",
+ "modified": "2023-06-15 15:46:53.865712",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Dunning",
diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index b4df0a5..9d0d36b 100644
--- a/erpnext/accounts/doctype/dunning/dunning.py
+++ b/erpnext/accounts/doctype/dunning/dunning.py
@@ -1,131 +1,150 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
+"""
+# Accounting
 
+1. Payment of outstanding invoices with dunning amount
 
+		- Debit full amount to bank
+		- Credit invoiced amount to receivables
+		- Credit dunning amount to interest and similar revenue
+
+		-> Resolves dunning automatically
+"""
 import json
 
 import frappe
-from frappe.utils import cint, flt, getdate
+from frappe import _
+from frappe.contacts.doctype.address.address import get_address_display
+from frappe.utils import getdate
 
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
-	get_accounting_dimensions,
-)
-from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
 from erpnext.controllers.accounts_controller import AccountsController
 
 
 class Dunning(AccountsController):
 	def validate(self):
-		self.validate_overdue_days()
-		self.validate_amount()
-		if not self.income_account:
-			self.income_account = frappe.get_cached_value("Company", self.company, "default_income_account")
+		self.validate_same_currency()
+		self.validate_overdue_payments()
+		self.validate_totals()
+		self.set_party_details()
+		self.set_dunning_level()
 
-	def validate_overdue_days(self):
-		self.overdue_days = (getdate(self.posting_date) - getdate(self.due_date)).days or 0
+	def validate_same_currency(self):
+		"""
+		Throw an error if invoice currency differs from dunning currency.
+		"""
+		for row in self.overdue_payments:
+			invoice_currency = frappe.get_value("Sales Invoice", row.sales_invoice, "currency")
+			if invoice_currency != self.currency:
+				frappe.throw(
+					_(
+						"The currency of invoice {} ({}) is different from the currency of this dunning ({})."
+					).format(row.sales_invoice, invoice_currency, self.currency)
+				)
 
-	def validate_amount(self):
-		amounts = calculate_interest_and_amount(
-			self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days
+	def validate_overdue_payments(self):
+		daily_interest = self.rate_of_interest / 100 / 365
+
+		for row in self.overdue_payments:
+			row.overdue_days = (getdate(self.posting_date) - getdate(row.due_date)).days or 0
+			row.interest = row.outstanding * daily_interest * row.overdue_days
+
+	def validate_totals(self):
+		self.total_outstanding = sum(row.outstanding for row in self.overdue_payments)
+		self.total_interest = sum(row.interest for row in self.overdue_payments)
+		self.dunning_amount = self.total_interest + self.dunning_fee
+		self.base_dunning_amount = self.dunning_amount * self.conversion_rate
+		self.grand_total = self.total_outstanding + self.dunning_amount
+
+	def set_party_details(self):
+		from erpnext.accounts.party import _get_party_details
+
+		party_details = _get_party_details(
+			self.customer,
+			ignore_permissions=self.flags.ignore_permissions,
+			doctype=self.doctype,
+			company=self.company,
+			posting_date=self.get("posting_date"),
+			fetch_payment_terms_template=False,
+			party_address=self.customer_address,
+			company_address=self.get("company_address"),
 		)
-		if self.interest_amount != amounts.get("interest_amount"):
-			self.interest_amount = flt(amounts.get("interest_amount"), self.precision("interest_amount"))
-		if self.dunning_amount != amounts.get("dunning_amount"):
-			self.dunning_amount = flt(amounts.get("dunning_amount"), self.precision("dunning_amount"))
-		if self.grand_total != amounts.get("grand_total"):
-			self.grand_total = flt(amounts.get("grand_total"), self.precision("grand_total"))
+		for field in [
+			"customer_address",
+			"address_display",
+			"company_address",
+			"contact_person",
+			"contact_display",
+			"contact_mobile",
+		]:
+			self.set(field, party_details.get(field))
 
-	def on_submit(self):
-		self.make_gl_entries()
+		self.set("company_address_display", get_address_display(self.company_address))
 
-	def on_cancel(self):
-		if self.dunning_amount:
-			self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
-			make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
-
-	def make_gl_entries(self):
-		if not self.dunning_amount:
-			return
-		gl_entries = []
-		invoice_fields = [
-			"project",
-			"cost_center",
-			"debit_to",
-			"party_account_currency",
-			"conversion_rate",
-			"cost_center",
-		]
-		inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
-
-		accounting_dimensions = get_accounting_dimensions()
-		invoice_fields.extend(accounting_dimensions)
-
-		dunning_in_company_currency = flt(self.dunning_amount * inv.conversion_rate)
-		default_cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
-
-		gl_entries.append(
-			self.get_gl_dict(
-				{
-					"account": inv.debit_to,
-					"party_type": "Customer",
-					"party": self.customer,
-					"due_date": self.due_date,
-					"against": self.income_account,
-					"debit": dunning_in_company_currency,
-					"debit_in_account_currency": self.dunning_amount,
-					"against_voucher": self.name,
-					"against_voucher_type": "Dunning",
-					"cost_center": inv.cost_center or default_cost_center,
-					"project": inv.project,
+	def set_dunning_level(self):
+		for row in self.overdue_payments:
+			past_dunnings = frappe.get_all(
+				"Overdue Payment",
+				filters={
+					"payment_schedule": row.payment_schedule,
+					"parent": ("!=", row.parent),
+					"docstatus": 1,
 				},
-				inv.party_account_currency,
-				item=inv,
 			)
-		)
-		gl_entries.append(
-			self.get_gl_dict(
-				{
-					"account": self.income_account,
-					"against": self.customer,
-					"credit": dunning_in_company_currency,
-					"cost_center": inv.cost_center or default_cost_center,
-					"credit_in_account_currency": self.dunning_amount,
-					"project": inv.project,
-				},
-				item=inv,
-			)
-		)
-		make_gl_entries(
-			gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False
-		)
+			row.dunning_level = len(past_dunnings) + 1
 
 
 def resolve_dunning(doc, state):
+	"""
+	Check if all payments have been made and resolve dunning, if yes. Called
+	when a Payment Entry is submitted.
+	"""
 	for reference in doc.references:
-		if reference.reference_doctype == "Sales Invoice" and reference.outstanding_amount <= 0:
-			dunnings = frappe.get_list(
-				"Dunning",
-				filters={"sales_invoice": reference.reference_name, "status": ("!=", "Resolved")},
-				ignore_permissions=True,
-			)
+		# Consider partial and full payments:
+		# Submitting full payment: outstanding_amount will be 0
+		# Submitting 1st partial payment: outstanding_amount will be the pending installment
+		# Cancelling full payment: outstanding_amount will revert to total amount
+		# Cancelling last partial payment: outstanding_amount will revert to pending amount
+		submit_condition = reference.outstanding_amount < reference.total_amount
+		cancel_condition = reference.outstanding_amount <= reference.total_amount
+
+		if reference.reference_doctype == "Sales Invoice" and (
+			submit_condition if doc.docstatus == 1 else cancel_condition
+		):
+			state = "Resolved" if doc.docstatus == 2 else "Unresolved"
+			dunnings = get_linked_dunnings_as_per_state(reference.reference_name, state)
 
 			for dunning in dunnings:
-				frappe.db.set_value("Dunning", dunning.name, "status", "Resolved")
+				resolve = True
+				dunning = frappe.get_doc("Dunning", dunning.get("name"))
+				for overdue_payment in dunning.overdue_payments:
+					outstanding_inv = frappe.get_value(
+						"Sales Invoice", overdue_payment.sales_invoice, "outstanding_amount"
+					)
+					outstanding_ps = frappe.get_value(
+						"Payment Schedule", overdue_payment.payment_schedule, "outstanding"
+					)
+					resolve = False if (outstanding_ps > 0 and outstanding_inv > 0) else True
+
+				dunning.status = "Resolved" if resolve else "Unresolved"
+				dunning.save()
 
 
-def calculate_interest_and_amount(outstanding_amount, rate_of_interest, dunning_fee, overdue_days):
-	interest_amount = 0
-	grand_total = flt(outstanding_amount) + flt(dunning_fee)
-	if rate_of_interest:
-		interest_per_year = flt(outstanding_amount) * flt(rate_of_interest) / 100
-		interest_amount = (interest_per_year * cint(overdue_days)) / 365
-		grand_total += flt(interest_amount)
-	dunning_amount = flt(interest_amount) + flt(dunning_fee)
-	return {
-		"interest_amount": interest_amount,
-		"grand_total": grand_total,
-		"dunning_amount": dunning_amount,
-	}
+def get_linked_dunnings_as_per_state(sales_invoice, state):
+	dunning = frappe.qb.DocType("Dunning")
+	overdue_payment = frappe.qb.DocType("Overdue Payment")
+
+	return (
+		frappe.qb.from_(dunning)
+		.join(overdue_payment)
+		.on(overdue_payment.parent == dunning.name)
+		.select(dunning.name)
+		.where(
+			(dunning.status == state)
+			& (dunning.docstatus != 2)
+			& (overdue_payment.sales_invoice == sales_invoice)
+		)
+	).run(as_dict=True)
 
 
 @frappe.whitelist()
diff --git a/erpnext/accounts/doctype/dunning/dunning_dashboard.py b/erpnext/accounts/doctype/dunning/dunning_dashboard.py
deleted file mode 100644
index d1d4031..0000000
--- a/erpnext/accounts/doctype/dunning/dunning_dashboard.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from frappe import _
-
-
-def get_data():
-	return {
-		"fieldname": "dunning",
-		"non_standard_fieldnames": {
-			"Journal Entry": "reference_name",
-			"Payment Entry": "reference_name",
-		},
-		"transactions": [{"label": _("Payment"), "items": ["Payment Entry", "Journal Entry"]}],
-	}
diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py
index e1fd1e9..b29ace2 100644
--- a/erpnext/accounts/doctype/dunning/test_dunning.py
+++ b/erpnext/accounts/doctype/dunning/test_dunning.py
@@ -1,162 +1,197 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
-
-import unittest
-
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, nowdate, today
 
-from erpnext.accounts.doctype.dunning.dunning import calculate_interest_and_amount
+from erpnext import get_default_cost_center
 from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
 	unlink_payment_on_cancel_of_invoice,
 )
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
+	create_dunning as create_dunning_from_sales_invoice,
+)
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
 	create_sales_invoice_against_cost_center,
 )
 
+test_dependencies = ["Company", "Cost Center"]
 
-class TestDunning(unittest.TestCase):
+
+class TestDunning(FrappeTestCase):
 	@classmethod
-	def setUpClass(self):
-		create_dunning_type()
-		create_dunning_type_with_zero_interest_rate()
+	def setUpClass(cls):
+		super().setUpClass()
+		create_dunning_type("First Notice", fee=0.0, interest=0.0, is_default=1)
+		create_dunning_type("Second Notice", fee=10.0, interest=10.0, is_default=0)
 		unlink_payment_on_cancel_of_invoice()
 
 	@classmethod
-	def tearDownClass(self):
+	def tearDownClass(cls):
 		unlink_payment_on_cancel_of_invoice(0)
+		super().tearDownClass()
 
-	def test_dunning(self):
-		dunning = create_dunning()
-		amounts = calculate_interest_and_amount(
-			dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days
-		)
-		self.assertEqual(round(amounts.get("interest_amount"), 2), 0.44)
-		self.assertEqual(round(amounts.get("dunning_amount"), 2), 20.44)
-		self.assertEqual(round(amounts.get("grand_total"), 2), 120.44)
+	def test_dunning_without_fees(self):
+		dunning = create_dunning(overdue_days=20)
 
-	def test_dunning_with_zero_interest_rate(self):
-		dunning = create_dunning_with_zero_interest_rate()
-		amounts = calculate_interest_and_amount(
-			dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days
-		)
-		self.assertEqual(round(amounts.get("interest_amount"), 2), 0)
-		self.assertEqual(round(amounts.get("dunning_amount"), 2), 20)
-		self.assertEqual(round(amounts.get("grand_total"), 2), 120)
+		self.assertEqual(round(dunning.total_outstanding, 2), 100.00)
+		self.assertEqual(round(dunning.total_interest, 2), 0.00)
+		self.assertEqual(round(dunning.dunning_fee, 2), 0.00)
+		self.assertEqual(round(dunning.dunning_amount, 2), 0.00)
+		self.assertEqual(round(dunning.grand_total, 2), 100.00)
 
-	def test_gl_entries(self):
-		dunning = create_dunning()
-		dunning.submit()
-		gl_entries = frappe.db.sql(
-			"""select account, debit, credit
-			from `tabGL Entry` where voucher_type='Dunning' and voucher_no=%s
-			order by account asc""",
-			dunning.name,
-			as_dict=1,
-		)
-		self.assertTrue(gl_entries)
-		expected_values = dict(
-			(d[0], d) for d in [["Debtors - _TC", 20.44, 0.0], ["Sales - _TC", 0.0, 20.44]]
-		)
-		for gle in gl_entries:
-			self.assertEqual(expected_values[gle.account][0], gle.account)
-			self.assertEqual(expected_values[gle.account][1], gle.debit)
-			self.assertEqual(expected_values[gle.account][2], gle.credit)
+	def test_dunning_with_fees_and_interest(self):
+		dunning = create_dunning(overdue_days=15, dunning_type_name="Second Notice - _TC")
 
-	def test_payment_entry(self):
-		dunning = create_dunning()
+		self.assertEqual(round(dunning.total_outstanding, 2), 100.00)
+		self.assertEqual(round(dunning.total_interest, 2), 0.41)
+		self.assertEqual(round(dunning.dunning_fee, 2), 10.00)
+		self.assertEqual(round(dunning.dunning_amount, 2), 10.41)
+		self.assertEqual(round(dunning.grand_total, 2), 110.41)
+
+	def test_dunning_with_payment_entry(self):
+		dunning = create_dunning(overdue_days=15, dunning_type_name="Second Notice - _TC")
 		dunning.submit()
 		pe = get_payment_entry("Dunning", dunning.name)
 		pe.reference_no = "1"
 		pe.reference_date = nowdate()
-		pe.paid_from_account_currency = dunning.currency
-		pe.paid_to_account_currency = dunning.currency
-		pe.source_exchange_rate = 1
-		pe.target_exchange_rate = 1
 		pe.insert()
 		pe.submit()
-		si_doc = frappe.get_doc("Sales Invoice", dunning.sales_invoice)
-		self.assertEqual(si_doc.outstanding_amount, 0)
+
+		for overdue_payment in dunning.overdue_payments:
+			outstanding_amount = frappe.get_value(
+				"Sales Invoice", overdue_payment.sales_invoice, "outstanding_amount"
+			)
+			self.assertEqual(outstanding_amount, 0)
+
+		dunning.reload()
+		self.assertEqual(dunning.status, "Resolved")
+
+	def test_dunning_and_payment_against_partially_due_invoice(self):
+		"""
+		Create SI with first installment overdue. Check impact of Dunning and Payment Entry.
+		"""
+		create_payment_terms_template_for_dunning()
+		sales_invoice = create_sales_invoice_against_cost_center(
+			posting_date=add_days(today(), -1 * 6),
+			qty=1,
+			rate=100,
+			do_not_submit=True,
+		)
+		sales_invoice.payment_terms_template = "_Test 50-50 for Dunning"
+		sales_invoice.submit()
+		dunning = create_dunning_from_sales_invoice(sales_invoice.name)
+
+		self.assertEqual(len(dunning.overdue_payments), 1)
+		self.assertEqual(dunning.overdue_payments[0].payment_term, "_Test Payment Term 1 for Dunning")
+
+		dunning.submit()
+		pe = get_payment_entry("Dunning", dunning.name)
+		pe.reference_no, pe.reference_date = "2", nowdate()
+		pe.insert()
+		pe.submit()
+		sales_invoice.load_from_db()
+		dunning.load_from_db()
+
+		self.assertEqual(sales_invoice.status, "Partly Paid")
+		self.assertEqual(sales_invoice.payment_schedule[0].outstanding, 0)
+		self.assertEqual(dunning.status, "Resolved")
+
+		# Test impact on cancellation of PE
+		pe.cancel()
+		sales_invoice.reload()
+		dunning.reload()
+
+		self.assertEqual(sales_invoice.status, "Overdue")
+		self.assertEqual(dunning.status, "Unresolved")
 
 
-def create_dunning():
-	posting_date = add_days(today(), -20)
-	due_date = add_days(today(), -15)
+def create_dunning(overdue_days, dunning_type_name=None):
+	posting_date = add_days(today(), -1 * overdue_days)
 	sales_invoice = create_sales_invoice_against_cost_center(
-		posting_date=posting_date, due_date=due_date, status="Overdue"
+		posting_date=posting_date, qty=1, rate=100
 	)
-	dunning_type = frappe.get_doc("Dunning Type", "First Notice")
-	dunning = frappe.new_doc("Dunning")
-	dunning.sales_invoice = sales_invoice.name
-	dunning.customer_name = sales_invoice.customer_name
-	dunning.outstanding_amount = sales_invoice.outstanding_amount
-	dunning.debit_to = sales_invoice.debit_to
-	dunning.currency = sales_invoice.currency
-	dunning.company = sales_invoice.company
-	dunning.posting_date = nowdate()
-	dunning.due_date = sales_invoice.due_date
-	dunning.dunning_type = "First Notice"
-	dunning.rate_of_interest = dunning_type.rate_of_interest
-	dunning.dunning_fee = dunning_type.dunning_fee
-	dunning.save()
-	return dunning
+	dunning = create_dunning_from_sales_invoice(sales_invoice.name)
+
+	if dunning_type_name:
+		dunning_type = frappe.get_doc("Dunning Type", dunning_type_name)
+		dunning.dunning_type = dunning_type.name
+		dunning.rate_of_interest = dunning_type.rate_of_interest
+		dunning.dunning_fee = dunning_type.dunning_fee
+		dunning.income_account = dunning_type.income_account
+		dunning.cost_center = dunning_type.cost_center
+
+	return dunning.save()
 
 
-def create_dunning_with_zero_interest_rate():
-	posting_date = add_days(today(), -20)
-	due_date = add_days(today(), -15)
-	sales_invoice = create_sales_invoice_against_cost_center(
-		posting_date=posting_date, due_date=due_date, status="Overdue"
-	)
-	dunning_type = frappe.get_doc("Dunning Type", "First Notice with 0% Rate of Interest")
-	dunning = frappe.new_doc("Dunning")
-	dunning.sales_invoice = sales_invoice.name
-	dunning.customer_name = sales_invoice.customer_name
-	dunning.outstanding_amount = sales_invoice.outstanding_amount
-	dunning.debit_to = sales_invoice.debit_to
-	dunning.currency = sales_invoice.currency
-	dunning.company = sales_invoice.company
-	dunning.posting_date = nowdate()
-	dunning.due_date = sales_invoice.due_date
-	dunning.dunning_type = "First Notice with 0% Rate of Interest"
-	dunning.rate_of_interest = dunning_type.rate_of_interest
-	dunning.dunning_fee = dunning_type.dunning_fee
-	dunning.save()
-	return dunning
+def create_dunning_type(title, fee, interest, is_default):
+	company = "_Test Company"
+	if frappe.db.exists("Dunning Type", f"{title} - _TC"):
+		return
 
-
-def create_dunning_type():
 	dunning_type = frappe.new_doc("Dunning Type")
-	dunning_type.dunning_type = "First Notice"
-	dunning_type.start_day = 10
-	dunning_type.end_day = 20
-	dunning_type.dunning_fee = 20
-	dunning_type.rate_of_interest = 8
+	dunning_type.dunning_type = title
+	dunning_type.company = company
+	dunning_type.is_default = is_default
+	dunning_type.dunning_fee = fee
+	dunning_type.rate_of_interest = interest
+	dunning_type.income_account = get_income_account(company)
+	dunning_type.cost_center = get_default_cost_center(company)
 	dunning_type.append(
 		"dunning_letter_text",
 		{
 			"language": "en",
-			"body_text": "We have still not received payment for our invoice ",
+			"body_text": "We have still not received payment for our invoice",
 			"closing_text": "We kindly request that you pay the outstanding amount immediately, including interest and late fees.",
 		},
 	)
-	dunning_type.save()
+	dunning_type.insert()
 
 
-def create_dunning_type_with_zero_interest_rate():
-	dunning_type = frappe.new_doc("Dunning Type")
-	dunning_type.dunning_type = "First Notice with 0% Rate of Interest"
-	dunning_type.start_day = 10
-	dunning_type.end_day = 20
-	dunning_type.dunning_fee = 20
-	dunning_type.rate_of_interest = 0
-	dunning_type.append(
-		"dunning_letter_text",
-		{
-			"language": "en",
-			"body_text": "We have still not received payment for our invoice ",
-			"closing_text": "We kindly request that you pay the outstanding amount immediately, and late fees.",
-		},
+def get_income_account(company):
+	return (
+		frappe.get_value("Company", company, "default_income_account")
+		or frappe.get_all(
+			"Account",
+			filters={"is_group": 0, "company": company},
+			or_filters={
+				"report_type": "Profit and Loss",
+				"account_type": ("in", ("Income Account", "Temporary")),
+			},
+			limit=1,
+			pluck="name",
+		)[0]
 	)
-	dunning_type.save()
+
+
+def create_payment_terms_template_for_dunning():
+	from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_term
+
+	create_payment_term("_Test Payment Term 1 for Dunning")
+	create_payment_term("_Test Payment Term 2 for Dunning")
+
+	if not frappe.db.exists("Payment Terms Template", "_Test 50-50 for Dunning"):
+		frappe.get_doc(
+			{
+				"doctype": "Payment Terms Template",
+				"template_name": "_Test 50-50 for Dunning",
+				"allocate_payment_based_on_payment_terms": 1,
+				"terms": [
+					{
+						"doctype": "Payment Terms Template Detail",
+						"payment_term": "_Test Payment Term 1 for Dunning",
+						"invoice_portion": 50.00,
+						"credit_days_based_on": "Day(s) after invoice date",
+						"credit_days": 5,
+					},
+					{
+						"doctype": "Payment Terms Template Detail",
+						"payment_term": "_Test Payment Term 2 for Dunning",
+						"invoice_portion": 50.00,
+						"credit_days_based_on": "Day(s) after invoice date",
+						"credit_days": 10,
+					},
+				],
+			}
+		).insert()
diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.js b/erpnext/accounts/doctype/dunning_type/dunning_type.js
index 54156b4..b2c08c1 100644
--- a/erpnext/accounts/doctype/dunning_type/dunning_type.js
+++ b/erpnext/accounts/doctype/dunning_type/dunning_type.js
@@ -1,8 +1,24 @@
 // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-frappe.ui.form.on('Dunning Type', {
-	// refresh: function(frm) {
-
-	// }
+frappe.ui.form.on("Dunning Type", {
+	setup: function (frm) {
+		frm.set_query("income_account", () => {
+			return {
+				filters: {
+					root_type: "Income",
+					is_group: 0,
+					company: frm.doc.company,
+				},
+			};
+		});
+		frm.set_query("cost_center", () => {
+			return {
+				filters: {
+					is_group: 0,
+					company: frm.doc.company,
+				},
+			};
+		});
+	},
 });
diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.json b/erpnext/accounts/doctype/dunning_type/dunning_type.json
index da43664..5e39769 100644
--- a/erpnext/accounts/doctype/dunning_type/dunning_type.json
+++ b/erpnext/accounts/doctype/dunning_type/dunning_type.json
@@ -1,23 +1,26 @@
 {
  "actions": [],
  "allow_rename": 1,
- "autoname": "field:dunning_type",
+ "beta": 1,
  "creation": "2019-12-04 04:59:08.003664",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
   "dunning_type",
-  "overdue_interval_section",
-  "start_day",
-  "column_break_4",
-  "end_day",
+  "is_default",
+  "column_break_3",
+  "company",
   "section_break_6",
   "dunning_fee",
   "column_break_8",
   "rate_of_interest",
   "text_block_section",
-  "dunning_letter_text"
+  "dunning_letter_text",
+  "section_break_9",
+  "income_account",
+  "column_break_13",
+  "cost_center"
  ],
  "fields": [
   {
@@ -46,10 +49,6 @@
    "options": "Dunning Letter Text"
   },
   {
-   "fieldname": "column_break_4",
-   "fieldtype": "Column Break"
-  },
-  {
    "fieldname": "section_break_6",
    "fieldtype": "Section Break"
   },
@@ -58,32 +57,61 @@
    "fieldtype": "Column Break"
   },
   {
-   "fieldname": "overdue_interval_section",
-   "fieldtype": "Section Break",
-   "label": "Overdue Interval"
-  },
-  {
-   "fieldname": "start_day",
-   "fieldtype": "Int",
-   "label": "Start Day"
-  },
-  {
-   "fieldname": "end_day",
-   "fieldtype": "Int",
-   "label": "End Day"
-  },
-  {
    "fieldname": "rate_of_interest",
    "fieldtype": "Float",
    "in_list_view": 1,
    "label": "Rate of Interest (%) Yearly"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_default",
+   "fieldtype": "Check",
+   "label": "Is Default"
+  },
+  {
+   "fieldname": "section_break_9",
+   "fieldtype": "Section Break",
+   "label": "Accounting Details"
+  },
+  {
+   "fieldname": "income_account",
+   "fieldtype": "Link",
+   "label": "Income Account",
+   "options": "Account"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_13",
+   "fieldtype": "Column Break"
   }
  ],
- "links": [],
- "modified": "2020-07-15 17:14:17.835074",
+ "links": [
+  {
+   "link_doctype": "Dunning",
+   "link_fieldname": "dunning_type"
+  }
+ ],
+ "modified": "2021-11-13 00:25:35.659283",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Dunning Type",
+ "naming_rule": "By script",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.py b/erpnext/accounts/doctype/dunning_type/dunning_type.py
index 1b9bb9c..226e159 100644
--- a/erpnext/accounts/doctype/dunning_type/dunning_type.py
+++ b/erpnext/accounts/doctype/dunning_type/dunning_type.py
@@ -2,9 +2,11 @@
 # For license information, please see license.txt
 
 
-# import frappe
+import frappe
 from frappe.model.document import Document
 
 
 class DunningType(Document):
-	pass
+	def autoname(self):
+		company_abbr = frappe.get_value("Company", self.company, "abbr")
+		self.name = f"{self.dunning_type} - {company_abbr}"
diff --git a/erpnext/accounts/doctype/dunning_type/test_records.json b/erpnext/accounts/doctype/dunning_type/test_records.json
new file mode 100644
index 0000000..7f28aab
--- /dev/null
+++ b/erpnext/accounts/doctype/dunning_type/test_records.json
@@ -0,0 +1,36 @@
+[
+    {
+        "doctype": "Dunning Type",
+        "dunning_type": "_Test First Notice",
+        "company": "_Test Company",
+        "is_default": 1,
+        "dunning_fee": 0.0,
+        "rate_of_interest": 0.0,
+        "dunning_letter_text": [
+            {
+                "language": "en",
+                "body_text": "We have still not received payment for our invoice",
+                "closing_text": "We kindly request that you pay the outstanding amount immediately, including interest and late fees."
+            }
+        ],
+        "income_account": "Sales - _TC",
+        "cost_center": "_Test Cost Center - _TC"
+    },
+    {
+        "doctype": "Dunning Type",
+        "dunning_type": "_Test Second Notice",
+        "company": "_Test Company",
+        "is_default": 0,
+        "dunning_fee": 10.0,
+        "rate_of_interest": 10.0,
+        "dunning_letter_text": [
+            {
+                "language": "en",
+                "body_text": "We have still not received payment for our invoice",
+                "closing_text": "We kindly request that you pay the outstanding amount immediately, including interest and late fees."
+            }
+        ],
+        "income_account": "Sales - _TC",
+        "cost_center": "_Test Cost Center - _TC"
+    }
+]
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
index c52ea24..3b5698b 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -258,8 +258,8 @@
 					new_balance_in_base_currency = 0
 					new_balance_in_account_currency = 0
 
-					current_exchange_rate = calculate_exchange_rate_using_last_gle(
-						company, d.account, d.party_type, d.party
+					current_exchange_rate = (
+						calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party) or 0.0
 					)
 
 					gain_loss = new_balance_in_account_currency - (
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js
index bc77dac..508b2ea 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js
@@ -8,17 +8,6 @@
 				frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1));
 		}
 	},
-	refresh: function (frm) {
-		if (!frm.doc.__islocal && (frm.doc.name != frappe.sys_defaults.fiscal_year)) {
-			frm.add_custom_button(__("Set as Default"), () => frm.events.set_as_default(frm));
-			frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'"));
-		} else {
-			frm.set_intro("");
-		}
-	},
-	set_as_default: function(frm) {
-		return frm.call('set_as_default');
-	},
 	year_start_date: function(frm) {
 		if (!frm.doc.is_short_year) {
 			let year_end_date =
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
index 9d1b99b..0dfe569 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
@@ -4,28 +4,12 @@
 
 import frappe
 from dateutil.relativedelta import relativedelta
-from frappe import _, msgprint
+from frappe import _
 from frappe.model.document import Document
 from frappe.utils import add_days, add_years, cstr, getdate
 
 
 class FiscalYear(Document):
-	@frappe.whitelist()
-	def set_as_default(self):
-		frappe.db.set_single_value("Global Defaults", "current_fiscal_year", self.name)
-		global_defaults = frappe.get_doc("Global Defaults")
-		global_defaults.check_permission("write")
-		global_defaults.on_update()
-
-		# clear cache
-		frappe.clear_cache()
-
-		msgprint(
-			_(
-				"{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect."
-			).format(self.name)
-		)
-
 	def validate(self):
 		self.validate_dates()
 		self.validate_overlap()
@@ -68,13 +52,6 @@
 		frappe.cache().delete_value("fiscal_years")
 
 	def on_trash(self):
-		global_defaults = frappe.get_doc("Global Defaults")
-		if global_defaults.current_fiscal_year == self.name:
-			frappe.throw(
-				_(
-					"You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings"
-				).format(self.name)
-			)
 		frappe.cache().delete_value("fiscal_years")
 
 	def validate_overlap(self):
diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json
index b42d712..87f0ad1 100644
--- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json
+++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json
@@ -35,6 +35,7 @@
   {
    "fieldname": "company",
    "fieldtype": "Link",
+   "in_filter": 1,
    "in_list_view": 1,
    "label": "Company",
    "options": "Company",
@@ -56,7 +57,7 @@
   }
  ],
  "links": [],
- "modified": "2022-01-18 21:11:23.105589",
+ "modified": "2023-07-09 18:11:23.105589",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Item Tax Template",
@@ -102,4 +103,4 @@
  "states": [],
  "title_field": "title",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 83312db..ea4a2d4 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -408,6 +408,15 @@
 							d.idx, d.account
 						)
 					)
+				elif (
+					d.party_type
+					and frappe.db.get_value("Party Type", d.party_type, "account_type") != account_type
+				):
+					frappe.throw(
+						_("Row {0}: Account {1} and Party Type {2} have different account types").format(
+							d.idx, d.account, d.party_type
+						)
+					)
 
 	def check_credit_limit(self):
 		customers = list(
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py b/erpnext/accounts/doctype/overdue_payment/__init__.py
similarity index 100%
rename from erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py
rename to erpnext/accounts/doctype/overdue_payment/__init__.py
diff --git a/erpnext/accounts/doctype/overdue_payment/overdue_payment.json b/erpnext/accounts/doctype/overdue_payment/overdue_payment.json
new file mode 100644
index 0000000..99e1646
--- /dev/null
+++ b/erpnext/accounts/doctype/overdue_payment/overdue_payment.json
@@ -0,0 +1,170 @@
+{
+ "actions": [],
+ "creation": "2021-09-15 18:34:27.172906",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "sales_invoice",
+  "payment_schedule",
+  "dunning_level",
+  "payment_term",
+  "section_break_15",
+  "description",
+  "section_break_4",
+  "due_date",
+  "overdue_days",
+  "mode_of_payment",
+  "column_break_5",
+  "invoice_portion",
+  "section_break_16",
+  "payment_amount",
+  "outstanding",
+  "paid_amount",
+  "discounted_amount",
+  "interest"
+ ],
+ "fields": [
+  {
+   "columns": 2,
+   "fieldname": "payment_term",
+   "fieldtype": "Link",
+   "label": "Payment Term",
+   "options": "Payment Term",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "section_break_15",
+   "fieldtype": "Section Break",
+   "label": "Description"
+  },
+  {
+   "columns": 2,
+   "fetch_from": "payment_term.description",
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "label": "Description",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break"
+  },
+  {
+   "columns": 2,
+   "fieldname": "due_date",
+   "fieldtype": "Date",
+   "label": "Due Date",
+   "read_only": 1
+  },
+  {
+   "fieldname": "mode_of_payment",
+   "fieldtype": "Link",
+   "label": "Mode of Payment",
+   "options": "Mode of Payment",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_5",
+   "fieldtype": "Column Break"
+  },
+  {
+   "columns": 2,
+   "fieldname": "invoice_portion",
+   "fieldtype": "Percent",
+   "label": "Invoice Portion",
+   "read_only": 1
+  },
+  {
+   "columns": 2,
+   "fieldname": "payment_amount",
+   "fieldtype": "Currency",
+   "label": "Payment Amount",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "payment_amount",
+   "fieldname": "outstanding",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Outstanding",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "depends_on": "paid_amount",
+   "fieldname": "paid_amount",
+   "fieldtype": "Currency",
+   "label": "Paid Amount",
+   "options": "currency"
+  },
+  {
+   "default": "0",
+   "depends_on": "discounted_amount",
+   "fieldname": "discounted_amount",
+   "fieldtype": "Currency",
+   "label": "Discounted Amount",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "sales_invoice",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Sales Invoice",
+   "options": "Sales Invoice",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "payment_schedule",
+   "fieldtype": "Data",
+   "label": "Payment Schedule",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "overdue_days",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Overdue Days",
+   "read_only": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "dunning_level",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Dunning Level",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_16",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "interest",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Interest",
+   "options": "currency",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-23 13:48:27.898830",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Overdue Payment",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/overdue_payment/overdue_payment.py b/erpnext/accounts/doctype/overdue_payment/overdue_payment.py
new file mode 100644
index 0000000..6a543ad
--- /dev/null
+++ b/erpnext/accounts/doctype/overdue_payment/overdue_payment.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class OverduePayment(Document):
+	pass
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 65ed466..7542bab 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -226,10 +226,12 @@
 		latest_lookup = {}
 		for d in latest_references:
 			d = frappe._dict(d)
-			latest_lookup.update({(d.voucher_type, d.voucher_no): d})
+			latest_lookup.setdefault((d.voucher_type, d.voucher_no), frappe._dict())[d.payment_term] = d
 
 		for d in self.get("references"):
-			latest = latest_lookup.get((d.reference_doctype, d.reference_name))
+			latest = (latest_lookup.get((d.reference_doctype, d.reference_name)) or frappe._dict()).get(
+				d.payment_term
+			)
 
 			# The reference has already been fully paid
 			if not latest:
@@ -237,10 +239,9 @@
 					_("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name)
 				)
 			# The reference has already been partly paid
-			elif (
-				latest.outstanding_amount < latest.invoice_amount
-				and flt(d.outstanding_amount, d.precision("outstanding_amount")) != latest.outstanding_amount
-			):
+			elif latest.outstanding_amount < latest.invoice_amount and flt(
+				d.outstanding_amount, d.precision("outstanding_amount")
+			) != flt(latest.outstanding_amount, d.precision("outstanding_amount")):
 				frappe.throw(
 					_(
 						"{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts."
@@ -252,6 +253,18 @@
 			if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount):
 				frappe.throw(fail_message.format(d.idx))
 
+			if d.payment_term and (
+				(flt(d.allocated_amount)) > 0
+				and flt(d.allocated_amount) > flt(latest.payment_term_outstanding)
+			):
+				frappe.throw(
+					_(
+						"Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3}"
+					).format(
+						d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term
+					)
+				)
+
 			# Check for negative outstanding invoices as well
 			if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount):
 				frappe.throw(fail_message.format(d.idx))
@@ -1433,6 +1446,9 @@
 	if args.get("party_type") == "Member":
 		return
 
+	if not args.get("get_outstanding_invoices") and not args.get("get_orders_to_be_billed"):
+		args["get_outstanding_invoices"] = True
+
 	ple = qb.DocType("Payment Ledger Entry")
 	common_filter = []
 	accounting_dimensions_filter = []
@@ -1498,7 +1514,9 @@
 			accounting_dimensions=accounting_dimensions_filter,
 		)
 
-		outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
+		outstanding_invoices = split_invoices_based_on_payment_terms(
+			outstanding_invoices, args.get("company")
+		)
 
 		for d in outstanding_invoices:
 			d["exchange_rate"] = 1
@@ -1558,8 +1576,27 @@
 	return data
 
 
-def split_invoices_based_on_payment_terms(outstanding_invoices):
+def split_invoices_based_on_payment_terms(outstanding_invoices, company):
 	invoice_ref_based_on_payment_terms = {}
+
+	company_currency = (
+		frappe.db.get_value("Company", company, "default_currency") if company else None
+	)
+	exc_rates = frappe._dict()
+	for doctype in ["Sales Invoice", "Purchase Invoice"]:
+		invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype]
+		for x in frappe.db.get_all(
+			doctype,
+			filters={"name": ["in", invoices]},
+			fields=["name", "currency", "conversion_rate", "party_account_currency"],
+		):
+			exc_rates[x.name] = frappe._dict(
+				conversion_rate=x.conversion_rate,
+				currency=x.currency,
+				party_account_currency=x.party_account_currency,
+				company_currency=company_currency,
+			)
+
 	for idx, d in enumerate(outstanding_invoices):
 		if d.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
 			payment_term_template = frappe.db.get_value(
@@ -1576,6 +1613,14 @@
 
 					for payment_term in payment_schedule:
 						if payment_term.outstanding > 0.1:
+							doc_details = exc_rates.get(payment_term.parent, None)
+							is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and (
+								doc_details.party_account_currency != doc_details.company_currency
+							)
+							payment_term_outstanding = flt(payment_term.outstanding)
+							if not is_multi_currency_acc:
+								payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding)
+
 							invoice_ref_based_on_payment_terms.setdefault(idx, [])
 							invoice_ref_based_on_payment_terms[idx].append(
 								frappe._dict(
@@ -1587,6 +1632,7 @@
 										"posting_date": d.posting_date,
 										"invoice_amount": flt(d.invoice_amount),
 										"outstanding_amount": flt(d.outstanding_amount),
+										"payment_term_outstanding": payment_term_outstanding,
 										"payment_amount": payment_term.payment_amount,
 										"payment_term": payment_term.payment_term,
 										"account": d.account,
@@ -1627,60 +1673,59 @@
 	cost_center=None,
 	filters=None,
 ):
+	voucher_type = None
 	if party_type == "Customer":
 		voucher_type = "Sales Order"
 	elif party_type == "Supplier":
 		voucher_type = "Purchase Order"
-	elif party_type == "Employee":
-		voucher_type = None
+
+	if not voucher_type:
+		return []
 
 	# Add cost center condition
-	if voucher_type:
-		doc = frappe.get_doc({"doctype": voucher_type})
-		condition = ""
-		if doc and hasattr(doc, "cost_center") and doc.cost_center:
-			condition = " and cost_center='%s'" % cost_center
+	doc = frappe.get_doc({"doctype": voucher_type})
+	condition = ""
+	if doc and hasattr(doc, "cost_center") and doc.cost_center:
+		condition = " and cost_center='%s'" % cost_center
 
-	orders = []
-	if voucher_type:
-		if party_account_currency == company_currency:
-			grand_total_field = "base_grand_total"
-			rounded_total_field = "base_rounded_total"
-		else:
-			grand_total_field = "grand_total"
-			rounded_total_field = "rounded_total"
+	if party_account_currency == company_currency:
+		grand_total_field = "base_grand_total"
+		rounded_total_field = "base_rounded_total"
+	else:
+		grand_total_field = "grand_total"
+		rounded_total_field = "rounded_total"
 
-		orders = frappe.db.sql(
-			"""
-			select
-				name as voucher_no,
-				if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
-				(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
-				transaction_date as posting_date
-			from
-				`tab{voucher_type}`
-			where
-				{party_type} = %s
-				and docstatus = 1
-				and company = %s
-				and ifnull(status, "") != "Closed"
-				and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
-				and abs(100 - per_billed) > 0.01
-				{condition}
-			order by
-				transaction_date, name
-		""".format(
-				**{
-					"rounded_total_field": rounded_total_field,
-					"grand_total_field": grand_total_field,
-					"voucher_type": voucher_type,
-					"party_type": scrub(party_type),
-					"condition": condition,
-				}
-			),
-			(party, company),
-			as_dict=True,
-		)
+	orders = frappe.db.sql(
+		"""
+		select
+			name as voucher_no,
+			if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
+			(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
+			transaction_date as posting_date
+		from
+			`tab{voucher_type}`
+		where
+			{party_type} = %s
+			and docstatus = 1
+			and company = %s
+			and ifnull(status, "") != "Closed"
+			and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
+			and abs(100 - per_billed) > 0.01
+			{condition}
+		order by
+			transaction_date, name
+	""".format(
+			**{
+				"rounded_total_field": rounded_total_field,
+				"grand_total_field": grand_total_field,
+				"voucher_type": voucher_type,
+				"party_type": scrub(party_type),
+				"condition": condition,
+			}
+		),
+		(party, company),
+		as_dict=True,
+	)
 
 	order_list = []
 	for d in orders:
@@ -1713,6 +1758,8 @@
 	cost_center=None,
 	condition=None,
 ):
+	if party_type not in ["Customer", "Supplier"]:
+		return []
 	voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
 	account = "debit_to" if voucher_type == "Sales Invoice" else "credit_to"
 	supplier_condition = ""
@@ -2007,28 +2054,27 @@
 				pe.append("references", reference)
 		else:
 			if dt == "Dunning":
+				for overdue_payment in doc.overdue_payments:
+					pe.append(
+						"references",
+						{
+							"reference_doctype": "Sales Invoice",
+							"reference_name": overdue_payment.sales_invoice,
+							"payment_term": overdue_payment.payment_term,
+							"due_date": overdue_payment.due_date,
+							"total_amount": overdue_payment.outstanding,
+							"outstanding_amount": overdue_payment.outstanding,
+							"allocated_amount": overdue_payment.outstanding,
+						},
+					)
+
 				pe.append(
-					"references",
+					"deductions",
 					{
-						"reference_doctype": "Sales Invoice",
-						"reference_name": doc.get("sales_invoice"),
-						"bill_no": doc.get("bill_no"),
-						"due_date": doc.get("due_date"),
-						"total_amount": doc.get("outstanding_amount"),
-						"outstanding_amount": doc.get("outstanding_amount"),
-						"allocated_amount": doc.get("outstanding_amount"),
-					},
-				)
-				pe.append(
-					"references",
-					{
-						"reference_doctype": dt,
-						"reference_name": dn,
-						"bill_no": doc.get("bill_no"),
-						"due_date": doc.get("due_date"),
-						"total_amount": doc.get("dunning_amount"),
-						"outstanding_amount": doc.get("dunning_amount"),
-						"allocated_amount": doc.get("dunning_amount"),
+						"account": doc.income_account,
+						"cost_center": doc.cost_center,
+						"amount": -1 * doc.dunning_amount,
+						"description": _("Interest and/or dunning fee"),
 					},
 				)
 			else:
@@ -2122,8 +2168,10 @@
 
 def set_payment_type(dt, doc):
 	if (
-		dt == "Sales Order" or (dt in ("Sales Invoice", "Dunning") and doc.outstanding_amount > 0)
-	) or (dt == "Purchase Invoice" and doc.outstanding_amount < 0):
+		(dt == "Sales Order" or (dt == "Sales Invoice" and doc.outstanding_amount > 0))
+		or (dt == "Purchase Invoice" and doc.outstanding_amount < 0)
+		or dt == "Dunning"
+	):
 		payment_type = "Receive"
 	else:
 		payment_type = "Pay"
@@ -2368,6 +2416,7 @@
 					"due_date": doc.get("due_date"),
 					"total_amount": grand_total,
 					"outstanding_amount": outstanding_amount,
+					"payment_term_outstanding": payment_term_outstanding,
 					"payment_term": payment_term.payment_term,
 					"allocated_amount": payment_term_outstanding,
 				}
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 70cc4b3..c6e93f3 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -1061,6 +1061,101 @@
 		}
 		self.assertDictEqual(ref_details, expected_response)
 
+	@change_settings(
+		"Accounts Settings",
+		{
+			"unlink_payment_on_cancellation_of_invoice": 1,
+			"delete_linked_ledger_entries": 1,
+			"allow_multi_currency_invoices_against_single_party_account": 1,
+		},
+	)
+	def test_overallocation_validation_on_payment_terms(self):
+		"""
+		Validate Allocation on Payment Entry based on Payment Schedule. Upon overallocation, validation error must be thrown.
+
+		"""
+		customer = create_customer()
+		create_payment_terms_template()
+
+		# Validate allocation on base/company currency
+		si1 = create_sales_invoice(do_not_save=1, qty=1, rate=200)
+		si1.payment_terms_template = "Test Receivable Template"
+		si1.save().submit()
+
+		si1.reload()
+		pe = get_payment_entry(si1.doctype, si1.name).save()
+		# Allocated amount should be according to the payment schedule
+		for idx, schedule in enumerate(si1.payment_schedule):
+			with self.subTest(idx=idx):
+				self.assertEqual(flt(schedule.payment_amount), flt(pe.references[idx].allocated_amount))
+		pe.save()
+
+		# Overallocation validation should trigger
+		pe.paid_amount = 400
+		pe.references[0].allocated_amount = 200
+		pe.references[1].allocated_amount = 200
+		self.assertRaises(frappe.ValidationError, pe.save)
+		pe.delete()
+		si1.cancel()
+		si1.delete()
+
+		# Validate allocation on foreign currency
+		si2 = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=80,
+			do_not_save=1,
+		)
+		si2.payment_terms_template = "Test Receivable Template"
+		si2.save().submit()
+
+		si2.reload()
+		pe = get_payment_entry(si2.doctype, si2.name).save()
+		# Allocated amount should be according to the payment schedule
+		for idx, schedule in enumerate(si2.payment_schedule):
+			with self.subTest(idx=idx):
+				self.assertEqual(flt(schedule.payment_amount), flt(pe.references[idx].allocated_amount))
+		pe.save()
+
+		# Overallocation validation should trigger
+		pe.paid_amount = 200
+		pe.references[0].allocated_amount = 100
+		pe.references[1].allocated_amount = 100
+		self.assertRaises(frappe.ValidationError, pe.save)
+		pe.delete()
+		si2.cancel()
+		si2.delete()
+
+		# Validate allocation in base/company currency on a foreign currency document
+		# when invoice is made is foreign currency, but posted to base/company currency debtors account
+		si3 = create_sales_invoice(
+			customer=customer,
+			currency="USD",
+			conversion_rate=80,
+			do_not_save=1,
+		)
+		si3.payment_terms_template = "Test Receivable Template"
+		si3.save().submit()
+
+		si3.reload()
+		pe = get_payment_entry(si3.doctype, si3.name).save()
+		# Allocated amount should be equal to payment term outstanding
+		self.assertEqual(len(pe.references), 2)
+		for idx, ref in enumerate(pe.references):
+			with self.subTest(idx=idx):
+				self.assertEqual(ref.payment_term_outstanding, ref.allocated_amount)
+		pe.save()
+
+		# Overallocation validation should trigger
+		pe.paid_amount = 16000
+		pe.references[0].allocated_amount = 8000
+		pe.references[1].allocated_amount = 8000
+		self.assertRaises(frappe.ValidationError, pe.save)
+		pe.delete()
+		si3.cancel()
+		si3.delete()
+
 
 def create_payment_entry(**args):
 	payment_entry = frappe.new_doc("Payment Entry")
@@ -1150,3 +1245,17 @@
 def create_payment_term(name):
 	if not frappe.db.exists("Payment Term", name):
 		frappe.get_doc({"doctype": "Payment Term", "payment_term_name": name}).insert()
+
+
+def create_customer(name="_Test Customer 2 USD", currency="USD"):
+	customer = None
+	if frappe.db.exists("Customer", name):
+		customer = name
+	else:
+		customer = frappe.new_doc("Customer")
+		customer.customer_name = name
+		customer.default_currency = currency
+		customer.type = "Individual"
+		customer.save()
+		customer = customer.name
+	return customer
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
index 641f452..4947248 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -126,21 +126,22 @@
 	def make_gl_entries(self, get_opening_entries=False):
 		gl_entries = self.get_gl_entries()
 		closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries)
-		if gl_entries:
-			if len(gl_entries) > 5000:
-				frappe.enqueue(
-					process_gl_entries,
-					gl_entries=gl_entries,
-					closing_entries=closing_entries,
-					voucher_name=self.name,
-					queue="long",
-				)
-				frappe.msgprint(
-					_("The GL Entries will be processed in the background, it can take a few minutes."),
-					alert=True,
-				)
-			else:
-				process_gl_entries(gl_entries, closing_entries, voucher_name=self.name)
+		if len(gl_entries) > 5000:
+			frappe.enqueue(
+				process_gl_entries,
+				gl_entries=gl_entries,
+				closing_entries=closing_entries,
+				voucher_name=self.name,
+				company=self.company,
+				closing_date=self.posting_date,
+				queue="long",
+			)
+			frappe.msgprint(
+				_("The GL Entries will be processed in the background, it can take a few minutes."),
+				alert=True,
+			)
+		else:
+			process_gl_entries(gl_entries, closing_entries, self.name, self.company, self.posting_date)
 
 	def get_grouped_gl_entries(self, get_opening_entries=False):
 		closing_entries = []
@@ -321,24 +322,22 @@
 		return query.run(as_dict=1)
 
 
-def process_gl_entries(gl_entries, closing_entries, voucher_name=None):
+def process_gl_entries(gl_entries, closing_entries, voucher_name, company, closing_date):
 	from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
 		make_closing_entries,
 	)
 	from erpnext.accounts.general_ledger import make_gl_entries
 
 	try:
-		make_gl_entries(gl_entries, merge_entries=False)
-		make_closing_entries(gl_entries + closing_entries, voucher_name=voucher_name)
-		frappe.db.set_value(
-			"Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Completed"
-		)
+		if gl_entries:
+			make_gl_entries(gl_entries, merge_entries=False)
+
+		make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date)
+		frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Completed")
 	except Exception as e:
 		frappe.db.rollback()
 		frappe.log_error(e)
-		frappe.db.set_value(
-			"Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Failed"
-		)
+		frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Failed")
 
 
 def make_reverse_gl_entries(voucher_type, voucher_no):
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 8753ebc..4ec103c 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -142,9 +142,15 @@
 					cur_frm.events.create_invoice_discounting(cur_frm);
 				}, __('Create'));
 
-				if (doc.due_date < frappe.datetime.get_today()) {
-					cur_frm.add_custom_button(__('Dunning'), function() {
-						cur_frm.events.create_dunning(cur_frm);
+				const payment_is_overdue = doc.payment_schedule.map(
+					row => Date.parse(row.due_date) < Date.now()
+				).reduce(
+					(prev, current) => prev || current
+				);
+
+				if (payment_is_overdue) {
+					this.frm.add_custom_button(__('Dunning'), () => {
+						this.frm.events.create_dunning(this.frm);
 					}, __('Create'));
 				}
 			}
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 7ab1c89..b3212b5 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -2516,55 +2516,49 @@
 
 
 @frappe.whitelist()
-def create_dunning(source_name, target_doc=None):
+def create_dunning(source_name, target_doc=None, ignore_permissions=False):
 	from frappe.model.mapper import get_mapped_doc
 
-	from erpnext.accounts.doctype.dunning.dunning import (
-		calculate_interest_and_amount,
-		get_dunning_letter_text,
-	)
+	def postprocess_dunning(source, target):
+		from erpnext.accounts.doctype.dunning.dunning import get_dunning_letter_text
 
-	def set_missing_values(source, target):
-		target.sales_invoice = source_name
-		target.outstanding_amount = source.outstanding_amount
-		overdue_days = (getdate(target.posting_date) - getdate(source.due_date)).days
-		target.overdue_days = overdue_days
-		if frappe.db.exists(
-			"Dunning Type", {"start_day": ["<", overdue_days], "end_day": [">=", overdue_days]}
-		):
-			dunning_type = frappe.get_doc(
-				"Dunning Type", {"start_day": ["<", overdue_days], "end_day": [">=", overdue_days]}
-			)
+		dunning_type = frappe.db.exists("Dunning Type", {"is_default": 1, "company": source.company})
+		if dunning_type:
+			dunning_type = frappe.get_doc("Dunning Type", dunning_type)
 			target.dunning_type = dunning_type.name
 			target.rate_of_interest = dunning_type.rate_of_interest
 			target.dunning_fee = dunning_type.dunning_fee
-			letter_text = get_dunning_letter_text(dunning_type=dunning_type.name, doc=target.as_dict())
+			target.income_account = dunning_type.income_account
+			target.cost_center = dunning_type.cost_center
+			letter_text = get_dunning_letter_text(
+				dunning_type=dunning_type.name, doc=target.as_dict(), language=source.language
+			)
+
 			if letter_text:
 				target.body_text = letter_text.get("body_text")
 				target.closing_text = letter_text.get("closing_text")
 				target.language = letter_text.get("language")
-			amounts = calculate_interest_and_amount(
-				target.outstanding_amount,
-				target.rate_of_interest,
-				target.dunning_fee,
-				target.overdue_days,
-			)
-			target.interest_amount = amounts.get("interest_amount")
-			target.dunning_amount = amounts.get("dunning_amount")
-			target.grand_total = amounts.get("grand_total")
 
-	doclist = get_mapped_doc(
-		"Sales Invoice",
-		source_name,
-		{
+		target.validate()
+
+	return get_mapped_doc(
+		from_doctype="Sales Invoice",
+		from_docname=source_name,
+		target_doc=target_doc,
+		table_maps={
 			"Sales Invoice": {
 				"doctype": "Dunning",
-			}
+				"field_map": {"customer_address": "customer_address", "parent": "sales_invoice"},
+			},
+			"Payment Schedule": {
+				"doctype": "Overdue Payment",
+				"field_map": {"name": "payment_schedule", "parent": "sales_invoice"},
+				"condition": lambda doc: doc.outstanding > 0 and getdate(doc.due_date) < getdate(),
+			},
 		},
-		target_doc,
-		set_missing_values,
+		postprocess=postprocess_dunning,
+		ignore_permissions=ignore_permissions,
 	)
-	return doclist
 
 
 def check_if_return_invoice_linked_with_payment_entry(self):
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.js b/erpnext/accounts/doctype/shareholder/shareholder.js
index c6f101e..544d417 100644
--- a/erpnext/accounts/doctype/shareholder/shareholder.js
+++ b/erpnext/accounts/doctype/shareholder/shareholder.js
@@ -3,8 +3,6 @@
 
 frappe.ui.form.on('Shareholder', {
 	refresh: function(frm) {
-		frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Shareholder' };
-
 		frm.toggle_display(['contact_html'], !frm.doc.__islocal);
 
 		if (frm.doc.__islocal) {
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index f1dad87..e9dc5fc 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -13,14 +13,11 @@
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_accounting_dimensions,
 )
+from erpnext.accounts.doctype.accounting_period.accounting_period import ClosedAccountingPeriod
 from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
 from erpnext.accounts.utils import create_payment_ledger_entry
 
 
-class ClosedAccountingPeriod(frappe.ValidationError):
-	pass
-
-
 def make_gl_entries(
 	gl_map,
 	cancel=False,
diff --git a/erpnext/accounts/print_format/dunning_letter/dunning_letter.json b/erpnext/accounts/print_format/dunning_letter/dunning_letter.json
index a7eac70..c48e1cf 100644
--- a/erpnext/accounts/print_format/dunning_letter/dunning_letter.json
+++ b/erpnext/accounts/print_format/dunning_letter/dunning_letter.json
@@ -1,4 +1,5 @@
 {
+ "absolute_value": 0,
  "align_labels_right": 0,
  "creation": "2019-12-11 04:37:14.012805",
  "css": ".print-format th {\n    background-color: transparent !important;\n    border-bottom: 1px solid !important;\n    border-top: none !important;\n}\n.print-format .ql-editor {\n    padding-left: 0px;\n    padding-right: 0px;\n}\n\n.print-format table {\n    margin-bottom: 0px;\n    }\n.print-format .table-data tr:last-child { \n    border-bottom: 1px solid !important;\n}\n\n.print-format .table-inner tr:last-child {\n    border-bottom:none !important;\n}\n.print-format .table-inner {\n    margin: 0px 0px;\n}\n\n.print-format .table-data ul li { \n    color:#787878 !important;\n}\n\n.no-top-border {\n    border-top:none !important;\n}\n\n.table-inner td {\n    padding-left: 0px !important;    \n    padding-top: 1px !important;\n    padding-bottom: 1px !important;\n    color:#787878 !important;\n}\n\n.total {\n    background-color: lightgrey !important;\n    padding-top: 4px !important;\n    padding-bottom: 4px !important;\n}\n",
@@ -9,10 +10,10 @@
  "docstatus": 0,
  "doctype": "Print Format",
  "font": "Arial",
- "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"<div></div>\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"<b>{{doc.customer_name}}</b> <br />\\n{{doc.address_display}}\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"<div style=\\\"text-align:left;\\\">\\n<div style=\\\"font-size:24px; text-transform:uppercase;\\\">{{_(doc.dunning_type)}}</div>\\n<div style=\\\"font-size:16px;padding-bottom:5px;\\\">{{ doc.name }}</div>\\n</div>\"}, {\"fieldname\": \"posting_date\", \"print_hide\": 0, \"label\": \"Date\"}, {\"fieldname\": \"sales_invoice\", \"print_hide\": 0, \"label\": \"Sales Invoice\"}, {\"fieldname\": \"due_date\", \"print_hide\": 0, \"label\": \"Due Date\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"body_text\", \"print_hide\": 0, \"label\": \"Body Text\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"<table class=\\\"table table-borderless table-data\\\">\\n   <tbody>\\n        <tr>\\n            <th>{{_(\\\"Description\\\")}}</th>\\n\\t        <th style=\\\"text-align: right;\\\">{{_(\\\"Amount\\\")}}</th>\\n        </tr>\\n        <tr>\\n            <td>\\n                {{_(\\\"Outstanding Amount\\\")}}\\n             </td>\\n            <td style=\\\"text-align: right;\\\">\\n                {{doc.get_formatted(\\\"outstanding_amount\\\")}}\\n            </td>\\n        </tr>\\n        {%if doc.rate_of_interest > 0%}\\n        <tr>\\n            <td>\\n                {{_(\\\"Interest \\\")}} {{doc.rate_of_interest}}% p.a. ({{doc.overdue_days}} {{_(\\\"days\\\")}})\\n             </td>\\n            <td style=\\\"text-align: right;\\\">\\n                {{doc.get_formatted(\\\"interest_amount\\\")}}\\n            </td>\\n        </tr>\\n        {% endif %}\\n        {%if doc.dunning_fee > 0%}\\n        <tr>\\n            <td>\\n                {{_(\\\"Dunning Fee\\\")}}\\n             </td>\\n            <td style=\\\"text-align: right;\\\">\\n                {{doc.get_formatted(\\\"dunning_fee\\\")}}\\n            </td>\\n        </tr>\\n        {% endif %}\\n    </tbody>\\n</table>\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n<div class=\\\"row total\\\" style =\\\"margin-right: 0px;\\\">\\n\\t\\t<div class=\\\"col-xs-5\\\">\\n\\t\\t\\t<b>{{_(\\\"Grand Total\\\")}}</b></div>\\n\\t\\t<div class=\\\"col-xs-7 text-right\\\" style=\\\"padding-right: 4px;\\\">\\n\\t\\t\\t<b>{{doc.get_formatted(\\\"grand_total\\\")}}</b>\\n\\t\\t</div>\\n</div>\\n\\n\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"closing_text\", \"print_hide\": 0, \"label\": \"Closing Text\"}]",
+ "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"<div></div>\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"<b>{{doc.customer_name}}</b> <br />\\n{{doc.address_display}}\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"<div style=\\\"text-align:left;\\\">\\n<div style=\\\"font-size:24px; text-transform:uppercase;\\\">{{_(doc.dunning_type)}}</div>\\n<div style=\\\"font-size:16px;padding-bottom:5px;\\\">{{ doc.name }}</div>\\n</div>\"}, {\"fieldname\": \"posting_date\", \"print_hide\": 0, \"label\": \"Date\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"body_text\", \"print_hide\": 0, \"label\": \"Body Text\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"overdue_payments\", \"print_hide\": 0, \"label\": \"Overdue Payments\", \"visible_columns\": [{\"fieldname\": \"sales_invoice\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"dunning_level\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"due_date\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"overdue_days\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"invoice_portion\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"outstanding\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"interest\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"total_outstanding\", \"print_hide\": 0, \"label\": \"Total Outstanding\"}, {\"fieldname\": \"dunning_fee\", \"print_hide\": 0, \"label\": \"Dunning Fee\"}, {\"fieldname\": \"total_interest\", \"print_hide\": 0, \"label\": \"Total Interest\"}, {\"fieldname\": \"grand_total\", \"print_hide\": 0, \"label\": \"Grand Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"closing_text\", \"print_hide\": 0, \"label\": \"Closing Text\"}]",
  "idx": 0,
  "line_breaks": 0,
- "modified": "2020-07-14 18:25:44.348207",
+ "modified": "2021-09-30 10:22:02.603871",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Dunning Letter",
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
index dd965a9..d58fd95 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
@@ -49,7 +49,7 @@
 				"label": __("Start Year"),
 				"fieldtype": "Link",
 				"options": "Fiscal Year",
-				"default": frappe.defaults.get_user_default("fiscal_year"),
+				"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 				"reqd": 1,
 				on_change: () => {
 					frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), function(r) {
@@ -65,7 +65,7 @@
 				"label": __("End Year"),
 				"fieldtype": "Link",
 				"options": "Fiscal Year",
-				"default": frappe.defaults.get_user_default("fiscal_year"),
+				"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 				"reqd": 1,
 				on_change: () => {
 					frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), function(r) {
@@ -139,7 +139,7 @@
 			return value;
 		},
 		onload: function() {
-			let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
+			let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
 
 			frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
 				var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js
index 0056b9e..96e0c84 100644
--- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js
+++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js
@@ -48,7 +48,7 @@
 			"label": __("Start Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"reqd": 1
 		},
 		{
@@ -56,7 +56,7 @@
 			"label": __("End Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"reqd": 1
 		},
 		{
@@ -100,7 +100,7 @@
 		return default_formatter(value, row, column, data);
 	},
 	onload: function(report){
-		let fiscal_year = frappe.defaults.get_user_default("fiscal_year");
+		let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
 
 		frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
 			var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py
index 3e11643..cad5325 100644
--- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py
+++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py
@@ -4,9 +4,10 @@
 import frappe
 from frappe import _, qb
 from frappe.query_builder import Column, functions
-from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, rounded
+from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, getdate, rounded
 
 from erpnext.accounts.report.financial_statements import get_period_list
+from erpnext.accounts.utils import get_fiscal_year
 
 
 class Deferred_Item(object):
@@ -226,7 +227,7 @@
 
 		# If no filters are provided, get user defaults
 		if not filters:
-			fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
+			fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date=getdate()))
 			self.filters = frappe._dict(
 				{
 					"company": frappe.defaults.get_user_default("Company"),
diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
index 023ff22..c84b843 100644
--- a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
+++ b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
@@ -10,6 +10,7 @@
 from erpnext.accounts.report.deferred_revenue_and_expense.deferred_revenue_and_expense import (
 	Deferred_Revenue_and_Expense_Report,
 )
+from erpnext.accounts.utils import get_fiscal_year
 from erpnext.buying.doctype.supplier.test_supplier import create_supplier
 from erpnext.stock.doctype.item.test_item import create_item
 
@@ -116,7 +117,7 @@
 		pda.submit()
 
 		# execute report
-		fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
+		fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
 		self.filters = frappe._dict(
 			{
 				"company": frappe.defaults.get_user_default("Company"),
@@ -209,7 +210,7 @@
 		pda.submit()
 
 		# execute report
-		fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
+		fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
 		self.filters = frappe._dict(
 			{
 				"company": frappe.defaults.get_user_default("Company"),
@@ -297,7 +298,7 @@
 		pda.submit()
 
 		# execute report
-		fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
+		fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
 		self.filters = frappe._dict(
 			{
 				"company": frappe.defaults.get_user_default("Company"),
diff --git a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js
index ea05a35..9d416db 100644
--- a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js
+++ b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js
@@ -18,7 +18,7 @@
 				"label": __("Fiscal Year"),
 				"fieldtype": "Link",
 				"options": "Fiscal Year",
-				"default": frappe.defaults.get_user_default("fiscal_year"),
+				"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 				"reqd": 1,
 				"on_change": function(query_report) {
 					var fiscal_year = query_report.get_values().fiscal_year;
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index f3a892b..db9609d 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -416,6 +416,7 @@
 	filters,
 	gl_entries_by_account,
 	ignore_closing_entries=False,
+	ignore_opening_entries=False,
 ):
 	"""Returns a dict like { "account": [gl entries], ... }"""
 	gl_entries = []
@@ -426,7 +427,6 @@
 		pluck="name",
 	)
 
-	ignore_opening_entries = False
 	if accounts_list:
 		# For balance sheet
 		if not from_date:
diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js
index 8dc5ab3..92cf36e 100644
--- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js
+++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js
@@ -13,14 +13,6 @@
 
 	frappe.query_reports["Gross and Net Profit Report"]["filters"].push(
 		{
-			"fieldname": "project",
-			"label": __("Project"),
-			"fieldtype": "MultiSelectList",
-			get_data: function(txt) {
-				return frappe.db.get_link_options('Project', txt);
-			}
-		},
-		{
 			"fieldname": "accumulated_values",
 			"label": __("Accumulated Values"),
 			"fieldtype": "Check"
diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js
index 298d838..e794f27 100644
--- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js
+++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js
@@ -10,16 +10,6 @@
 
 	frappe.query_reports["Profit and Loss Statement"]["filters"].push(
 		{
-			"fieldname": "project",
-			"label": __("Project"),
-			"fieldtype": "MultiSelectList",
-			get_data: function(txt) {
-				return frappe.db.get_link_options('Project', txt, {
-					company: frappe.query_report.get_filter_value("company")
-				});
-			},
-		},
-		{
 			"fieldname": "include_default_book_entries",
 			"label": __("Include Default Book Entries"),
 			"fieldtype": "Check",
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
index 889ede5..6caebd3 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
@@ -25,7 +25,7 @@
 				"label": __("Fiscal Year"),
 				"fieldtype": "Link",
 				"options": "Fiscal Year",
-				"default": frappe.defaults.get_user_default("fiscal_year"),
+				"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 				"reqd": 1,
 				"on_change": function(query_report) {
 					var fiscal_year = query_report.get_values().fiscal_year;
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js
index 078b065..e45c3ad 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.js
+++ b/erpnext/accounts/report/trial_balance/trial_balance.js
@@ -17,7 +17,7 @@
 				"label": __("Fiscal Year"),
 				"fieldtype": "Link",
 				"options": "Fiscal Year",
-				"default": frappe.defaults.get_user_default("fiscal_year"),
+				"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 				"reqd": 1,
 				"on_change": function(query_report) {
 					var fiscal_year = query_report.get_values().fiscal_year;
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index d51c4c4..599c8a3 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -117,6 +117,7 @@
 		filters,
 		gl_entries_by_account,
 		ignore_closing_entries=not flt(filters.with_period_closing_entry),
+		ignore_opening_entries=True,
 	)
 
 	calculate_values(accounts, gl_entries_by_account, opening_balances)
@@ -159,6 +160,8 @@
 			accounting_dimensions,
 			period_closing_voucher=last_period_closing_voucher[0].name,
 		)
+
+		# Report getting generate from the mid of a fiscal year
 		if getdate(last_period_closing_voucher[0].posting_date) < getdate(
 			add_days(filters.from_date, -1)
 		):
@@ -218,9 +221,18 @@
 		)
 	else:
 		if start_date:
-			opening_balance = opening_balance.where(closing_balance.posting_date >= start_date)
+			opening_balance = opening_balance.where(
+				(closing_balance.posting_date >= start_date)
+				& (closing_balance.posting_date < filters.from_date)
+			)
 			opening_balance = opening_balance.where(closing_balance.is_opening == "No")
-		opening_balance = opening_balance.where(closing_balance.posting_date < filters.from_date)
+		else:
+			opening_balance = opening_balance.where(
+				(closing_balance.posting_date < filters.from_date) | (closing_balance.is_opening == "Yes")
+			)
+
+	if doctype == "GL Entry":
+		opening_balance = opening_balance.where(closing_balance.is_cancelled == 0)
 
 	if (
 		not filters.show_unclosed_fy_pl_balances
diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js
index 0e93035..0f7578c 100644
--- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js
+++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js
@@ -16,7 +16,7 @@
 			"label": __("Fiscal Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"reqd": 1,
 			"on_change": function(query_report) {
 				var fiscal_year = query_report.get_values().fiscal_year;
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 31473db..e354663 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -850,7 +850,7 @@
 
 	if party_type == "Supplier":
 		held_invoices = frappe.db.sql(
-			"select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()",
+			"select name from `tabPurchase Invoice` where on_hold = 1 and release_date IS NOT NULL and release_date > CURDATE()",
 			as_dict=1,
 		)
 		held_invoices = set(d["name"] for d in held_invoices)
@@ -1110,6 +1110,12 @@
 	return " - ".join(parts)
 
 
+def parse_naming_series_variable(doc, variable):
+	if variable == "FY":
+		date = doc.get("posting_date") or doc.get("transaction_date") or getdate()
+		return get_fiscal_year(date=date, company=doc.get("company"))[0]
+
+
 @frappe.whitelist()
 def get_coa(doctype, parent, is_root, chart=None):
 	from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import (
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js
index f9c6007..4ccc3f8 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.js
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.js
@@ -63,7 +63,7 @@
 			fieldnames_to_be_altered = {
 				target_location: { read_only: 0, reqd: 1 },
 				source_location: { read_only: 1, reqd: 0 },
-				from_employee: { read_only: 0, reqd: 1 },
+				from_employee: { read_only: 0, reqd: 0 },
 				to_employee: { read_only: 1, reqd: 0 }
 			};
 		}
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py
index b58ca10..22055dc 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.py
@@ -62,29 +62,20 @@
 					frappe.throw(_("Source and Target Location cannot be same"))
 
 			if self.purpose == "Receipt":
-				# only when asset is bought and first entry is made
-				if not d.source_location and not (d.target_location or d.to_employee):
+				if not (d.source_location or d.from_employee) and not (d.target_location or d.to_employee):
 					frappe.throw(
 						_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset)
 					)
-				elif d.source_location:
-					# when asset is received from an employee
-					if d.target_location and not d.from_employee:
-						frappe.throw(
-							_("From employee is required while receiving Asset {0} to a target location").format(
-								d.asset
-							)
-						)
-					if d.from_employee and not d.target_location:
-						frappe.throw(
-							_("Target Location is required while receiving Asset {0} from an employee").format(d.asset)
-						)
-					if d.to_employee and d.target_location:
-						frappe.throw(
-							_(
-								"Asset {0} cannot be received at a location and given to employee in a single movement"
-							).format(d.asset)
-						)
+				elif d.from_employee and not d.target_location:
+					frappe.throw(
+						_("Target Location is required while receiving Asset {0} from an employee").format(d.asset)
+					)
+				elif d.to_employee and d.target_location:
+					frappe.throw(
+						_(
+							"Asset {0} cannot be received at a location and given to an employee in a single movement"
+						).format(d.asset)
+					)
 
 	def validate_employee(self):
 		for d in self.assets:
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
index b788a32..48b17f5 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
@@ -82,7 +82,7 @@
 			"label": __("Start Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
 		},
 		{
@@ -90,7 +90,7 @@
 			"label": __("End Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
 		},
 		{
diff --git a/erpnext/buying/dashboard_chart/purchase_order_trends/purchase_order_trends.json b/erpnext/buying/dashboard_chart/purchase_order_trends/purchase_order_trends.json
index 6452ed2..751796b 100644
--- a/erpnext/buying/dashboard_chart/purchase_order_trends/purchase_order_trends.json
+++ b/erpnext/buying/dashboard_chart/purchase_order_trends/purchase_order_trends.json
@@ -5,18 +5,19 @@
  "custom_options": "{\"type\": \"line\", \"axisOptions\": {\"shortenYAxisNumbers\": 1}, \"tooltipOptions\": {}, \"lineOptions\": {\"regionFill\": 1}}",
  "docstatus": 0,
  "doctype": "Dashboard Chart",
- "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
+ "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
  "filters_json": "{\"period\":\"Monthly\",\"period_based_on\":\"posting_date\",\"based_on\":\"Item\"}",
- "idx": 0,
+ "idx": 1,
  "is_public": 1,
  "is_standard": 1,
- "modified": "2020-07-21 16:13:25.092287",
+ "modified": "2023-07-19 13:06:42.937941",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Trends",
  "number_of_groups": 0,
  "owner": "Administrator",
  "report_name": "Purchase Order Trends",
+ "roles": [],
  "timeseries": 0,
  "type": "Line",
  "use_report_chart": 1,
diff --git a/erpnext/buying/dashboard_chart/top_suppliers/top_suppliers.json b/erpnext/buying/dashboard_chart/top_suppliers/top_suppliers.json
index 6f7da8e..f6b9717 100644
--- a/erpnext/buying/dashboard_chart/top_suppliers/top_suppliers.json
+++ b/erpnext/buying/dashboard_chart/top_suppliers/top_suppliers.json
@@ -4,18 +4,19 @@
  "creation": "2020-07-20 21:01:02.329519",
  "docstatus": 0,
  "doctype": "Dashboard Chart",
- "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
+ "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
  "filters_json": "{\"period\":\"Monthly\",\"period_based_on\":\"posting_date\",\"based_on\":\"Supplier\"}",
  "idx": 0,
  "is_public": 1,
  "is_standard": 1,
- "modified": "2020-07-22 12:43:40.829652",
+ "modified": "2023-07-19 13:07:41.753556",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Top Suppliers",
  "number_of_groups": 0,
  "owner": "Administrator",
  "report_name": "Purchase Receipt Trends",
+ "roles": [],
  "timeseries": 0,
  "type": "Bar",
  "use_report_chart": 1,
diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js
index 5b95d0f..372ca56 100644
--- a/erpnext/buying/doctype/supplier/supplier.js
+++ b/erpnext/buying/doctype/supplier/supplier.js
@@ -66,8 +66,6 @@
 	},
 
 	refresh: function (frm) {
-		frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Supplier' }
-
 		if (frappe.defaults.get_default("supp_master_name") != "Naming Series") {
 			frm.toggle_display("naming_series", false);
 		} else {
diff --git a/erpnext/communication/doctype/communication_medium/communication_medium.json b/erpnext/communication/doctype/communication_medium/communication_medium.json
index 1e1fe3b..b6b9c7e 100644
--- a/erpnext/communication/doctype/communication_medium/communication_medium.json
+++ b/erpnext/communication/doctype/communication_medium/communication_medium.json
@@ -61,7 +61,7 @@
    "fieldname": "communication_channel",
    "fieldtype": "Select",
    "label": "Communication Channel",
-   "options": "\nExotel"
+   "options": ""
   }
  ],
  "links": [],
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 3bb1128..d1dcd6a 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -824,6 +824,15 @@
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
+def get_doctypes_for_closing(doctype, txt, searchfield, start, page_len, filters):
+	doctypes = frappe.get_hooks("period_closing_doctypes")
+	if txt:
+		doctypes = [d for d in doctypes if txt.lower() in d.lower()]
+	return [(d,) for d in set(doctypes)]
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
 def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
 
 	item_doc = frappe.get_cached_doc("Item", filters.get("item_code"))
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 9546680..173e812 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -669,7 +669,11 @@
 	if reference_voucher_detail_no:
 		filters["voucher_detail_no"] = reference_voucher_detail_no
 
-	if item_row and item_row.get("warehouse"):
+	if (
+		voucher_type in ["Purchase Receipt", "Purchase Invoice"]
+		and item_row
+		and item_row.get("warehouse")
+	):
 		filters["warehouse"] = item_row.get("warehouse")
 
 	return filters
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 5137e03..caf4b6f 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -201,6 +201,12 @@
 					warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"]
 
 				expense_account = frappe.get_cached_value("Company", self.company, "default_expense_account")
+				if not expense_account:
+					frappe.throw(
+						_(
+							"Please set default cost of goods sold account in company {0} for booking rounding gain and loss during stock transfer"
+						).format(frappe.bold(self.company))
+					)
 
 				gl_list.append(
 					self.get_gl_dict(
diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js
index b98a27e..9ac5418 100644
--- a/erpnext/crm/doctype/lead/lead.js
+++ b/erpnext/crm/doctype/lead/lead.js
@@ -30,11 +30,6 @@
 		var me = this;
 		let doc = this.frm.doc;
 		erpnext.toggle_naming_series();
-		frappe.dynamic_link = {
-			doc: doc,
-			fieldname: 'name',
-			doctype: 'Lead'
-		};
 
 		if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) {
 			this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create"));
diff --git a/erpnext/crm/doctype/prospect/prospect.js b/erpnext/crm/doctype/prospect/prospect.js
index 495ed29..c1a7ff5 100644
--- a/erpnext/crm/doctype/prospect/prospect.js
+++ b/erpnext/crm/doctype/prospect/prospect.js
@@ -3,8 +3,6 @@
 
 frappe.ui.form.on('Prospect', {
 	refresh (frm) {
-		frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: frm.doctype };
-
 		if (!frm.is_new() && frappe.boot.user.can_create.includes("Customer")) {
 			frm.add_custom_button(__("Customer"), function() {
 				frappe.model.open_mapped_doc({
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json
deleted file mode 100644
index 0d42ca8..0000000
--- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json
+++ /dev/null
@@ -1,89 +0,0 @@
-{
- "actions": [],
- "creation": "2019-05-21 07:41:53.536536",
- "doctype": "DocType",
- "engine": "InnoDB",
- "field_order": [
-  "enabled",
-  "section_break_2",
-  "account_sid",
-  "api_key",
-  "api_token",
-  "section_break_6",
-  "map_custom_field_to_doctype",
-  "target_doctype"
- ],
- "fields": [
-  {
-   "default": "0",
-   "fieldname": "enabled",
-   "fieldtype": "Check",
-   "label": "Enabled"
-  },
-  {
-   "depends_on": "enabled",
-   "fieldname": "section_break_2",
-   "fieldtype": "Section Break",
-   "label": "Credentials"
-  },
-  {
-   "fieldname": "account_sid",
-   "fieldtype": "Data",
-   "label": "Account SID"
-  },
-  {
-   "fieldname": "api_token",
-   "fieldtype": "Data",
-   "label": "API Token"
-  },
-  {
-   "fieldname": "api_key",
-   "fieldtype": "Data",
-   "label": "API Key"
-  },
-  {
-   "depends_on": "enabled",
-   "fieldname": "section_break_6",
-   "fieldtype": "Section Break",
-   "label": "Custom Field"
-  },
-  {
-   "default": "0",
-   "fieldname": "map_custom_field_to_doctype",
-   "fieldtype": "Check",
-   "label": "Map Custom Field to DocType"
-  },
-  {
-   "depends_on": "map_custom_field_to_doctype",
-   "fieldname": "target_doctype",
-   "fieldtype": "Link",
-   "label": "Target DocType",
-   "mandatory_depends_on": "map_custom_field_to_doctype",
-   "options": "DocType"
-  }
- ],
- "issingle": 1,
- "links": [],
- "modified": "2022-12-14 17:24:50.176107",
- "modified_by": "Administrator",
- "module": "ERPNext Integrations",
- "name": "Exotel Settings",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "role": "System Manager",
-   "share": 1,
-   "write": 1
-  }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "ASC",
- "states": [],
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
deleted file mode 100644
index 4879cb5..0000000
--- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-import requests
-from frappe import _
-from frappe.model.document import Document
-
-
-class ExotelSettings(Document):
-	def validate(self):
-		self.verify_credentials()
-
-	def verify_credentials(self):
-		if self.enabled:
-			response = requests.get(
-				"https://api.exotel.com/v1/Accounts/{sid}".format(sid=self.account_sid),
-				auth=(self.api_key, self.api_token),
-			)
-			if response.status_code != 200:
-				frappe.throw(_("Invalid credentials"))
diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py
deleted file mode 100644
index 0d40667..0000000
--- a/erpnext/erpnext_integrations/exotel_integration.py
+++ /dev/null
@@ -1,151 +0,0 @@
-import frappe
-import requests
-
-# api/method/erpnext.erpnext_integrations.exotel_integration.handle_incoming_call
-# api/method/erpnext.erpnext_integrations.exotel_integration.handle_end_call
-# api/method/erpnext.erpnext_integrations.exotel_integration.handle_missed_call
-
-
-@frappe.whitelist(allow_guest=True)
-def handle_incoming_call(**kwargs):
-	try:
-		exotel_settings = get_exotel_settings()
-		if not exotel_settings.enabled:
-			return
-
-		call_payload = kwargs
-		status = call_payload.get("Status")
-		if status == "free":
-			return
-
-		call_log = get_call_log(call_payload)
-		if not call_log:
-			create_call_log(call_payload)
-		else:
-			update_call_log(call_payload, call_log=call_log)
-	except Exception as e:
-		frappe.db.rollback()
-		exotel_settings.log_error("Error in Exotel incoming call")
-		frappe.db.commit()
-
-
-@frappe.whitelist(allow_guest=True)
-def handle_end_call(**kwargs):
-	update_call_log(kwargs, "Completed")
-
-
-@frappe.whitelist(allow_guest=True)
-def handle_missed_call(**kwargs):
-	status = ""
-	call_type = kwargs.get("CallType")
-	dial_call_status = kwargs.get("DialCallStatus")
-
-	if call_type == "incomplete" and dial_call_status == "no-answer":
-		status = "No Answer"
-	elif call_type == "client-hangup" and dial_call_status == "canceled":
-		status = "Canceled"
-	elif call_type == "incomplete" and dial_call_status == "failed":
-		status = "Failed"
-
-	update_call_log(kwargs, status)
-
-
-def update_call_log(call_payload, status="Ringing", call_log=None):
-	call_log = call_log or get_call_log(call_payload)
-
-	# for a new sid, call_log and get_call_log will be empty so create a new log
-	if not call_log:
-		call_log = create_call_log(call_payload)
-	if call_log:
-		call_log.status = status
-		call_log.to = call_payload.get("DialWhomNumber")
-		call_log.duration = call_payload.get("DialCallDuration") or 0
-		call_log.recording_url = call_payload.get("RecordingUrl")
-		call_log.save(ignore_permissions=True)
-		frappe.db.commit()
-		return call_log
-
-
-def get_call_log(call_payload):
-	call_log_id = call_payload.get("CallSid")
-	if frappe.db.exists("Call Log", call_log_id):
-		return frappe.get_doc("Call Log", call_log_id)
-
-
-def map_custom_field(call_payload, call_log):
-	field_value = call_payload.get("CustomField")
-
-	if not field_value:
-		return call_log
-
-	settings = get_exotel_settings()
-	target_doctype = settings.target_doctype
-	mapping_enabled = settings.map_custom_field_to_doctype
-
-	if not mapping_enabled or not target_doctype:
-		return call_log
-
-	call_log.append("links", {"link_doctype": target_doctype, "link_name": field_value})
-
-	return call_log
-
-
-def create_call_log(call_payload):
-	call_log = frappe.new_doc("Call Log")
-	call_log.id = call_payload.get("CallSid")
-	call_log.to = call_payload.get("DialWhomNumber")
-	call_log.medium = call_payload.get("To")
-	call_log.status = "Ringing"
-	setattr(call_log, "from", call_payload.get("CallFrom"))
-	map_custom_field(call_payload, call_log)
-	call_log.save(ignore_permissions=True)
-	frappe.db.commit()
-	return call_log
-
-
-@frappe.whitelist()
-def get_call_status(call_id):
-	endpoint = get_exotel_endpoint("Calls/{call_id}.json".format(call_id=call_id))
-	response = requests.get(endpoint)
-	status = response.json().get("Call", {}).get("Status")
-	return status
-
-
-@frappe.whitelist()
-def make_a_call(from_number, to_number, caller_id, **kwargs):
-	endpoint = get_exotel_endpoint("Calls/connect.json?details=true")
-	response = requests.post(
-		endpoint, data={"From": from_number, "To": to_number, "CallerId": caller_id, **kwargs}
-	)
-
-	return response.json()
-
-
-def get_exotel_settings():
-	return frappe.get_single("Exotel Settings")
-
-
-def whitelist_numbers(numbers, caller_id):
-	endpoint = get_exotel_endpoint("CustomerWhitelist")
-	response = requests.post(
-		endpoint,
-		data={
-			"VirtualNumber": caller_id,
-			"Number": numbers,
-		},
-	)
-
-	return response
-
-
-def get_all_exophones():
-	endpoint = get_exotel_endpoint("IncomingPhoneNumbers")
-	response = requests.post(endpoint)
-	return response
-
-
-def get_exotel_endpoint(action):
-	settings = get_exotel_settings()
-	return "https://{api_key}:{api_token}@api.exotel.com/v1/Accounts/{sid}/{action}".format(
-		api_key=settings.api_key, api_token=settings.api_token, sid=settings.account_sid, action=action
-	)
diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
index ccc46b7..5c4be6f 100644
--- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
+++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
@@ -231,17 +231,6 @@
    "type": "Card Break"
   },
   {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Exotel Settings",
-   "link_count": 0,
-   "link_to": "Exotel Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
    "hidden": 0,
    "is_query_report": 0,
    "label": "Woocommerce Settings",
@@ -252,7 +241,7 @@
    "type": "Link"
   }
  ],
- "modified": "2023-05-24 14:47:25.984717",
+ "modified": "2023-05-24 14:47:26.984717",
  "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "ERPNext Integrations",
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index d02d318..d8b40e3 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -83,7 +83,7 @@
 my_account_context = "erpnext.e_commerce.shopping_cart.utils.update_my_account_context"
 webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform_list_context"
 
-calendars = ["Task", "Work Order", "Leave Application", "Sales Order", "Holiday List", "ToDo"]
+calendars = ["Task", "Work Order", "Sales Order", "Holiday List", "ToDo"]
 
 website_generators = ["Item Group", "Website Item", "BOM", "Sales Partner"]
 
@@ -285,10 +285,34 @@
 	"Customer": "erpnext.controllers.queries.customer_query",
 }
 
+period_closing_doctypes = [
+	"Sales Invoice",
+	"Purchase Invoice",
+	"Journal Entry",
+	"Bank Clearance",
+	"Stock Entry",
+	"Dunning",
+	"Invoice Discounting",
+	"Payment Entry",
+	"Period Closing Voucher",
+	"Process Deferred Accounting",
+	"Asset",
+	"Asset Capitalization",
+	"Asset Repair",
+	"Delivery Note",
+	"Landed Cost Voucher",
+	"Purchase Receipt",
+	"Stock Reconciliation",
+	"Subcontracting Receipt",
+]
+
 doc_events = {
 	"*": {
 		"validate": "erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
 	},
+	tuple(period_closing_doctypes): {
+		"validate": "erpnext.accounts.doctype.accounting_period.accounting_period.validate_accounting_period_on_doc_save",
+	},
 	"Stock Entry": {
 		"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
 		"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
@@ -334,6 +358,7 @@
 			"erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status",
 			"erpnext.accounts.doctype.dunning.dunning.resolve_dunning",
 		],
+		"on_cancel": ["erpnext.accounts.doctype.dunning.dunning.resolve_dunning"],
 		"on_trash": "erpnext.regional.check_deletion_permission",
 	},
 	"Address": {
@@ -354,6 +379,11 @@
 	},
 }
 
+# function should expect the variable and doc as arguments
+naming_series_variables = {
+	"FY": "erpnext.accounts.utils.parse_naming_series_variable",
+}
+
 # On cancel event Payment Entry will be exempted and all linked submittable doctype will get cancelled.
 # to maintain data integrity we exempted payment entry. it will un-link when sales invoice get cancelled.
 # if payment entry not in auto cancel exempted doctypes it will cancel payment entry.
@@ -459,15 +489,6 @@
 
 invoice_doctypes = ["Sales Invoice", "Purchase Invoice"]
 
-period_closing_doctypes = [
-	"Sales Invoice",
-	"Purchase Invoice",
-	"Journal Entry",
-	"Bank Clearance",
-	"Asset",
-	"Stock Entry",
-]
-
 bank_reconciliation_doctypes = [
 	"Payment Entry",
 	"Journal Entry",
@@ -611,3 +632,8 @@
 additional_timeline_content = {
 	"*": ["erpnext.telephony.doctype.call_log.call_log.get_linked_call_logs"]
 }
+
+
+extend_bootinfo = [
+	"erpnext.support.doctype.service_level_agreement.service_level_agreement.add_sla_doctypes",
+]
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 6dc1ff6..d8cc8f6 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -621,7 +621,7 @@
 	def create_work_order(self, item):
 		from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError
 
-		if item.get("qty") <= 0:
+		if flt(item.get("qty")) <= 0:
 			return
 
 		wo = frappe.new_doc("Work Order")
@@ -697,10 +697,9 @@
 			material_request.flags.ignore_permissions = 1
 			material_request.run_method("set_missing_values")
 
+			material_request.save()
 			if self.get("submit_material_request"):
 				material_request.submit()
-			else:
-				material_request.save()
 
 		frappe.flags.mute_messages = False
 
@@ -1540,7 +1539,7 @@
 		frappe.qb.from_(table)
 		.inner_join(child)
 		.on(table.name == child.parent)
-		.select(Sum(child.required_bom_qty * IfNull(child.conversion_factor, 1.0)))
+		.select(Sum(child.quantity * IfNull(child.conversion_factor, 1.0)))
 		.where(
 			(table.docstatus == 1)
 			& (child.item_code == item_code)
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index fcfba7f..f60dbfc 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -933,6 +933,54 @@
 
 		self.assertEqual(after_qty, before_qty)
 
+	def test_resered_qty_for_production_plan_for_material_requests_with_multi_UOM(self):
+		from erpnext.stock.utils import get_or_make_bin
+
+		fg_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name
+		bom_item = make_item(
+			properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1", "purchase_uom": "Nos"}
+		).name
+
+		if not frappe.db.exists("UOM Conversion Detail", {"parent": bom_item, "uom": "Nos"}):
+			doc = frappe.get_doc("Item", bom_item)
+			doc.append("uoms", {"uom": "Nos", "conversion_factor": 25})
+			doc.save()
+
+		make_bom(item=fg_item, raw_materials=[bom_item], source_warehouse="_Test Warehouse - _TC")
+
+		bin_name = get_or_make_bin(bom_item, "_Test Warehouse - _TC")
+		before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+
+		pln = create_production_plan(
+			item_code=fg_item, planned_qty=100, ignore_existing_ordered_qty=1, stock_uom="_Test UOM 1"
+		)
+
+		for row in pln.mr_items:
+			self.assertEqual(row.uom, "Nos")
+			self.assertEqual(row.quantity, 4)
+
+			reserved_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+			self.assertEqual(reserved_qty - before_qty, 100.0)
+
+		pln.submit_material_request = 1
+		pln.make_work_order()
+
+		for work_order in frappe.get_all(
+			"Work Order",
+			fields=["name"],
+			filters={"production_plan": pln.name},
+		):
+			wo_doc = frappe.get_doc("Work Order", work_order.name)
+			wo_doc.source_warehouse = "_Test Warehouse - _TC"
+			wo_doc.wip_warehouse = "_Test Warehouse 1 - _TC"
+			wo_doc.fg_warehouse = "_Test Warehouse - _TC"
+			wo_doc.submit()
+
+		reserved_qty_after_mr = flt(
+			frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")
+		)
+		self.assertEqual(reserved_qty_after_mr, before_qty)
+
 	def test_skip_available_qty_for_sub_assembly_items(self):
 		from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
 
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 79b1e79..7c15bf9 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -1026,7 +1026,7 @@
 			consumed_qty = frappe.db.sql(
 				"""
 				SELECT
-					SUM(qty)
+					SUM(detail.qty)
 				FROM
 					`tabStock Entry` entry,
 					`tabStock Entry Detail` detail
diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
index 782ce81..a874f22 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
@@ -17,7 +17,7 @@
 			label: __("Fiscal Year"),
 			fieldtype: "Link",
 			options: "Fiscal Year",
-			default: frappe.defaults.get_user_default("fiscal_year"),
+			default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			reqd: 1,
 			on_change: function(query_report) {
 				var fiscal_year = query_report.get_values().fiscal_year;
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index b3b9bc6..fb59a7d 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -317,7 +317,7 @@
 erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries
 erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch
 erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance
-erpnext.patches.v14_0.update_closing_balances #17-05-2023
+erpnext.patches.v14_0.update_closing_balances #14-07-2023
 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
 # below migration patches should always run last
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
@@ -333,4 +333,7 @@
 erpnext.patches.v14_0.cleanup_workspaces
 erpnext.patches.v15_0.remove_loan_management_module #2023-07-03
 erpnext.patches.v14_0.set_report_in_process_SOA
-erpnext.buying.doctype.supplier.patches.migrate_supplier_portal_users
\ No newline at end of file
+erpnext.buying.doctype.supplier.patches.migrate_supplier_portal_users
+execute:frappe.defaults.clear_default("fiscal_year")
+erpnext.patches.v15_0.remove_exotel_integration
+erpnext.patches.v14_0.single_to_multi_dunning
diff --git a/erpnext/patches/v14_0/single_to_multi_dunning.py b/erpnext/patches/v14_0/single_to_multi_dunning.py
new file mode 100644
index 0000000..7a8e591
--- /dev/null
+++ b/erpnext/patches/v14_0/single_to_multi_dunning.py
@@ -0,0 +1,49 @@
+import frappe
+
+from erpnext.accounts.general_ledger import make_reverse_gl_entries
+
+
+def execute():
+	frappe.reload_doc("accounts", "doctype", "overdue_payment")
+	frappe.reload_doc("accounts", "doctype", "dunning")
+
+	all_dunnings = frappe.get_all("Dunning", filters={"docstatus": ("!=", 2)}, pluck="name")
+	for dunning_name in all_dunnings:
+		dunning = frappe.get_doc("Dunning", dunning_name)
+		if not dunning.sales_invoice:
+			# nothing we can do
+			continue
+
+		if dunning.overdue_payments:
+			# something's already here, doesn't need patching
+			continue
+
+		payment_schedules = frappe.get_all(
+			"Payment Schedule",
+			filters={"parent": dunning.sales_invoice},
+			fields=[
+				"parent as sales_invoice",
+				"name as payment_schedule",
+				"payment_term",
+				"due_date",
+				"invoice_portion",
+				"payment_amount",
+				# at the time of creating this dunning, the full amount was outstanding
+				"payment_amount as outstanding",
+				"'0' as paid_amount",
+				"discounted_amount",
+			],
+		)
+
+		dunning.extend("overdue_payments", payment_schedules)
+		dunning.validate()
+
+		dunning.flags.ignore_validate_update_after_submit = True
+		dunning.save()
+
+		if dunning.status != "Resolved":
+			# With the new logic, dunning amount gets recorded as additional income
+			# at time of payment. We don't want to record the dunning amount twice,
+			# so we reverse previous GL Entries that recorded the dunning amount at
+			# time of submission of the Dunning.
+			make_reverse_gl_entries(voucher_type="Dunning", voucher_no=dunning.name)
diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py
index d664677..2c84281 100644
--- a/erpnext/patches/v14_0/update_closing_balances.py
+++ b/erpnext/patches/v14_0/update_closing_balances.py
@@ -13,56 +13,62 @@
 def execute():
 	frappe.db.truncate("Account Closing Balance")
 
-	i = 0
-	company_wise_order = {}
-	for pcv in frappe.db.get_all(
-		"Period Closing Voucher",
-		fields=["company", "posting_date", "name"],
-		filters={"docstatus": 1},
-		order_by="posting_date",
-	):
+	for company in frappe.get_all("Company", pluck="name"):
+		i = 0
+		company_wise_order = {}
+		for pcv in frappe.db.get_all(
+			"Period Closing Voucher",
+			fields=["company", "posting_date", "name"],
+			filters={"docstatus": 1, "company": company},
+			order_by="posting_date",
+		):
 
-		company_wise_order.setdefault(pcv.company, [])
-		if pcv.posting_date not in company_wise_order[pcv.company]:
-			pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name)
-			pcv_doc.year_start_date = get_fiscal_year(
-				pcv.posting_date, pcv.fiscal_year, company=pcv.company
-			)[1]
+			company_wise_order.setdefault(pcv.company, [])
+			if pcv.posting_date not in company_wise_order[pcv.company]:
+				pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name)
+				pcv_doc.year_start_date = get_fiscal_year(
+					pcv.posting_date, pcv.fiscal_year, company=pcv.company
+				)[1]
 
-			# get gl entries against pcv
-			gl_entries = frappe.db.get_all(
-				"GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"]
-			)
-			for entry in gl_entries:
-				entry["is_period_closing_voucher_entry"] = 1
-				entry["closing_date"] = pcv_doc.posting_date
-				entry["period_closing_voucher"] = pcv_doc.name
-
-			# get all gl entries for the year
-			closing_entries = frappe.db.get_all(
-				"GL Entry",
-				filters={
-					"is_cancelled": 0,
-					"voucher_no": ["!=", pcv.name],
-					"posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]],
-					"is_opening": "No",
-				},
-				fields=["*"],
-			)
-
-			if i == 0:
-				# add opening entries only for the first pcv
-				closing_entries += frappe.db.get_all(
-					"GL Entry",
-					filters={"is_cancelled": 0, "is_opening": "Yes"},
-					fields=["*"],
+				# get gl entries against pcv
+				gl_entries = frappe.db.get_all(
+					"GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"]
 				)
+				for entry in gl_entries:
+					entry["is_period_closing_voucher_entry"] = 1
+					entry["closing_date"] = pcv_doc.posting_date
+					entry["period_closing_voucher"] = pcv_doc.name
 
-			for entry in closing_entries:
-				entry["closing_date"] = pcv_doc.posting_date
-				entry["period_closing_voucher"] = pcv_doc.name
+				closing_entries = []
 
-			make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name)
-			company_wise_order[pcv.company].append(pcv.posting_date)
+				if pcv.posting_date not in company_wise_order[pcv.company]:
+					# get all gl entries for the year
+					closing_entries = frappe.db.get_all(
+						"GL Entry",
+						filters={
+							"is_cancelled": 0,
+							"voucher_no": ["!=", pcv.name],
+							"posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]],
+							"is_opening": "No",
+							"company": company,
+						},
+						fields=["*"],
+					)
 
-			i += 1
+				if i == 0:
+					# add opening entries only for the first pcv
+					closing_entries += frappe.db.get_all(
+						"GL Entry",
+						filters={"is_cancelled": 0, "is_opening": "Yes", "company": company},
+						fields=["*"],
+					)
+
+				for entry in closing_entries:
+					entry["closing_date"] = pcv_doc.posting_date
+					entry["period_closing_voucher"] = pcv_doc.name
+
+				entries = gl_entries + closing_entries
+
+				make_closing_entries(entries, pcv.name, pcv.company, pcv.posting_date)
+				company_wise_order[pcv.company].append(pcv.posting_date)
+				i += 1
diff --git a/erpnext/patches/v15_0/remove_exotel_integration.py b/erpnext/patches/v15_0/remove_exotel_integration.py
new file mode 100644
index 0000000..9b99fc6
--- /dev/null
+++ b/erpnext/patches/v15_0/remove_exotel_integration.py
@@ -0,0 +1,37 @@
+import click
+import frappe
+from frappe import _
+from frappe.desk.doctype.notification_log.notification_log import make_notification_logs
+from frappe.utils.user import get_system_managers
+
+SETTINGS_DOCTYPE = "Exotel Settings"
+
+
+def execute():
+	if "exotel_integration" in frappe.get_installed_apps():
+		return
+
+	try:
+		exotel = frappe.get_doc(SETTINGS_DOCTYPE)
+		if exotel.enabled:
+			notify_existing_users()
+
+		frappe.delete_doc("DocType", SETTINGS_DOCTYPE)
+	except Exception:
+		frappe.log_error("Failed to remove Exotel Integration.")
+
+
+def notify_existing_users():
+	click.secho(
+		"Exotel integration is moved to a separate app and will be removed from ERPNext in version-15.\n"
+		"Please install the app to continue using the integration: https://github.com/frappe/exotel_integration",
+		fg="yellow",
+	)
+
+	notification = {
+		"subject": _(
+			"WARNING: Exotel app has been separated from ERPNext, please install the app to continue using Exotel integration."
+		),
+		"type": "Alert",
+	}
+	make_notification_logs(notification, get_system_managers(only_name=True))
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 543d0e9..6410333 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -358,12 +358,14 @@
 	}
 
 	refresh() {
+
 		erpnext.toggle_naming_series();
 		erpnext.hide_company();
 		this.set_dynamic_labels();
 		this.setup_sms();
 		this.setup_quality_inspection();
 		this.validate_has_items();
+		erpnext.utils.view_serial_batch_nos(this.frm);
 	}
 
 	scan_barcode() {
diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js
index b0082bd..959cf50 100644
--- a/erpnext/public/js/financial_statements.js
+++ b/erpnext/public/js/financial_statements.js
@@ -56,7 +56,7 @@
 		// dropdown for links to other financial statements
 		erpnext.financial_statements.filters = get_filters()
 
-		let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
+		let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
 
 		frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
 			var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
@@ -137,7 +137,7 @@
 			"label": __("Start Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"reqd": 1,
 			"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
 		},
@@ -146,7 +146,7 @@
 			"label": __("End Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"reqd": 1,
 			"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
 		},
@@ -182,6 +182,16 @@
 					company: frappe.query_report.get_filter_value("company")
 				});
 			}
+		},
+		{
+			"fieldname": "project",
+			"label": __("Project"),
+			"fieldtype": "MultiSelectList",
+			get_data: function(txt) {
+				return frappe.db.get_link_options('Project', txt, {
+					company: frappe.query_report.get_filter_value("company")
+				});
+			},
 		}
 	]
 
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index a859a67..cc03eca 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -113,6 +113,23 @@
 		}
 	},
 
+	view_serial_batch_nos: function(frm) {
+		let bundle_ids = frm.doc.items.filter(d => d.serial_and_batch_bundle);
+
+		if (bundle_ids?.length) {
+			frm.add_custom_button(__('Serial / Batch Nos'), () => {
+				frappe.route_options = {
+					"voucher_no": frm.doc.name,
+					"voucher_type": frm.doc.doctype,
+					"from_date": frm.doc.posting_date || frm.doc.transaction_date,
+					"to_date": frm.doc.posting_date || frm.doc.transaction_date,
+					"company": frm.doc.company,
+				};
+				frappe.set_route("query-report", "Serial and Batch Summary");
+			}, __('View'));
+		}
+	},
+
 	add_indicator_for_multicompany: function(frm, info) {
 		frm.dashboard.stats_area.show();
 		frm.dashboard.stats_area_row.addClass('flex');
@@ -381,6 +398,27 @@
 					});
 				});
 		});
+	},
+
+	get_fiscal_year: function(date) {
+		if(!date) {
+			date = frappe.datetime.get_today();
+		}
+
+		let fiscal_year = '';
+		frappe.call({
+			method: "erpnext.accounts.utils.get_fiscal_year",
+			args: {
+				date: date
+			},
+			async: false,
+			callback: function(r) {
+				if (r.message) {
+					fiscal_year = r.message[0];
+				}
+			}
+		});
+		return fiscal_year;
 	}
 });
 
@@ -632,7 +670,6 @@
 		fields.splice(3, 0, {
 			fieldtype: 'Float',
 			fieldname: "conversion_factor",
-			in_list_view: 1,
 			label: __("Conversion Factor"),
 			precision: get_precision('conversion_factor')
 		})
@@ -640,6 +677,7 @@
 
 	new frappe.ui.Dialog({
 		title: __("Update Items"),
+		size: "extra-large",
 		fields: [
 			{
 				fieldname: "trans_items",
@@ -854,95 +892,87 @@
 
 // Show SLA dashboard
 $(document).on('app_ready', function() {
-	frappe.call({
-		method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_sla_doctypes',
-		callback: function(r) {
-			if (!r.message)
-				return;
+	$.each(frappe.boot.service_level_agreement_doctypes, function(_i, d) {
+		frappe.ui.form.on(d, {
+			onload: function(frm) {
+				if (!frm.doc.service_level_agreement)
+					return;
 
-			$.each(r.message, function(_i, d) {
-				frappe.ui.form.on(d, {
-					onload: function(frm) {
-						if (!frm.doc.service_level_agreement)
-							return;
-
-						frappe.call({
-							method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters',
-							args: {
-								doctype: frm.doc.doctype,
-								name: frm.doc.service_level_agreement,
-								customer: frm.doc.customer
-							},
-							callback: function (r) {
-								if (r && r.message) {
-									frm.set_query('priority', function() {
-										return {
-											filters: {
-												'name': ['in', r.message.priority],
-											}
-										};
-									});
-									frm.set_query('service_level_agreement', function() {
-										return {
-											filters: {
-												'name': ['in', r.message.service_level_agreements],
-											}
-										};
-									});
-								}
-							}
-						});
+				frappe.call({
+					method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters',
+					args: {
+						doctype: frm.doc.doctype,
+						name: frm.doc.service_level_agreement,
+						customer: frm.doc.customer
 					},
-
-					refresh: function(frm) {
-						if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement
-							&& ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) {
-							frappe.call({
-								'method': 'frappe.client.get',
-								args: {
-									doctype: 'Service Level Agreement',
-									name: frm.doc.service_level_agreement
-								},
-								callback: function(data) {
-									let statuses = data.message.pause_sla_on;
-									const hold_statuses = [];
-									$.each(statuses, (_i, entry) => {
-										hold_statuses.push(entry.status);
-									});
-									if (hold_statuses.includes(frm.doc.status)) {
-										frm.dashboard.clear_headline();
-										let message = {'indicator': 'orange', 'msg': __('SLA is on hold since {0}', [moment(frm.doc.on_hold_since).fromNow(true)])};
-										frm.dashboard.set_headline_alert(
-											'<div class="row">' +
-												'<div class="col-xs-12">' +
-													'<span class="indicator whitespace-nowrap '+ message.indicator +'"><span>'+ message.msg +'</span></span> ' +
-												'</div>' +
-											'</div>'
-										);
-									} else {
-										set_time_to_resolve_and_response(frm, data.message.apply_sla_for_resolution);
+					callback: function (r) {
+						if (r && r.message) {
+							frm.set_query('priority', function() {
+								return {
+									filters: {
+										'name': ['in', r.message.priority],
 									}
-								}
+								};
 							});
-						} else if (frm.doc.service_level_agreement) {
-							frm.dashboard.clear_headline();
-
-							let agreement_status = (frm.doc.agreement_status == 'Fulfilled') ?
-								{'indicator': 'green', 'msg': 'Service Level Agreement has been fulfilled'} :
-								{'indicator': 'red', 'msg': 'Service Level Agreement Failed'};
-
-							frm.dashboard.set_headline_alert(
-								'<div class="row">' +
-									'<div class="col-xs-12">' +
-										'<span class="indicator whitespace-nowrap '+ agreement_status.indicator +'"><span class="hidden-xs">'+ agreement_status.msg +'</span></span> ' +
-									'</div>' +
-								'</div>'
-							);
+							frm.set_query('service_level_agreement', function() {
+								return {
+									filters: {
+										'name': ['in', r.message.service_level_agreements],
+									}
+								};
+							});
 						}
-					},
+					}
 				});
-			});
-		}
+			},
+
+			refresh: function(frm) {
+				if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement
+					&& ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) {
+					frappe.call({
+						'method': 'frappe.client.get',
+						args: {
+							doctype: 'Service Level Agreement',
+							name: frm.doc.service_level_agreement
+						},
+						callback: function(data) {
+							let statuses = data.message.pause_sla_on;
+							const hold_statuses = [];
+							$.each(statuses, (_i, entry) => {
+								hold_statuses.push(entry.status);
+							});
+							if (hold_statuses.includes(frm.doc.status)) {
+								frm.dashboard.clear_headline();
+								let message = {'indicator': 'orange', 'msg': __('SLA is on hold since {0}', [moment(frm.doc.on_hold_since).fromNow(true)])};
+								frm.dashboard.set_headline_alert(
+									'<div class="row">' +
+										'<div class="col-xs-12">' +
+											'<span class="indicator whitespace-nowrap '+ message.indicator +'"><span>'+ message.msg +'</span></span> ' +
+										'</div>' +
+									'</div>'
+								);
+							} else {
+								set_time_to_resolve_and_response(frm, data.message.apply_sla_for_resolution);
+							}
+						}
+					});
+				} else if (frm.doc.service_level_agreement) {
+					frm.dashboard.clear_headline();
+
+					let agreement_status = (frm.doc.agreement_status == 'Fulfilled') ?
+						{'indicator': 'green', 'msg': 'Service Level Agreement has been fulfilled'} :
+						{'indicator': 'red', 'msg': 'Service Level Agreement Failed'};
+
+					frm.dashboard.set_headline_alert(
+						'<div class="row">' +
+							'<div class="col-xs-12">' +
+								'<span class="indicator whitespace-nowrap '+ agreement_status.indicator +'"><span class="hidden-xs">'+ agreement_status.msg +'</span></span> ' +
+							'</div>' +
+						'</div>'
+					);
+				}
+			},
+		});
 	});
 });
 
@@ -1011,4 +1041,4 @@
 	$btn.on("click", function() {
 		context.show_serial_batch_selector(grid_row.frm, grid_row.doc, "", "", true);
 	});
-}
+}
\ No newline at end of file
diff --git "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.js" "b/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.js"
index a4c7640..b85b58f 100644
--- "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.js"
+++ "b/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.js"
@@ -16,7 +16,7 @@
 			"label": __("Fiscal Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"reqd": 1
 		}
 	],
diff --git a/erpnext/regional/report/irs_1099/irs_1099.js b/erpnext/regional/report/irs_1099/irs_1099.js
index 070ff43..b3508e4 100644
--- a/erpnext/regional/report/irs_1099/irs_1099.js
+++ b/erpnext/regional/report/irs_1099/irs_1099.js
@@ -17,7 +17,7 @@
 			"label": __("Fiscal Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
+			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"reqd": 1,
 			"width": 80,
 		},
diff --git a/erpnext/selling/dashboard_chart/sales_order_trends/sales_order_trends.json b/erpnext/selling/dashboard_chart/sales_order_trends/sales_order_trends.json
index 914d915..2f668a8 100644
--- a/erpnext/selling/dashboard_chart/sales_order_trends/sales_order_trends.json
+++ b/erpnext/selling/dashboard_chart/sales_order_trends/sales_order_trends.json
@@ -5,18 +5,19 @@
  "custom_options": "{\"type\": \"line\", \"axisOptions\": {\"shortenYAxisNumbers\": 1}, \"tooltipOptions\": {}, \"lineOptions\": {\"regionFill\": 1}}",
  "docstatus": 0,
  "doctype": "Dashboard Chart",
- "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
+ "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
  "filters_json": "{\"period\":\"Monthly\",\"based_on\":\"Item\"}",
- "idx": 0,
+ "idx": 1,
  "is_public": 1,
  "is_standard": 1,
- "modified": "2020-07-22 16:24:45.726270",
+ "modified": "2023-07-19 13:09:45.341791",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Trends",
  "number_of_groups": 0,
  "owner": "Administrator",
  "report_name": "Sales Order Trends",
+ "roles": [],
  "timeseries": 0,
  "type": "Line",
  "use_report_chart": 1,
diff --git a/erpnext/selling/dashboard_chart/top_customers/top_customers.json b/erpnext/selling/dashboard_chart/top_customers/top_customers.json
index 59a2ba3..2972980 100644
--- a/erpnext/selling/dashboard_chart/top_customers/top_customers.json
+++ b/erpnext/selling/dashboard_chart/top_customers/top_customers.json
@@ -5,18 +5,19 @@
  "custom_options": "",
  "docstatus": 0,
  "doctype": "Dashboard Chart",
- "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
+ "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
  "filters_json": "{\"period\":\"Yearly\",\"based_on\":\"Customer\"}",
  "idx": 0,
  "is_public": 1,
  "is_standard": 1,
- "modified": "2020-07-22 17:03:10.320147",
+ "modified": "2023-07-19 13:14:20.151502",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Top Customers",
  "number_of_groups": 0,
  "owner": "Administrator",
  "report_name": "Delivery Note Trends",
+ "roles": [],
  "timeseries": 0,
  "type": "Bar",
  "use_report_chart": 1,
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index 540e767..60f0941 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -131,8 +131,6 @@
 			erpnext.toggle_naming_series();
 		}
 
-		frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Customer'}
-
 		if(!frm.doc.__islocal) {
 			frappe.contacts.render_address_and_contact(frm);
 
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 45100d7..796e258 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1904,12 +1904,11 @@
 						"voucher_no": so.name,
 						"voucher_detail_no": item.name,
 					},
-					fields=["status", "reserved_qty", "delivered_qty"],
+					fields=["reserved_qty", "delivered_qty"],
 				)
 
 				for sre_detail in sre_details:
 					self.assertEqual(sre_detail.reserved_qty, sre_detail.delivered_qty)
-					self.assertEqual(sre_detail.status, "Delivered")
 
 	def test_delivered_item_material_request(self):
 		"SO -> MR (Manufacture) -> WO. Test if WO Qty is updated in SO."
diff --git a/erpnext/selling/report/address_and_contacts/address_and_contacts.js b/erpnext/selling/report/address_and_contacts/address_and_contacts.js
index ef87586..8aa14d1 100644
--- a/erpnext/selling/report/address_and_contacts/address_and_contacts.js
+++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.js
@@ -13,7 +13,7 @@
 			"get_query": function() {
 				return {
 					"filters": {
-						"name": ["in","Customer,Supplier,Sales Partner"],
+						"name": ["in","Customer,Supplier,Sales Partner,Lead"],
 					}
 				}
 			}
diff --git a/erpnext/selling/report/address_and_contacts/address_and_contacts.py b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
index 9a1cfda..4542bdf 100644
--- a/erpnext/selling/report/address_and_contacts/address_and_contacts.py
+++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
@@ -130,6 +130,7 @@
 		"Customer": "customer_group",
 		"Supplier": "supplier_group",
 		"Sales Partner": "partner_type",
+		"Lead": "status",
 	}
 
 	return group[party_type]
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 3335387..f4682c1 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -81,8 +81,6 @@
 			disbale_coa_fields(frm);
 			frappe.contacts.render_address_and_contact(frm);
 
-			frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Company'}
-
 			if (frappe.perm.has_perm("Cost Center", 0, 'read')) {
 				frm.add_custom_button(__('Cost Centers'), function() {
 					frappe.set_route('Tree', 'Cost Center', {'company': frm.doc.name});
diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.json b/erpnext/setup/doctype/global_defaults/global_defaults.json
index bafb97a..823d2ba 100644
--- a/erpnext/setup/doctype/global_defaults/global_defaults.json
+++ b/erpnext/setup/doctype/global_defaults/global_defaults.json
@@ -1,352 +1,99 @@
 {
- "allow_copy": 1, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2013-05-02 17:53:24", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "editable_grid": 0, 
+ "actions": [],
+ "allow_copy": 1,
+ "creation": "2013-05-02 17:53:24",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+  "default_company",
+  "country",
+  "default_distance_unit",
+  "column_break_8",
+  "default_currency",
+  "hide_currency_symbol",
+  "disable_rounded_total",
+  "disable_in_words"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "default_company", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 1, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Default Company", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "default_company",
+   "fieldtype": "Link",
+   "ignore_user_permissions": 1,
+   "label": "Default Company",
+   "options": "Company"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "current_fiscal_year", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Current Fiscal Year", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Fiscal Year", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "country",
+   "fieldtype": "Link",
+   "label": "Country",
+   "options": "Country"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "country", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Country", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Country", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "default_distance_unit",
+   "fieldtype": "Link",
+   "label": "Default Distance Unit",
+   "options": "UOM"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "", 
-   "fieldname": "default_distance_unit", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Default Distance Unit", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "UOM", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_8",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_8", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "INR",
+   "fieldname": "default_currency",
+   "fieldtype": "Link",
+   "ignore_user_permissions": 1,
+   "in_list_view": 1,
+   "label": "Default Currency",
+   "options": "Currency",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "INR", 
-   "fieldname": "default_currency", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 1, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Default Currency", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Currency", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "description": "Do not show any symbol like $ etc next to currencies.",
+   "fieldname": "hide_currency_symbol",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Hide Currency Symbol",
+   "options": "\nNo\nYes"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "Do not show any symbol like $ etc next to currencies.", 
-   "fieldname": "hide_currency_symbol", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Hide Currency Symbol", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nNo\nYes", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "description": "If disable, 'Rounded Total' field will not be visible in any transaction",
+   "fieldname": "disable_rounded_total",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Disable Rounded Total"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "If disable, 'Rounded Total' field will not be visible in any transaction", 
-   "fieldname": "disable_rounded_total", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Disable Rounded Total", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "If disable, 'In Words' field will not be visible in any transaction", 
-   "fieldname": "disable_in_words", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Disable In Words", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "default": "0",
+   "description": "If disable, 'In Words' field will not be visible in any transaction",
+   "fieldname": "disable_in_words",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Disable In Words"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "icon": "fa fa-cog", 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 1, 
- "is_submittable": 0, 
- "issingle": 1, 
- "istable": 0, 
- "max_attachments": 0, 
- "menu_index": 0, 
- "modified": "2018-10-15 03:08:19.886212", 
- "modified_by": "Administrator", 
- "module": "Setup", 
- "name": "Global Defaults", 
- "owner": "Administrator", 
+ ],
+ "icon": "fa fa-cog",
+ "idx": 1,
+ "in_create": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2023-07-01 19:45:00.323953",
+ "modified_by": "Administrator",
+ "module": "Setup",
+ "name": "Global Defaults",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 0, 
-   "email": 0, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 0, 
-   "read": 1, 
-   "report": 0, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "read": 1,
+   "role": "System Manager",
+   "share": 1,
    "write": 1
   }
- ], 
- "quick_entry": 0, 
- "read_only": 1, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "read_only": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py
index 16e9434..fc80483 100644
--- a/erpnext/setup/doctype/global_defaults/global_defaults.py
+++ b/erpnext/setup/doctype/global_defaults/global_defaults.py
@@ -10,7 +10,6 @@
 
 keydict = {
 	# "key in defaults": "key in Global Defaults"
-	"fiscal_year": "current_fiscal_year",
 	"company": "default_company",
 	"currency": "default_currency",
 	"country": "country",
@@ -29,22 +28,6 @@
 		for key in keydict:
 			frappe.db.set_default(key, self.get(keydict[key], ""))
 
-		# update year start date and year end date from fiscal_year
-		if self.current_fiscal_year:
-			if fiscal_year := frappe.get_all(
-				"Fiscal Year",
-				filters={"name": self.current_fiscal_year},
-				fields=["year_start_date", "year_end_date"],
-				limit=1,
-				order_by=None,
-			):
-				ysd = fiscal_year[0].year_start_date or ""
-				yed = fiscal_year[0].year_end_date or ""
-
-				if ysd and yed:
-					frappe.db.set_default("year_start_date", ysd.strftime("%Y-%m-%d"))
-					frappe.db.set_default("year_end_date", yed.strftime("%Y-%m-%d"))
-
 		# enable default currency
 		if self.default_currency:
 			frappe.db.set_value("Currency", self.default_currency, "enabled", 1)
diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.js b/erpnext/setup/doctype/holiday_list/holiday_list.js
index ea033c7..90d9f1b 100644
--- a/erpnext/setup/doctype/holiday_list/holiday_list.js
+++ b/erpnext/setup/doctype/holiday_list/holiday_list.js
@@ -6,13 +6,41 @@
 		if (frm.doc.holidays) {
 			frm.set_value("total_holidays", frm.doc.holidays.length);
 		}
+
+		frm.call("get_supported_countries").then(r => {
+			frm.subdivisions_by_country = r.message.subdivisions_by_country;
+			frm.fields_dict.country.set_data(
+				r.message.countries.sort((a, b) => a.label.localeCompare(b.label))
+			);
+
+			if (frm.doc.country) {
+				frm.trigger("set_subdivisions");
+			}
+		});
 	},
 	from_date: function(frm) {
 		if (frm.doc.from_date && !frm.doc.to_date) {
 			var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12);
 			frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1));
 		}
-	}
+	},
+	country: function(frm) {
+		frm.set_value("subdivision", "");
+
+		if (frm.doc.country) {
+			frm.trigger("set_subdivisions");
+		}
+	},
+	set_subdivisions: function(frm) {
+		const subdivisions = [...frm.subdivisions_by_country[frm.doc.country]];
+		if (subdivisions && subdivisions.length > 0) {
+			frm.fields_dict.subdivision.set_data(subdivisions);
+			frm.set_df_property("subdivision", "hidden", 0);
+		} else {
+			frm.fields_dict.subdivision.set_data([]);
+			frm.set_df_property("subdivision", "hidden", 1);
+		}
+	},
 });
 
 frappe.tour["Holiday List"] = [
diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.json b/erpnext/setup/doctype/holiday_list/holiday_list.json
index 4bbe6a6..45671d1 100644
--- a/erpnext/setup/doctype/holiday_list/holiday_list.json
+++ b/erpnext/setup/doctype/holiday_list/holiday_list.json
@@ -1,480 +1,166 @@
 {
- "allow_copy": 0,
- "allow_guest_to_view": 0,
+ "actions": [],
  "allow_import": 1,
  "allow_rename": 1,
  "autoname": "field:holiday_list_name",
- "beta": 0,
  "creation": "2013-01-10 16:34:14",
- "custom": 0,
- "docstatus": 0,
  "doctype": "DocType",
  "document_type": "Setup",
- "editable_grid": 0,
  "engine": "InnoDB",
+ "field_order": [
+  "holiday_list_name",
+  "from_date",
+  "to_date",
+  "column_break_4",
+  "total_holidays",
+  "add_weekly_holidays",
+  "weekly_off",
+  "get_weekly_off_dates",
+  "add_local_holidays",
+  "country",
+  "subdivision",
+  "get_local_holidays",
+  "holidays_section",
+  "holidays",
+  "clear_table",
+  "section_break_9",
+  "color"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "holiday_list_name",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Holiday List Name",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "holiday_list_name",
    "oldfieldtype": "Data",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
    "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
    "unique": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "from_date",
    "fieldtype": "Date",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
    "in_list_view": 1,
-   "in_standard_filter": 0,
    "label": "From Date",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "to_date",
    "fieldtype": "Date",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
    "in_list_view": 1,
-   "in_standard_filter": 0,
    "label": "To Date",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "column_break_4",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "total_holidays",
    "fieldtype": "Int",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
    "in_list_view": 1,
-   "in_standard_filter": 0,
    "label": "Total Holidays",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "read_only": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
    "collapsible": 1,
-   "columns": 0,
+   "depends_on": "eval: doc.from_date && doc.to_date",
    "fieldname": "add_weekly_holidays",
    "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Add Weekly Holidays",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Add Weekly Holidays"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "weekly_off",
    "fieldtype": "Select",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
    "in_standard_filter": 1,
    "label": "Weekly Off",
-   "length": 0,
    "no_copy": 1,
    "options": "\nSunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday",
-   "permlevel": 0,
    "print_hide": 1,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 1,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "report_hide": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "get_weekly_off_dates",
    "fieldtype": "Button",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Add to Holidays",
-   "length": 0,
-   "no_copy": 0,
-   "options": "get_weekly_off_dates",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "get_weekly_off_dates"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "holidays_section",
    "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Holidays",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Holidays"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "holidays",
    "fieldtype": "Table",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Holidays",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "holiday_list_details",
    "oldfieldtype": "Table",
-   "options": "Holiday",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Holiday"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "clear_table",
    "fieldtype": "Button",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Clear Table",
-   "length": 0,
-   "no_copy": 0,
-   "options": "clear_table",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "clear_table"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "section_break_9",
-   "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Section Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "color",
    "fieldtype": "Color",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Color",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 1,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "print_hide": 1
+  },
+  {
+   "fieldname": "country",
+   "fieldtype": "Autocomplete",
+   "label": "Country"
+  },
+  {
+   "depends_on": "country",
+   "fieldname": "subdivision",
+   "fieldtype": "Autocomplete",
+   "label": "Subdivision"
+  },
+  {
+   "collapsible": 1,
+   "depends_on": "eval: doc.from_date && doc.to_date",
+   "fieldname": "add_local_holidays",
+   "fieldtype": "Section Break",
+   "label": "Add Local Holidays"
+  },
+  {
+   "fieldname": "get_local_holidays",
+   "fieldtype": "Button",
+   "label": "Add to Holidays",
+   "options": "get_local_holidays"
   }
  ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
  "icon": "fa fa-calendar",
  "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-07-03 07:22:46.474096",
+ "links": [],
+ "modified": "2023-07-14 13:28:53.156421",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Holiday List",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0,
-   "cancel": 0,
    "create": 1,
    "delete": 1,
    "email": 1,
-   "export": 0,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
    "role": "HR Manager",
-   "set_user_permissions": 0,
    "share": 1,
-   "submit": 0,
    "write": 1
   }
  ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
  "sort_field": "modified",
  "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py
index 84d0d35..2ef4e65 100644
--- a/erpnext/setup/doctype/holiday_list/holiday_list.py
+++ b/erpnext/setup/doctype/holiday_list/holiday_list.py
@@ -3,11 +3,15 @@
 
 
 import json
+from datetime import date
 
 import frappe
+from babel import Locale
 from frappe import _, throw
 from frappe.model.document import Document
-from frappe.utils import cint, formatdate, getdate, today
+from frappe.utils import formatdate, getdate, today
+from holidays import country_holidays
+from holidays.utils import list_supported_countries
 
 
 class OverlapError(frappe.ValidationError):
@@ -21,25 +25,66 @@
 
 	@frappe.whitelist()
 	def get_weekly_off_dates(self):
-		self.validate_values()
-		date_list = self.get_weekly_off_date_list(self.from_date, self.to_date)
-		last_idx = max(
-			[cint(d.idx) for d in self.get("holidays")]
-			or [
-				0,
-			]
-		)
-		for i, d in enumerate(date_list):
-			ch = self.append("holidays", {})
-			ch.description = _(self.weekly_off)
-			ch.holiday_date = d
-			ch.weekly_off = 1
-			ch.idx = last_idx + i + 1
-
-	def validate_values(self):
 		if not self.weekly_off:
 			throw(_("Please select weekly off day"))
 
+		existing_holidays = self.get_holidays()
+
+		for d in self.get_weekly_off_date_list(self.from_date, self.to_date):
+			if d in existing_holidays:
+				continue
+
+			self.append("holidays", {"description": _(self.weekly_off), "holiday_date": d, "weekly_off": 1})
+
+		self.sort_holidays()
+
+	@frappe.whitelist()
+	def get_supported_countries(self):
+		subdivisions_by_country = list_supported_countries()
+		countries = [
+			{"value": country, "label": local_country_name(country)}
+			for country in subdivisions_by_country.keys()
+		]
+		return {
+			"countries": countries,
+			"subdivisions_by_country": subdivisions_by_country,
+		}
+
+	@frappe.whitelist()
+	def get_local_holidays(self):
+		if not self.country:
+			throw(_("Please select a country"))
+
+		existing_holidays = self.get_holidays()
+		from_date = getdate(self.from_date)
+		to_date = getdate(self.to_date)
+
+		for holiday_date, holiday_name in country_holidays(
+			self.country,
+			subdiv=self.subdivision,
+			years=[from_date.year, to_date.year],
+			language=frappe.local.lang,
+		).items():
+			if holiday_date in existing_holidays:
+				continue
+
+			if holiday_date < from_date or holiday_date > to_date:
+				continue
+
+			self.append(
+				"holidays", {"description": holiday_name, "holiday_date": holiday_date, "weekly_off": 0}
+			)
+
+		self.sort_holidays()
+
+	def sort_holidays(self):
+		self.holidays.sort(key=lambda x: getdate(x.holiday_date))
+		for i in range(len(self.holidays)):
+			self.holidays[i].idx = i + 1
+
+	def get_holidays(self) -> list[date]:
+		return [getdate(holiday.holiday_date) for holiday in self.holidays]
+
 	def validate_days(self):
 		if getdate(self.from_date) > getdate(self.to_date):
 			throw(_("To Date cannot be before From Date"))
@@ -120,3 +165,8 @@
 		)
 	else:
 		return False
+
+
+def local_country_name(country_code: str) -> str:
+	"""Return the localized country name for the given country code."""
+	return Locale.parse(frappe.local.lang).territories.get(country_code, country_code)
diff --git a/erpnext/setup/doctype/holiday_list/test_holiday_list.py b/erpnext/setup/doctype/holiday_list/test_holiday_list.py
index d32cfe8..23b08fd 100644
--- a/erpnext/setup/doctype/holiday_list/test_holiday_list.py
+++ b/erpnext/setup/doctype/holiday_list/test_holiday_list.py
@@ -3,7 +3,7 @@
 
 import unittest
 from contextlib import contextmanager
-from datetime import timedelta
+from datetime import date, timedelta
 
 import frappe
 from frappe.utils import getdate
@@ -23,6 +23,41 @@
 		fetched_holiday_list = frappe.get_value("Holiday List", holiday_list.name)
 		self.assertEqual(holiday_list.name, fetched_holiday_list)
 
+	def test_weekly_off(self):
+		holiday_list = frappe.new_doc("Holiday List")
+		holiday_list.from_date = "2023-01-01"
+		holiday_list.to_date = "2023-02-28"
+		holiday_list.weekly_off = "Sunday"
+		holiday_list.get_weekly_off_dates()
+
+		holidays = [holiday.holiday_date for holiday in holiday_list.holidays]
+
+		self.assertNotIn(date(2022, 12, 25), holidays)
+		self.assertIn(date(2023, 1, 1), holidays)
+		self.assertIn(date(2023, 1, 8), holidays)
+		self.assertIn(date(2023, 1, 15), holidays)
+		self.assertIn(date(2023, 1, 22), holidays)
+		self.assertIn(date(2023, 1, 29), holidays)
+		self.assertIn(date(2023, 2, 5), holidays)
+		self.assertIn(date(2023, 2, 12), holidays)
+		self.assertIn(date(2023, 2, 19), holidays)
+		self.assertIn(date(2023, 2, 26), holidays)
+		self.assertNotIn(date(2023, 3, 5), holidays)
+
+	def test_local_holidays(self):
+		holiday_list = frappe.new_doc("Holiday List")
+		holiday_list.from_date = "2023-04-01"
+		holiday_list.to_date = "2023-04-30"
+		holiday_list.country = "DE"
+		holiday_list.subdivision = "SN"
+		holiday_list.get_local_holidays()
+
+		holidays = [holiday.holiday_date for holiday in holiday_list.holidays]
+		self.assertNotIn(date(2023, 1, 1), holidays)
+		self.assertIn(date(2023, 4, 7), holidays)
+		self.assertIn(date(2023, 4, 10), holidays)
+		self.assertNotIn(date(2023, 5, 1), holidays)
+
 
 def make_holiday_list(
 	name, from_date=getdate() - timedelta(days=10), to_date=getdate(), holiday_dates=None
diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.js b/erpnext/setup/doctype/sales_partner/sales_partner.js
index 5656d43..f9e3770 100644
--- a/erpnext/setup/doctype/sales_partner/sales_partner.js
+++ b/erpnext/setup/doctype/sales_partner/sales_partner.js
@@ -3,8 +3,6 @@
 
 frappe.ui.form.on('Sales Partner', {
 	refresh: function(frm) {
-		frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Sales Partner'}
-
 		if(frm.doc.__islocal){
 			hide_field(['address_html', 'contact_html', 'address_contacts']);
 			frappe.contacts.clear_address_and_contact(frm);
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index 8e61fe2..535c87d 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -462,11 +462,9 @@
 
 def set_global_defaults(args):
 	global_defaults = frappe.get_doc("Global Defaults", "Global Defaults")
-	current_fiscal_year = frappe.get_all("Fiscal Year")[0]
 
 	global_defaults.update(
 		{
-			"current_fiscal_year": current_fiscal_year.name,
 			"default_currency": args.get("currency"),
 			"default_company": args.get("company_name"),
 			"country": args.get("country"),
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 8baae8a..0ef3027 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -318,6 +318,37 @@
 		self.assertEqual(dn.per_returned, 100)
 		self.assertEqual(dn.status, "Return Issued")
 
+	def test_delivery_note_return_valuation_on_different_warehuose(self):
+		from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
+		item_code = "Test Return Valuation For DN"
+		make_item("Test Return Valuation For DN", {"is_stock_item": 1})
+		return_warehouse = create_warehouse("Returned Test Warehouse", company=company)
+
+		make_stock_entry(item_code=item_code, target="Stores - TCP1", qty=5, basic_rate=150)
+
+		dn = create_delivery_note(
+			item_code=item_code,
+			qty=5,
+			rate=500,
+			warehouse="Stores - TCP1",
+			company=company,
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+		)
+
+		dn.submit()
+		self.assertEqual(dn.items[0].incoming_rate, 150)
+
+		from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
+		return_dn = make_return_doc(dn.doctype, dn.name)
+		return_dn.items[0].warehouse = return_warehouse
+		return_dn.save().submit()
+
+		self.assertEqual(return_dn.items[0].incoming_rate, 150)
+
 	def test_return_single_item_from_bundled_items(self):
 		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 34adbeb..87c2a7e 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -194,7 +194,8 @@
    "default": "0",
    "fieldname": "disabled",
    "fieldtype": "Check",
-   "label": "Disabled"
+   "label": "Disabled",
+   "search_index": 1
   },
   {
    "default": "0",
@@ -911,7 +912,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "make_attachments_public": 1,
- "modified": "2023-02-14 04:48:26.343620",
+ "modified": "2023-07-14 17:18:18.658942",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item",
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 93d799a..ef4155e 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -773,7 +773,7 @@
 
 		rows = ""
 		for docname, attr_list in not_included.items():
-			link = "<a href='/app/Form/Item/{0}'>{0}</a>".format(frappe.bold(_(docname)))
+			link = f"<a href='/app/item/{docname}'>{frappe.bold(docname)}</a>"
 			rows += table_row(link, body(attr_list))
 
 		error_description = _(
diff --git a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json
index 6d02ea9..9699ecb 100644
--- a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json
+++ b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json
@@ -1,370 +1,90 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "", 
- "beta": 0, 
- "creation": "2015-05-19 05:12:30.344797", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Other", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2015-05-19 05:12:30.344797",
+ "doctype": "DocType",
+ "document_type": "Other",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "variant_of",
+  "attribute",
+  "column_break_2",
+  "attribute_value",
+  "numeric_values",
+  "section_break_4",
+  "from_range",
+  "increment",
+  "column_break_8",
+  "to_range"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "variant_of", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Variant Of", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "variant_of",
+   "fieldtype": "Link",
+   "label": "Variant Of",
+   "options": "Item",
+   "search_index": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "attribute", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Attribute", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Item Attribute", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "attribute",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Attribute",
+   "options": "Item Attribute",
+   "reqd": 1,
+   "search_index": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "attribute_value", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Attribute Value", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "attribute_value",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Attribute Value"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "has_variants", 
-   "fieldname": "numeric_values", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Numeric Values", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "depends_on": "has_variants",
+   "fieldname": "numeric_values",
+   "fieldtype": "Check",
+   "label": "Numeric Values"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "numeric_values", 
-   "fieldname": "section_break_4", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "numeric_values",
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "from_range", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "From Range", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "from_range",
+   "fieldtype": "Float",
+   "label": "From Range"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "increment", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Increment", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "increment",
+   "fieldtype": "Float",
+   "label": "Increment"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_8", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_8",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "to_range", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "To Range", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fieldname": "to_range",
+   "fieldtype": "Float",
+   "label": "To Range"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "icon": "", 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2019-01-03 15:36:59.129006", 
- "modified_by": "Administrator", 
- "module": "Stock", 
- "name": "Item Variant Attribute", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2023-07-14 17:15:19.112119",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Item Variant Attribute",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.js b/erpnext/stock/doctype/manufacturer/manufacturer.js
index bb7e314..5b4990f 100644
--- a/erpnext/stock/doctype/manufacturer/manufacturer.js
+++ b/erpnext/stock/doctype/manufacturer/manufacturer.js
@@ -3,7 +3,6 @@
 
 frappe.ui.form.on('Manufacturer', {
 	refresh: function(frm) {
-		frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' };
 		if (frm.doc.__islocal) {
 			hide_field(['address_html','contact_html']);
 			frappe.contacts.clear_address_and_contact(frm);
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index ee247fd..00b1b20 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -118,8 +118,8 @@
 			self.title = _("{0} Request for {1}").format(_(self.material_request_type), items)[:100]
 
 	def on_submit(self):
-		self.update_requested_qty()
 		self.update_requested_qty_in_production_plan()
+		self.update_requested_qty()
 		if self.material_request_type == "Purchase":
 			self.validate_budget()
 
@@ -178,8 +178,8 @@
 				)
 
 	def on_cancel(self):
-		self.update_requested_qty()
 		self.update_requested_qty_in_production_plan()
+		self.update_requested_qty()
 
 	def get_mr_items_ordered_qty(self, mr_items):
 		mr_items_ordered_qty = {}
@@ -270,7 +270,13 @@
 				item_wh_list.append([d.item_code, d.warehouse])
 
 		for item_code, warehouse in item_wh_list:
-			update_bin_qty(item_code, warehouse, {"indented_qty": get_indented_qty(item_code, warehouse)})
+			update_bin_qty(
+				item_code,
+				warehouse,
+				{
+					"indented_qty": get_indented_qty(item_code, warehouse),
+				},
+			)
 
 	def update_requested_qty_in_production_plan(self):
 		production_plans = []
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index ced8946..6134bfa 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -1965,6 +1965,32 @@
 		ste5.reload()
 		self.assertEqual(ste5.items[0].valuation_rate, 275.00)
 
+		ste6 = make_stock_entry(
+			purpose="Material Transfer",
+			posting_date=add_days(today(), -3),
+			source=warehouse1,
+			target=warehouse,
+			item_code=item_code,
+			qty=20,
+			company=pr.company,
+		)
+
+		ste6.reload()
+		self.assertEqual(ste6.items[0].valuation_rate, 275.00)
+
+		ste7 = make_stock_entry(
+			purpose="Material Transfer",
+			posting_date=add_days(today(), -3),
+			source=warehouse,
+			target=warehouse1,
+			item_code=item_code,
+			qty=20,
+			company=pr.company,
+		)
+
+		ste7.reload()
+		self.assertEqual(ste7.items[0].valuation_rate, 275.00)
+
 		create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=2500 * -1)
 
 		pr.reload()
@@ -1985,6 +2011,12 @@
 		ste5.reload()
 		self.assertEqual(ste5.items[0].valuation_rate, valuation_rate)
 
+		ste6.reload()
+		self.assertEqual(ste6.items[0].valuation_rate, valuation_rate)
+
+		ste7.reload()
+		self.assertEqual(ste7.items[0].valuation_rate, valuation_rate)
+
 
 def prepare_data_for_internal_transfer():
 	from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
index 6955c76..c5b96ff 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
@@ -193,7 +193,7 @@
    "fieldname": "naming_series",
    "fieldtype": "Select",
    "label": "Naming Series",
-   "options": "SBB-.####"
+   "options": "SABB-.########"
   },
   {
    "default": "0",
@@ -244,7 +244,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-04-10 20:02:42.964309",
+ "modified": "2023-07-16 10:53:04.045605",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Serial and Batch Bundle",
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 0c6d33b..43bd7ac 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
@@ -889,13 +889,16 @@
 
 
 @frappe.whitelist()
-def get_serial_batch_ledgers(item_code, docstatus=None, voucher_no=None, name=None):
-	filters = get_filters_for_bundle(item_code, docstatus=docstatus, voucher_no=voucher_no, name=name)
+def get_serial_batch_ledgers(item_code=None, docstatus=None, voucher_no=None, name=None):
+	filters = get_filters_for_bundle(
+		item_code=item_code, docstatus=docstatus, voucher_no=voucher_no, name=name
+	)
 
 	return frappe.get_all(
 		"Serial and Batch Bundle",
 		fields=[
 			"`tabSerial and Batch Bundle`.`name`",
+			"`tabSerial and Batch Bundle`.`item_code`",
 			"`tabSerial and Batch Entry`.`qty`",
 			"`tabSerial and Batch Entry`.`warehouse`",
 			"`tabSerial and Batch Entry`.`batch_no`",
@@ -906,12 +909,14 @@
 	)
 
 
-def get_filters_for_bundle(item_code, docstatus=None, voucher_no=None, name=None):
+def get_filters_for_bundle(item_code=None, docstatus=None, voucher_no=None, name=None):
 	filters = [
-		["Serial and Batch Bundle", "item_code", "=", item_code],
 		["Serial and Batch Bundle", "is_cancelled", "=", 0],
 	]
 
+	if item_code:
+		filters.append(["Serial and Batch Bundle", "item_code", "=", item_code])
+
 	if not docstatus:
 		docstatus = [0, 1]
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 403e04a..3e83faf 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -925,6 +925,7 @@
 		this.toggle_related_fields(this.frm.doc);
 		this.toggle_enable_bom();
 		this.show_stock_ledger();
+		erpnext.utils.view_serial_batch_nos(this.frm);
 		if (this.frm.doc.docstatus===1 && erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
 			this.show_general_ledger();
 		}
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index d9b5503..0059a3f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -420,7 +420,7 @@
 						transferred_materials = frappe.db.sql(
 							"""
 									select
-										sum(qty) as qty
+										sum(sed.qty) as qty
 									from `tabStock Entry` se,`tabStock Entry Detail` sed
 									where
 										se.name = sed.parent and se.docstatus=1 and
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 0664c29..cb2adf1 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -337,6 +337,7 @@
 	refresh() {
 		if(this.frm.doc.docstatus > 0) {
 			this.show_stock_ledger();
+			erpnext.utils.view_serial_batch_nos(this.frm);
 			if (erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
 				this.show_general_ledger();
 			}
diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js
index 746a1cb..3819c0b2 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.js
+++ b/erpnext/stock/doctype/warehouse/warehouse.js
@@ -83,12 +83,6 @@
 		}
 
 		frm.toggle_enable(["is_group", "company"], false);
-
-		frappe.dynamic_link = {
-			doc: frm.doc,
-			fieldname: "name",
-			doctype: "Warehouse",
-		};
 	},
 });
 
diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js
index a7d7149..48a72a2 100644
--- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js
+++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js
@@ -9,13 +9,27 @@
 			"fieldtype": "Date",
 			"width": "80",
 			"default": frappe.sys_defaults.year_start_date,
+			"reqd": 1,
 		},
 		{
 			"fieldname":"to_date",
 			"label": __("To Date"),
 			"fieldtype": "Date",
 			"width": "80",
-			"default": frappe.datetime.get_today()
+			"default": frappe.datetime.get_today(),
+			"reqd": 1,
+		},
+		{
+			"fieldname":"item",
+			"label": __("Item"),
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": "100",
+			"get_query": function () {
+				return {
+					filters: {"has_batch_no": 1}
+				}
+			}
 		}
 	]
 }
diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
index ef7d6e6..5661e8b 100644
--- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
+++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
@@ -4,113 +4,86 @@
 
 import frappe
 from frappe import _
-from frappe.query_builder.functions import IfNull
-from frappe.utils import cint, getdate
+from frappe.query_builder.functions import Date
 
 
 def execute(filters=None):
-	if not filters:
-		filters = {}
+	validate_filters(filters)
 
-	float_precision = cint(frappe.db.get_default("float_precision")) or 3
-
-	columns = get_columns(filters)
-	item_map = get_item_details(filters)
-	iwb_map = get_item_warehouse_batch_map(filters, float_precision)
-
-	data = []
-	for item in sorted(iwb_map):
-		for wh in sorted(iwb_map[item]):
-			for batch in sorted(iwb_map[item][wh]):
-				qty_dict = iwb_map[item][wh][batch]
-
-				data.append(
-					[
-						item,
-						item_map[item]["item_name"],
-						item_map[item]["description"],
-						wh,
-						batch,
-						frappe.db.get_value("Batch", batch, "expiry_date"),
-						qty_dict.expiry_status,
-					]
-				)
+	columns = get_columns()
+	data = get_data(filters)
 
 	return columns, data
 
 
-def get_columns(filters):
-	"""return columns based on filters"""
+def validate_filters(filters):
+	if not filters:
+		frappe.throw(_("Please select the required filters"))
 
-	columns = (
-		[_("Item") + ":Link/Item:100"]
-		+ [_("Item Name") + "::150"]
-		+ [_("Description") + "::150"]
-		+ [_("Warehouse") + ":Link/Warehouse:100"]
-		+ [_("Batch") + ":Link/Batch:100"]
-		+ [_("Expires On") + ":Date:90"]
-		+ [_("Expiry (In Days)") + ":Int:120"]
-	)
-
-	return columns
-
-
-def get_stock_ledger_entries(filters):
 	if not filters.get("from_date"):
 		frappe.throw(_("'From Date' is required"))
 
 	if not filters.get("to_date"):
 		frappe.throw(_("'To Date' is required"))
 
-	sle = frappe.qb.DocType("Stock Ledger Entry")
-	query = (
-		frappe.qb.from_(sle)
-		.select(sle.item_code, sle.batch_no, sle.warehouse, sle.posting_date, sle.actual_qty)
-		.where(
-			(sle.is_cancelled == 0)
-			& (sle.docstatus < 2)
-			& (IfNull(sle.batch_no, "") != "")
-			& (sle.posting_date <= filters["to_date"])
-		)
-		.orderby(sle.item_code, sle.warehouse)
+
+def get_columns():
+	return (
+		[_("Item") + ":Link/Item:150"]
+		+ [_("Item Name") + "::150"]
+		+ [_("Batch") + ":Link/Batch:150"]
+		+ [_("Stock UOM") + ":Link/UOM:100"]
+		+ [_("Quantity") + ":Float:100"]
+		+ [_("Expires On") + ":Date:100"]
+		+ [_("Expiry (In Days)") + ":Int:130"]
 	)
 
-	return query.run(as_dict=True)
 
+def get_data(filters):
+	data = []
 
-def get_item_warehouse_batch_map(filters, float_precision):
-	sle = get_stock_ledger_entries(filters)
-	iwb_map = {}
-
-	from_date = getdate(filters["from_date"])
-	to_date = getdate(filters["to_date"])
-
-	for d in sle:
-		iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {}).setdefault(
-			d.batch_no, frappe._dict({"expires_on": None, "expiry_status": None})
+	for batch in get_batch_details(filters):
+		data.append(
+			[
+				batch.item,
+				batch.item_name,
+				batch.name,
+				batch.stock_uom,
+				batch.batch_qty,
+				batch.expiry_date,
+				max((batch.expiry_date - frappe.utils.datetime.date.today()).days, 0)
+				if batch.expiry_date
+				else None,
+			]
 		)
 
-		qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no]
-
-		expiry_date_unicode = frappe.db.get_value("Batch", d.batch_no, "expiry_date")
-		qty_dict.expires_on = expiry_date_unicode
-
-		exp_date = frappe.utils.data.getdate(expiry_date_unicode)
-		qty_dict.expires_on = exp_date
-
-		expires_in_days = (exp_date - frappe.utils.datetime.date.today()).days
-
-		if expires_in_days > 0:
-			qty_dict.expiry_status = expires_in_days
-		else:
-			qty_dict.expiry_status = 0
-
-	return iwb_map
+	return data
 
 
-def get_item_details(filters):
-	item_map = {}
-	for d in (frappe.qb.from_("Item").select("name", "item_name", "description")).run(as_dict=True):
-		item_map.setdefault(d.name, d)
+def get_batch_details(filters):
+	batch = frappe.qb.DocType("Batch")
+	query = (
+		frappe.qb.from_(batch)
+		.select(
+			batch.name,
+			batch.creation,
+			batch.expiry_date,
+			batch.item,
+			batch.item_name,
+			batch.stock_uom,
+			batch.batch_qty,
+		)
+		.where(
+			(batch.disabled == 0)
+			& (batch.batch_qty > 0)
+			& (
+				(Date(batch.creation) >= filters["from_date"]) & (Date(batch.creation) <= filters["to_date"])
+			)
+		)
+		.orderby(batch.creation)
+	)
 
-	return item_map
+	if filters.get("item"):
+		query = query.where(batch.item == filters["item"])
+
+	return query.run(as_dict=True)
diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
index c072874..e7d3e20 100644
--- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
+++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
@@ -10,11 +10,18 @@
 
 from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter
 
+SLE_COUNT_LIMIT = 10_000
+
 
 def execute(filters=None):
 	if not filters:
 		filters = {}
 
+	sle_count = frappe.db.count("Stock Ledger Entry", {"is_cancelled": 0})
+
+	if sle_count > SLE_COUNT_LIMIT and not filters.get("item_code") and not filters.get("warehouse"):
+		frappe.throw(_("Please select either the Item or Warehouse filter to generate the report."))
+
 	if filters.from_date > filters.to_date:
 		frappe.throw(_("From Date must be before To Date"))
 
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py b/erpnext/stock/report/serial_and_batch_summary/__init__.py
similarity index 100%
copy from erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py
copy to erpnext/stock/report/serial_and_batch_summary/__init__.py
diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.js b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.js
new file mode 100644
index 0000000..10e5925
--- /dev/null
+++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.js
@@ -0,0 +1,95 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Serial and Batch Summary"] = {
+	"filters": [
+		{
+			"fieldname":"company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"default": frappe.defaults.get_user_default("Company"),
+		},
+		{
+			"fieldname":"from_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+		},
+		{
+			"fieldname":"to_date",
+			"label": __("To Date"),
+			"fieldtype": "Date",
+			"default": frappe.datetime.get_today()
+		},
+		{
+			"fieldname":"item_code",
+			"label": __("Item"),
+			"fieldtype": "Link",
+			"options": "Item",
+		},
+		{
+			"fieldname":"warehouse",
+			"label": __("Warehouse"),
+			"fieldtype": "Link",
+			"options": "Warehouse",
+		},
+		{
+			"fieldname":"voucher_type",
+			"label": __("Voucher Type"),
+			"fieldtype": "Link",
+			"options": "DocType",
+			get_query: function() {
+				return {
+					query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_voucher_type",
+				}
+			}
+		},
+		{
+			"fieldname":"voucher_no",
+			"label": __("Voucher No"),
+			"fieldtype": "MultiSelectList",
+			get_data: function(txt) {
+				if (!frappe.query_report.filters) return;
+
+				let voucher_type = frappe.query_report.get_filter_value('voucher_type');
+				if (!voucher_type) return;
+
+				return frappe.db.get_link_options(voucher_type, txt);
+			},
+		},
+		{
+			"fieldname":"serial_no",
+			"label": __("Serial No"),
+			"fieldtype": "Link",
+			"options": "Serial No",
+			get_query: function() {
+				return {
+					query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_serial_nos",
+					filters: {
+						"item_code": frappe.query_report.get_filter_value('item_code'),
+						"voucher_type": frappe.query_report.get_filter_value('voucher_type'),
+						"voucher_no": frappe.query_report.get_filter_value('voucher_no'),
+					}
+				}
+			}
+		},
+		{
+			"fieldname":"batch_no",
+			"label": __("Batch No"),
+			"fieldtype": "Link",
+			"options": "Batch",
+			get_query: function() {
+				return {
+					query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_batch_nos",
+					filters: {
+						"item_code": frappe.query_report.get_filter_value('item_code'),
+						"voucher_type": frappe.query_report.get_filter_value('voucher_type'),
+						"voucher_no": frappe.query_report.get_filter_value('voucher_no'),
+					}
+				}
+			}
+		}
+	]
+};
diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.json b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.json
new file mode 100644
index 0000000..7511e3a
--- /dev/null
+++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.json
@@ -0,0 +1,38 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2023-07-13 16:53:27.735091",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "modified": "2023-07-13 16:53:33.204591",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Serial and Batch Summary",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Serial and Batch Bundle",
+ "report_name": "Serial and Batch Summary",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "System Manager"
+  },
+  {
+   "role": "Sales User"
+  },
+  {
+   "role": "Purchase User"
+  },
+  {
+   "role": "Stock User"
+  },
+  {
+   "role": "Maintenance User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py
new file mode 100644
index 0000000..3ea5e82
--- /dev/null
+++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py
@@ -0,0 +1,245 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+
+
+def execute(filters=None):
+	data = get_data(filters)
+	columns = get_columns(filters, data)
+
+	return columns, data
+
+
+def get_data(filters):
+	filter_conditions = get_filter_conditions(filters)
+
+	return frappe.get_all(
+		"Serial and Batch Bundle",
+		fields=[
+			"`tabSerial and Batch Bundle`.`voucher_type`",
+			"`tabSerial and Batch Bundle`.`posting_date`",
+			"`tabSerial and Batch Bundle`.`name`",
+			"`tabSerial and Batch Bundle`.`company`",
+			"`tabSerial and Batch Bundle`.`voucher_no`",
+			"`tabSerial and Batch Bundle`.`item_code`",
+			"`tabSerial and Batch Bundle`.`item_name`",
+			"`tabSerial and Batch Entry`.`serial_no`",
+			"`tabSerial and Batch Entry`.`batch_no`",
+			"`tabSerial and Batch Entry`.`warehouse`",
+			"`tabSerial and Batch Entry`.`incoming_rate`",
+			"`tabSerial and Batch Entry`.`stock_value_difference`",
+			"`tabSerial and Batch Entry`.`qty`",
+		],
+		filters=filter_conditions,
+		order_by="posting_date",
+	)
+
+
+def get_filter_conditions(filters):
+	filter_conditions = [
+		["Serial and Batch Bundle", "docstatus", "=", 1],
+		["Serial and Batch Bundle", "is_cancelled", "=", 0],
+	]
+
+	for field in ["voucher_type", "voucher_no", "item_code", "warehouse", "company"]:
+		if filters.get(field):
+			if field == "voucher_no":
+				filter_conditions.append(["Serial and Batch Bundle", field, "in", filters.get(field)])
+			else:
+				filter_conditions.append(["Serial and Batch Bundle", field, "=", filters.get(field)])
+
+	if filters.get("from_date") and filters.get("to_date"):
+		filter_conditions.append(
+			[
+				"Serial and Batch Bundle",
+				"posting_date",
+				"between",
+				[filters.get("from_date"), filters.get("to_date")],
+			]
+		)
+
+	for field in ["serial_no", "batch_no"]:
+		if filters.get(field):
+			filter_conditions.append(["Serial and Batch Entry", field, "=", filters.get(field)])
+
+	return filter_conditions
+
+
+def get_columns(filters, data):
+	columns = [
+		{
+			"label": _("Company"),
+			"fieldname": "company",
+			"fieldtype": "Link",
+			"options": "Company",
+			"width": 120,
+		},
+		{
+			"label": _("Serial and Batch Bundle"),
+			"fieldname": "name",
+			"fieldtype": "Link",
+			"options": "Serial and Batch Bundle",
+			"width": 110,
+		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
+	]
+
+	item_details = {}
+
+	item_codes = []
+	if filters.get("voucher_type"):
+		item_codes = [d.item_code for d in data]
+
+	if filters.get("item_code") or (item_codes and len(list(set(item_codes))) == 1):
+		item_details = frappe.get_cached_value(
+			"Item",
+			filters.get("item_code") or item_codes[0],
+			["has_serial_no", "has_batch_no"],
+			as_dict=True,
+		)
+
+	if not filters.get("voucher_no"):
+		columns.extend(
+			[
+				{
+					"label": _("Voucher Type"),
+					"fieldname": "voucher_type",
+					"fieldtype": "Link",
+					"options": "DocType",
+					"width": 120,
+				},
+				{
+					"label": _("Voucher No"),
+					"fieldname": "voucher_no",
+					"fieldtype": "Dynamic Link",
+					"options": "voucher_type",
+					"width": 160,
+				},
+			]
+		)
+
+	if not filters.get("item_code"):
+		columns.extend(
+			[
+				{
+					"label": _("Item Code"),
+					"fieldname": "item_code",
+					"fieldtype": "Link",
+					"options": "Item",
+					"width": 120,
+				},
+				{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 120},
+			]
+		)
+
+	if not filters.get("warehouse"):
+		columns.append(
+			{
+				"label": _("Warehouse"),
+				"fieldname": "warehouse",
+				"fieldtype": "Link",
+				"options": "Warehouse",
+				"width": 120,
+			}
+		)
+
+	if not item_details or item_details.get("has_serial_no"):
+		columns.append(
+			{"label": _("Serial No"), "fieldname": "serial_no", "fieldtype": "Data", "width": 120}
+		)
+
+	if not item_details or item_details.get("has_batch_no"):
+		columns.extend(
+			[
+				{"label": _("Batch No"), "fieldname": "batch_no", "fieldtype": "Data", "width": 120},
+				{"label": _("Batch Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 120},
+			]
+		)
+
+	columns.extend(
+		[
+			{"label": _("Incoming Rate"), "fieldname": "incoming_rate", "fieldtype": "Float", "width": 120},
+			{
+				"label": _("Change in Stock Value"),
+				"fieldname": "stock_value_difference",
+				"fieldtype": "Float",
+				"width": 120,
+			},
+		]
+	)
+
+	return columns
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_voucher_type(doctype, txt, searchfield, start, page_len, filters):
+	child_doctypes = frappe.get_all(
+		"DocField",
+		filters={"fieldname": "serial_and_batch_bundle"},
+		fields=["distinct parent as parent"],
+	)
+
+	query_filters = {"options": ["in", [d.parent for d in child_doctypes]]}
+	if txt:
+		query_filters["parent"] = ["like", "%{}%".format(txt)]
+
+	return frappe.get_all("DocField", filters=query_filters, fields=["distinct parent"], as_list=True)
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_serial_nos(doctype, txt, searchfield, start, page_len, filters):
+	query_filters = {}
+
+	if txt:
+		query_filters["serial_no"] = ["like", f"%{txt}%"]
+
+	if filters.get("voucher_no"):
+		serial_batch_bundle = frappe.get_cached_value(
+			"Serial and Batch Bundle",
+			{"voucher_no": ("in", filters.get("voucher_no")), "docstatus": 1, "is_cancelled": 0},
+			"name",
+		)
+
+		query_filters["parent"] = serial_batch_bundle
+		if not txt:
+			query_filters["serial_no"] = ("is", "set")
+
+		return frappe.get_all(
+			"Serial and Batch Entry", filters=query_filters, fields=["serial_no"], as_list=True
+		)
+
+	else:
+		query_filters["item_code"] = filters.get("item_code")
+		return frappe.get_all("Serial No", filters=query_filters, as_list=True)
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_batch_nos(doctype, txt, searchfield, start, page_len, filters):
+	query_filters = {}
+
+	if txt:
+		query_filters["batch_no"] = ["like", f"%{txt}%"]
+
+	if filters.get("voucher_no"):
+		serial_batch_bundle = frappe.get_cached_value(
+			"Serial and Batch Bundle",
+			{"voucher_no": ("in", filters.get("voucher_no")), "docstatus": 1, "is_cancelled": 0},
+			"name",
+		)
+
+		query_filters["parent"] = serial_batch_bundle
+		if not txt:
+			query_filters["batch_no"] = ("is", "set")
+
+		return frappe.get_all(
+			"Serial and Batch Entry", filters=query_filters, fields=["batch_no"], as_list=True
+		)
+
+	else:
+		query_filters["item"] = filters.get("item_code")
+		return frappe.get_all("Batch", filters=query_filters, as_list=True)
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 7b1eae5..5abb8e8 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -645,7 +645,7 @@
 
 	def update_distinct_item_warehouses(self, dependant_sle):
 		key = (dependant_sle.item_code, dependant_sle.warehouse)
-		val = frappe._dict({"sle": dependant_sle})
+		val = frappe._dict({"sle": dependant_sle, "dependent_voucher_detail_nos": []})
 
 		if key not in self.distinct_item_warehouses:
 			self.distinct_item_warehouses[key] = val
@@ -654,13 +654,26 @@
 			existing_sle_posting_date = (
 				self.distinct_item_warehouses[key].get("sle", {}).get("posting_date")
 			)
+
+			dependent_voucher_detail_nos = self.get_dependent_voucher_detail_nos(key)
+
 			if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date):
 				val.sle_changed = True
 				self.distinct_item_warehouses[key] = val
 				self.new_items_found = True
-			elif self.distinct_item_warehouses[key].get("reposting_status"):
-				self.distinct_item_warehouses[key] = val
+			elif dependant_sle.voucher_detail_no not in set(dependent_voucher_detail_nos):
+				# Future dependent voucher needs to be repost to get the correct stock value
+				# If dependent voucher has not reposted, then add it to the list
+				dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
 				self.new_items_found = True
+				val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
+				self.distinct_item_warehouses[key] = val
+
+	def get_dependent_voucher_detail_nos(self, key):
+		if "dependent_voucher_detail_nos" not in self.distinct_item_warehouses[key]:
+			self.distinct_item_warehouses[key].dependent_voucher_detail_nos = []
+
+		return self.distinct_item_warehouses[key].dependent_voucher_detail_nos
 
 	def process_sle(self, sle):
 		# previous sle data for this warehouse
@@ -1370,6 +1383,7 @@
 			"qty_after_transaction",
 			"posting_date",
 			"posting_time",
+			"voucher_detail_no",
 			"timestamp(posting_date, posting_time) as timestamp",
 		],
 		as_dict=1,
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
index 2a078c4..6c9bc54 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -21,6 +21,7 @@
 	time_diff_in_seconds,
 	to_timedelta,
 )
+from frappe.utils.caching import redis_cache
 from frappe.utils.nestedset import get_ancestors_of
 from frappe.utils.safe_exec import get_safe_globals
 
@@ -209,6 +210,10 @@
 	def on_update(self):
 		set_documents_with_active_service_level_agreement()
 
+	def clear_cache(self):
+		get_sla_doctypes.clear_cache()
+		return super().clear_cache()
+
 	def create_docfields(self, meta, service_level_agreement_fields):
 		last_index = len(meta.fields)
 
@@ -990,6 +995,7 @@
 
 
 @frappe.whitelist()
+@redis_cache()
 def get_sla_doctypes():
 	doctypes = []
 	data = frappe.get_all("Service Level Agreement", {"enabled": 1}, ["document_type"], distinct=1)
@@ -998,3 +1004,7 @@
 		doctypes.append(entry.document_type)
 
 	return doctypes
+
+
+def add_sla_doctypes(bootinfo):
+	bootinfo.service_level_agreement_doctypes = get_sla_doctypes()
diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py
index 7725e71..1d6839c 100644
--- a/erpnext/telephony/doctype/call_log/call_log.py
+++ b/erpnext/telephony/doctype/call_log/call_log.py
@@ -24,12 +24,10 @@
 		lead_number = self.get("from") if self.is_incoming_call() else self.get("to")
 		lead_number = strip_number(lead_number)
 
-		contact = get_contact_with_phone_number(strip_number(lead_number))
-		if contact:
+		if contact := get_contact_with_phone_number(strip_number(lead_number)):
 			self.add_link(link_type="Contact", link_name=contact)
 
-		lead = get_lead_with_phone_number(lead_number)
-		if lead:
+		if lead := get_lead_with_phone_number(lead_number):
 			self.add_link(link_type="Lead", link_name=lead)
 
 		# Add Employee Name
@@ -70,28 +68,30 @@
 		self.append("links", {"link_doctype": link_type, "link_name": link_name})
 
 	def trigger_call_popup(self):
-		if self.is_incoming_call():
-			scheduled_employees = get_scheduled_employees_for_popup(self.medium)
-			employees = get_employees_with_number(self.to)
-			employee_emails = [employee.get("user_id") for employee in employees]
+		if not self.is_incoming_call():
+			return
 
-			# check if employees with matched number are scheduled to receive popup
-			emails = set(scheduled_employees).intersection(employee_emails)
+		scheduled_employees = get_scheduled_employees_for_popup(self.medium)
+		employees = get_employees_with_number(self.to)
+		employee_emails = [employee.get("user_id") for employee in employees]
 
-			if frappe.conf.developer_mode:
-				self.add_comment(
-					text=f"""
+		# check if employees with matched number are scheduled to receive popup
+		emails = set(scheduled_employees).intersection(employee_emails)
+
+		if frappe.conf.developer_mode:
+			self.add_comment(
+				text=f"""
 					Scheduled Employees: {scheduled_employees}
 					Matching Employee: {employee_emails}
 					Show Popup To: {emails}
 				"""
-				)
+			)
 
-			if employee_emails and not emails:
-				self.add_comment(text=_("No employee was scheduled for call popup"))
+		if employee_emails and not emails:
+			self.add_comment(text=_("No employee was scheduled for call popup"))
 
-			for email in emails:
-				frappe.publish_realtime("show_call_popup", self, user=email)
+		for email in emails:
+			frappe.publish_realtime("show_call_popup", self, user=email)
 
 	def update_received_by(self):
 		if employees := get_employees_with_number(self.get("to")):
@@ -154,8 +154,8 @@
 						ELSE 0
 					END
 				)=0
-			""",
-				dict(phone_number="%{}".format(number), docname=doc.name, doctype=doc.doctype),
+				""",
+				dict(phone_number=f"%{number}", docname=doc.name, doctype=doc.doctype),
 			)
 
 			for log in logs:
@@ -175,7 +175,7 @@
 		filters={"parenttype": "Call Log", "link_doctype": doctype, "link_name": docname},
 	)
 
-	logs = set([log.parent for log in logs])
+	logs = {log.parent for log in logs}
 
 	logs = frappe.get_all("Call Log", fields=["*"], filters={"name": ["in", logs]})
 
diff --git a/erpnext/templates/includes/itemised_tax_breakup.html b/erpnext/templates/includes/itemised_tax_breakup.html
index 5652bb1..fbc80de 100644
--- a/erpnext/templates/includes/itemised_tax_breakup.html
+++ b/erpnext/templates/includes/itemised_tax_breakup.html
@@ -15,7 +15,7 @@
 			{% for item, taxes in itemised_tax.items() %}
 				<tr>
 					<td>{{ item }}</td>
-					<td class='text-right'>
+					<td class="text-right">
 						{% if doc.get('is_return') %}
 							{{ frappe.utils.fmt_money((itemised_taxable_amount.get(item, 0))|abs, None, doc.currency) }}
 						{% else %}
@@ -25,7 +25,7 @@
 					{% for tax_account in tax_accounts %}
 						{% set tax_details = taxes.get(tax_account) %}
 						{% if tax_details %}
-							<td class='text-right'>
+							<td class="text-right">
 								{% if tax_details.tax_rate or not tax_details.tax_amount %}
 									({{ tax_details.tax_rate }}%)
 								{% endif %}
diff --git a/erpnext/tests/exotel_test_data.py b/erpnext/tests/exotel_test_data.py
deleted file mode 100644
index 3ad2575..0000000
--- a/erpnext/tests/exotel_test_data.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import frappe
-
-call_initiation_data = frappe._dict(
-	{
-		"CallSid": "23c162077629863c1a2d7f29263a162m",
-		"CallFrom": "09999999991",
-		"CallTo": "09999999980",
-		"Direction": "incoming",
-		"Created": "Wed, 23 Feb 2022 12:31:59",
-		"From": "09999999991",
-		"To": "09999999988",
-		"CurrentTime": "2022-02-23 12:32:02",
-		"DialWhomNumber": "09999999999",
-		"Status": "busy",
-		"EventType": "Dial",
-		"AgentEmail": "test_employee_exotel@company.com",
-	}
-)
-
-call_end_data = frappe._dict(
-	{
-		"CallSid": "23c162077629863c1a2d7f29263a162m",
-		"CallFrom": "09999999991",
-		"CallTo": "09999999980",
-		"Direction": "incoming",
-		"ForwardedFrom": "null",
-		"Created": "Wed, 23 Feb 2022 12:31:59",
-		"DialCallDuration": "17",
-		"RecordingUrl": "https://s3-ap-southeast-1.amazonaws.com/random.mp3",
-		"StartTime": "2022-02-23 12:31:58",
-		"EndTime": "1970-01-01 05:30:00",
-		"DialCallStatus": "completed",
-		"CallType": "completed",
-		"DialWhomNumber": "09999999999",
-		"ProcessStatus": "null",
-		"flow_id": "228040",
-		"tenant_id": "67291",
-		"From": "09999999991",
-		"To": "09999999988",
-		"RecordingAvailableBy": "Wed, 23 Feb 2022 12:37:25",
-		"CurrentTime": "2022-02-23 12:32:25",
-		"OutgoingPhoneNumber": "09999999988",
-		"Legs": [
-			{
-				"Number": "09999999999",
-				"Type": "single",
-				"OnCallDuration": "10",
-				"CallerId": "09999999980",
-				"CauseCode": "NORMAL_CLEARING",
-				"Cause": "16",
-			}
-		],
-	}
-)
-
-call_disconnected_data = frappe._dict(
-	{
-		"CallSid": "d96421addce69e24bdc7ce5880d1162l",
-		"CallFrom": "09999999991",
-		"CallTo": "09999999980",
-		"Direction": "incoming",
-		"ForwardedFrom": "null",
-		"Created": "Mon, 21 Feb 2022 15:58:12",
-		"DialCallDuration": "0",
-		"StartTime": "2022-02-21 15:58:12",
-		"EndTime": "1970-01-01 05:30:00",
-		"DialCallStatus": "canceled",
-		"CallType": "client-hangup",
-		"DialWhomNumber": "09999999999",
-		"ProcessStatus": "null",
-		"flow_id": "228040",
-		"tenant_id": "67291",
-		"From": "09999999991",
-		"To": "09999999988",
-		"CurrentTime": "2022-02-21 15:58:47",
-		"OutgoingPhoneNumber": "09999999988",
-		"Legs": [
-			{
-				"Number": "09999999999",
-				"Type": "single",
-				"OnCallDuration": "0",
-				"CallerId": "09999999980",
-				"CauseCode": "RING_TIMEOUT",
-				"Cause": "1003",
-			}
-		],
-	}
-)
-
-call_not_answered_data = frappe._dict(
-	{
-		"CallSid": "fdb67a2b4b2d057b610a52ef43f81622",
-		"CallFrom": "09999999991",
-		"CallTo": "09999999980",
-		"Direction": "incoming",
-		"ForwardedFrom": "null",
-		"Created": "Mon, 21 Feb 2022 15:47:02",
-		"DialCallDuration": "0",
-		"StartTime": "2022-02-21 15:47:02",
-		"EndTime": "1970-01-01 05:30:00",
-		"DialCallStatus": "no-answer",
-		"CallType": "incomplete",
-		"DialWhomNumber": "09999999999",
-		"ProcessStatus": "null",
-		"flow_id": "228040",
-		"tenant_id": "67291",
-		"From": "09999999991",
-		"To": "09999999988",
-		"CurrentTime": "2022-02-21 15:47:40",
-		"OutgoingPhoneNumber": "09999999988",
-		"Legs": [
-			{
-				"Number": "09999999999",
-				"Type": "single",
-				"OnCallDuration": "0",
-				"CallerId": "09999999980",
-				"CauseCode": "RING_TIMEOUT",
-				"Cause": "1003",
-			}
-		],
-	}
-)
diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py
deleted file mode 100644
index 9b91414..0000000
--- a/erpnext/tests/test_exotel.py
+++ /dev/null
@@ -1,68 +0,0 @@
-import frappe
-from frappe.contacts.doctype.contact.test_contact import create_contact
-from frappe.tests.test_api import FrappeAPITestCase
-
-from erpnext.setup.doctype.employee.test_employee import make_employee
-
-
-class TestExotel(FrappeAPITestCase):
-	@classmethod
-	def setUpClass(cls):
-		cls.CURRENT_DB_CONNECTION = frappe.db
-		cls.test_employee_name = make_employee(
-			user="test_employee_exotel@company.com", cell_number="9999999999"
-		)
-		frappe.db.set_single_value("Exotel Settings", "enabled", 1)
-		phones = [{"phone": "+91 9999999991", "is_primary_phone": 0, "is_primary_mobile_no": 1}]
-		create_contact(name="Test Contact", salutation="Mr", phones=phones)
-		frappe.db.commit()
-
-	def test_for_successful_call(self):
-		from .exotel_test_data import call_end_data, call_initiation_data
-
-		api_method = "handle_incoming_call"
-		end_call_api_method = "handle_end_call"
-
-		self.emulate_api_call_from_exotel(api_method, call_initiation_data)
-		self.emulate_api_call_from_exotel(end_call_api_method, call_end_data)
-		call_log = frappe.get_doc("Call Log", call_initiation_data.CallSid)
-
-		self.assertEqual(call_log.get("from"), call_initiation_data.CallFrom)
-		self.assertEqual(call_log.get("to"), call_initiation_data.DialWhomNumber)
-		self.assertEqual(call_log.get("call_received_by"), self.test_employee_name)
-		self.assertEqual(call_log.get("status"), "Completed")
-
-	def test_for_disconnected_call(self):
-		from .exotel_test_data import call_disconnected_data
-
-		api_method = "handle_missed_call"
-		self.emulate_api_call_from_exotel(api_method, call_disconnected_data)
-		call_log = frappe.get_doc("Call Log", call_disconnected_data.CallSid)
-		self.assertEqual(call_log.get("from"), call_disconnected_data.CallFrom)
-		self.assertEqual(call_log.get("to"), call_disconnected_data.DialWhomNumber)
-		self.assertEqual(call_log.get("call_received_by"), self.test_employee_name)
-		self.assertEqual(call_log.get("status"), "Canceled")
-
-	def test_for_call_not_answered(self):
-		from .exotel_test_data import call_not_answered_data
-
-		api_method = "handle_missed_call"
-		self.emulate_api_call_from_exotel(api_method, call_not_answered_data)
-		call_log = frappe.get_doc("Call Log", call_not_answered_data.CallSid)
-		self.assertEqual(call_log.get("from"), call_not_answered_data.CallFrom)
-		self.assertEqual(call_log.get("to"), call_not_answered_data.DialWhomNumber)
-		self.assertEqual(call_log.get("call_received_by"), self.test_employee_name)
-		self.assertEqual(call_log.get("status"), "No Answer")
-
-	def emulate_api_call_from_exotel(self, api_method, data):
-		self.post(
-			f"/api/method/erpnext.erpnext_integrations.exotel_integration.{api_method}",
-			data=frappe.as_json(data),
-			content_type="application/json",
-		)
-		# restart db connection to get latest data
-		frappe.connect()
-
-	@classmethod
-	def tearDownClass(cls):
-		frappe.db = cls.CURRENT_DB_CONNECTION
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index 5f0a8dc..8efa94d 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -1219,7 +1219,7 @@
 Hold,Anhalten,
 Hold Invoice,Rechnung zurückhalten,
 Holiday,Urlaub,
-Holiday List,Urlaubsübersicht,
+Holiday List,Feiertagsliste,
 Hotel Rooms of type {0} are unavailable on {1},Hotelzimmer vom Typ {0} sind auf {1} nicht verfügbar,
 Hotels,Hotels,
 Hourly,Stündlich,
@@ -3085,9 +3085,9 @@
 Total Order Considered,Geschätzte Summe der Bestellungen,
 Total Order Value,Gesamtbestellwert,
 Total Outgoing,Summe Auslieferungen,
-Total Outstanding,Absolut aussergewöhnlich,
-Total Outstanding Amount,Offener Gesamtbetrag,
-Total Outstanding: {0},Gesamtsumme: {0},
+Total Outstanding,Summe ausstehende Beträge,
+Total Outstanding Amount,Summe ausstehende Beträge,
+Total Outstanding: {0},Summe ausstehende Beträge: {0},
 Total Paid Amount,Summe gezahlte Beträge,
 Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total,Der gesamte Zahlungsbetrag im Zahlungsplan muss gleich Groß / Abgerundet sein,
 Total Payments,Gesamtzahlungen,
@@ -3317,7 +3317,7 @@
 Working,In Bearbeitung,
 Working Hours,Arbeitszeit,
 Workstation,Arbeitsplatz,
-Workstation is closed on the following dates as per Holiday List: {0},Arbeitsplatz ist an folgenden Tagen gemäß der Urlaubsliste geschlossen: {0},
+Workstation is closed on the following dates as per Holiday List: {0},Arbeitsplatz ist an folgenden Tagen gemäß der Feiertagsliste geschlossen: {0},
 Wrapping up,Aufwickeln,
 Wrong Password,Falsches Passwort,
 Year start date or end date is overlapping with {0}. To avoid please set company,"Jahresbeginn oder Enddatum überlappt mit {0}. Bitte ein Unternehmen wählen, um dies zu verhindern",
@@ -3583,6 +3583,7 @@
 Activity,Aktivität,
 Add / Manage Email Accounts.,Hinzufügen/Verwalten von E-Mail-Konten,
 Add Child,Unterpunkt hinzufügen,
+Add Local Holidays,Lokale Feiertage hinzufügen,
 Add Multiple,Mehrere hinzufügen,
 Add Participants,Teilnehmer hinzufügen,
 Add to Featured Item,Zum empfohlenen Artikel hinzufügen,
@@ -4046,6 +4047,7 @@
 Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.,Der Bestandswert ({0}) und der Kontostand ({1}) sind für das Konto {2} und die verknüpften Lager nicht synchron.,
 Stores - {0},Stores - {0},
 Student with email {0} does not exist,Der Student mit der E-Mail-Adresse {0} existiert nicht,
+Subdivision,Teilgebiet,
 Submit Review,Bewertung abschicken,
 Submitted,Gebucht,
 Supplier Addresses And Contacts,Lieferanten-Adressen und Kontaktdaten,
@@ -4192,6 +4194,7 @@
 No students Found,Keine Schüler gefunden,
 Not in Stock,Nicht lagernd,
 Please select a Customer,Bitte wählen Sie einen Kunden aus,
+Please select a country,Bitte wählen Sie ein Land aus,
 Printed On,Gedruckt auf,
 Received From,Erhalten von,
 Sales Person,Verkäufer,
@@ -6497,7 +6500,7 @@
 Attendance and Leave Details,Anwesenheits- und Urlaubsdetails,
 Leave Policy,Urlaubsrichtlinie,
 Attendance Device ID (Biometric/RF tag ID),Anwesenheitsgeräte-ID (biometrische / RF-Tag-ID),
-Applicable Holiday List,Geltende Urlaubsliste,
+Applicable Holiday List,Geltende Feiertagsliste,
 Default Shift,Standardverschiebung,
 Salary Details,Gehaltsdetails,
 Salary Mode,Gehaltsmodus,
@@ -6662,12 +6665,12 @@
 Expense Claim Detail,Auslage,
 Expense Date,Datum der Auslage,
 Expense Claim Type,Art der Auslagenabrechnung,
-Holiday List Name,Urlaubslistenname,
-Total Holidays,Insgesamt Feiertage,
-Add Weekly Holidays,Wöchentliche Feiertage hinzufügen,
+Holiday List Name,Name der Feiertagsliste,
+Total Holidays,Insgesamt freie Tage,
+Add Weekly Holidays,Wöchentlich freie Tage hinzufügen,
 Weekly Off,Wöchentlich frei,
-Add to Holidays,Zu Feiertagen hinzufügen,
-Holidays,Ferien,
+Add to Holidays,Zu freien Tagen hinzufügen,
+Holidays,Arbeitsfreie Tage,
 Clear Table,Tabelle leeren,
 HR Settings,Einstellungen zum Modul Personalwesen,
 Employee Settings,Mitarbeitereinstellungen,
@@ -6777,7 +6780,7 @@
 Is Carry Forward,Ist Übertrag,
 Is Expired,Ist abgelaufen,
 Is Leave Without Pay,Ist unbezahlter Urlaub,
-Holiday List for Optional Leave,Urlaubsliste für optionalen Urlaub,
+Holiday List for Optional Leave,Feiertagsliste für optionalen Urlaub,
 Leave Allocations,Zuteilungen verlassen,
 Leave Policy Details,Urlaubsrichtliniendetails,
 Leave Policy Detail,Urlaubsrichtliniendetail,
@@ -7646,7 +7649,7 @@
 Change Abbreviation,Abkürzung ändern,
 Parent Company,Muttergesellschaft,
 Default Values,Standardwerte,
-Default Holiday List,Standard-Urlaubsliste,
+Default Holiday List,Standard Feiertagsliste,
 Default Selling Terms,Standardverkaufsbedingungen,
 Default Buying Terms,Standard-Einkaufsbedingungen,
 Create Chart Of Accounts Based On,"Kontenplan erstellen, basierend auf",
@@ -8552,13 +8555,14 @@
 Enable Distributed Cost Center,Aktivieren Sie die verteilte Kostenstelle,
 Distributed Cost Center,Verteilte Kostenstelle,
 Dunning,Mahnung,
+Dunning Level,Mahnstufe,
 DUNN-.MM.-.YY.-,DUNN-.MM .-. YY.-,
 Overdue Days,Überfällige Tage,
 Dunning Type,Mahnart,
 Dunning Fee,Mahngebühr,
 Dunning Amount,Mahnbetrag,
-Resolved,Aufgelöst,
-Unresolved,Ungelöst,
+Resolved,Geklärt,
+Unresolved,Ungeklärt,
 Printing Setting,Druckeinstellung,
 Body Text,Hauptteil,
 Closing Text,Text schließen,
@@ -8738,7 +8742,7 @@
 Meta Data,Metadaten,
 Unresolve,Auflösen,
 Create Document,Dokument erstellen,
-Mark as unresolved,Als ungelöst markieren,
+Mark as unresolved,Als ungeklärt markieren,
 TaxJar Settings,TaxJar-Einstellungen,
 Sandbox Mode,Sandbox-Modus,
 Enable Tax Calculation,Steuerberechnung aktivieren,
diff --git a/pyproject.toml b/pyproject.toml
index 012ffb1..3e0dfb2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,6 +13,7 @@
     "Unidecode~=1.3.6",
     "barcodenumber~=0.5.0",
     "rapidfuzz~=2.15.0",
+    "holidays~=0.28",
 
     # integration dependencies
     "gocardless-pro~=1.22.0",