Merge branch 'develop' of https://github.com/frappe/erpnext into customer-details-in-tax-withholding-category-report
diff --git a/.eslintrc b/.eslintrc
index 12fefa0..f3d4fd5 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -2,65 +2,32 @@
 	"env": {
 		"browser": true,
 		"node": true,
-		"es6": true
+		"es2022": true
 	},
 	"parserOptions": {
-		"ecmaVersion": 11,
 		"sourceType": "module"
 	},
 	"extends": "eslint:recommended",
 	"rules": {
-		"indent": [
-			"error",
-			"tab",
-			{ "SwitchCase": 1 }
-		],
-		"brace-style": [
-			"error",
-			"1tbs"
-		],
-		"space-unary-ops": [
-			"error",
-			{ "words": true }
-		],
-		"linebreak-style": [
-			"error",
-			"unix"
-		],
-		"quotes": [
-			"off"
-		],
-		"semi": [
-			"warn",
-			"always"
-		],
-		"camelcase": [
-			"off"
-		],
-		"no-unused-vars": [
-			"warn"
-		],
-		"no-redeclare": [
-			"warn"
-		],
-		"no-console": [
-			"warn"
-		],
-		"no-extra-boolean-cast": [
-			"off"
-		],
-		"no-control-regex": [
-			"off"
-		],
-		"space-before-blocks": "warn",
-		"keyword-spacing": "warn",
-		"comma-spacing": "warn",
-		"key-spacing": "warn"
+		"indent": "off",
+		"brace-style": "off",
+		"no-mixed-spaces-and-tabs": "off",
+		"no-useless-escape": "off",
+		"space-unary-ops": ["error", { "words": true }],
+		"linebreak-style": "off",
+		"quotes": ["off"],
+		"semi": "off",
+		"camelcase": "off",
+		"no-unused-vars": "off",
+		"no-console": ["warn"],
+		"no-extra-boolean-cast": ["off"],
+		"no-control-regex": ["off"]
 	},
 	"root": true,
 	"globals": {
 		"frappe": true,
 		"Vue": true,
+		"SetVueGlobals": true,
 		"erpnext": true,
 		"hub": true,
 		"$": true,
@@ -97,8 +64,10 @@
 		"is_null": true,
 		"in_list": true,
 		"has_common": true,
+		"posthog": true,
 		"has_words": true,
 		"validate_email": true,
+		"open_web_template_values_editor": true,
 		"get_number_format": true,
 		"format_number": true,
 		"format_currency": true,
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
index af6d8f2..94b76b1 100644
--- a/.github/workflows/linters.yml
+++ b/.github/workflows/linters.yml
@@ -9,21 +9,22 @@
     name: linters
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
 
       - name: Set up Python 3.10
-        uses: actions/setup-python@v2
+        uses: actions/setup-python@v4
         with:
           python-version: '3.10'
+          cache: pip
 
       - name: Install and Run Pre-commit
-        uses: pre-commit/action@v2.0.3
+        uses: pre-commit/action@v3.0.0
 
       - name: Download Semgrep rules
         run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
 
       - name: Download semgrep
-        run: pip install semgrep==0.97.0
+        run: pip install semgrep
 
       - name: Run Semgrep rules
         run: semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness
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/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d70977c..2c9a60c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -16,8 +16,26 @@
       - id: check-merge-conflict
       - id: check-ast
 
+  - repo: https://github.com/pre-commit/mirrors-eslint
+    rev: v8.44.0
+    hooks:
+      - id: eslint
+        types_or: [javascript]
+        args: ['--quiet']
+        # Ignore any files that might contain jinja / bundles
+        exclude: |
+            (?x)^(
+                erpnext/public/dist/.*|
+                cypress/.*|
+                .*node_modules.*|
+                .*boilerplate.*|
+                erpnext/public/js/controllers/.*|
+                erpnext/templates/pages/order.js|
+                erpnext/templates/includes/.*
+            )$
+
   - repo: https://github.com/PyCQA/flake8
-    rev: 5.0.4
+    rev: 6.0.0
     hooks:
       - id: flake8
         additional_dependencies: [
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_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 6667193..83bd7fe 100644
--- a/erpnext/accounts/doctype/bank/bank.js
+++ b/erpnext/accounts/doctype/bank/bank.js
@@ -102,7 +102,7 @@
 	}
 
 	onScriptLoaded(me) {
-		me.linkHandler = Plaid.create({
+		me.linkHandler = Plaid.create({ // eslint-disable-line no-undef
 			env: me.plaid_env,
 			token: me.token,
 			onSuccess: me.plaid_success
diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js
index 632fab0..c427cc8 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.js
+++ b/erpnext/accounts/doctype/cost_center/cost_center.js
@@ -70,7 +70,7 @@
 				}
 			],
 			primary_action: function() {
-				var data = d.get_values();
+				let data = d.get_values();
 				if(data.cost_center_name === frm.doc.cost_center_name && data.cost_center_number === frm.doc.cost_center_number) {
 					d.hide();
 					return;
@@ -91,8 +91,8 @@
 							if(r.message) {
 								frappe.set_route("Form", "Cost Center", r.message);
 							} else {
-								me.frm.set_value("cost_center_name", data.cost_center_name);
-								me.frm.set_value("cost_center_number", data.cost_center_number);
+								frm.set_value("cost_center_name", data.cost_center_name);
+								frm.set_value("cost_center_number", data.cost_center_number);
 							}
 							d.hide();
 						}
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/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index a51e38e..8d8cbef 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -264,11 +264,11 @@
 			}
 
 			if(jvd.party_type && jvd.party) {
-				var party_field = "";
+				let party_field = "";
 				if(jvd.reference_type.indexOf("Sales")===0) {
-					var party_field = "customer";
+					party_field = "customer";
 				} else if (jvd.reference_type.indexOf("Purchase")===0) {
-					var party_field = "supplier";
+					party_field = "supplier";
 				}
 
 				if (party_field) {
@@ -368,7 +368,7 @@
 		td += flt(accounts[i].debit, precision("debit", accounts[i]));
 		tc += flt(accounts[i].credit, precision("credit", accounts[i]));
 	}
-	var doc = locals[doc.doctype][doc.name];
+	doc = locals[doc.doctype][doc.name];
 	doc.total_debit = td;
 	doc.total_credit = tc;
 	doc.difference = flt((td - tc), precision("difference"));
diff --git a/erpnext/accounts/doctype/overdue_payment/__init__.py b/erpnext/accounts/doctype/overdue_payment/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/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.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 0701435..ed18fea 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -1,10 +1,12 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-{% include "erpnext/public/js/controllers/accounts.js" %}
 frappe.provide("erpnext.accounts.dimensions");
 
 cur_frm.cscript.tax_table = "Advance Taxes and Charges";
 
+erpnext.accounts.taxes.setup_tax_validations("Payment Entry");
+erpnext.accounts.taxes.setup_tax_filters("Advance Taxes and Charges");
+
 frappe.ui.form.on('Payment Entry', {
 	onload: function(frm) {
 		frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', "Repost Payment Ledger"];
@@ -106,12 +108,11 @@
 		});
 
 		frm.set_query("reference_doctype", "references", function() {
+			let doctypes = ["Journal Entry"];
 			if (frm.doc.party_type == "Customer") {
-				var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"];
+				doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"];
 			} else if (frm.doc.party_type == "Supplier") {
-				var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
-			} else {
-				var doctypes = ["Journal Entry"];
+				doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
 			}
 
 			return {
@@ -122,13 +123,10 @@
 		frm.set_query('payment_term', 'references', function(frm, cdt, cdn) {
 			const child = locals[cdt][cdn];
 			if (in_list(['Purchase Invoice', 'Sales Invoice'], child.reference_doctype) && child.reference_name) {
-				let payment_term_list = frappe.get_list('Payment Schedule', {'parent': child.reference_name});
-
-				payment_term_list = payment_term_list.map(pt => pt.payment_term);
-
 				return {
+					query: "erpnext.controllers.queries.get_payment_terms_for_references",
 					filters: {
-						'name': ['in', payment_term_list]
+						'reference': child.reference_name
 					}
 				}
 			}
@@ -165,6 +163,7 @@
 	},
 
 	company: function(frm) {
+		frm.trigger('party');
 		frm.events.hide_unhide_fields(frm);
 		frm.events.set_dynamic_labels(frm);
 		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
@@ -287,6 +286,13 @@
 		}
 	},
 
+	mode_of_payment: function(frm) {
+		erpnext.accounts.pos.get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){
+			let payment_account_field = frm.doc.payment_type == "Receive" ? "paid_to" : "paid_from";
+			frm.set_value(payment_account_field, account);
+		})
+	},
+
 	party_type: function(frm) {
 
 		let party_types = Object.keys(frappe.boot.party_account_types);
@@ -319,10 +325,6 @@
 		}
 	},
 
-	company: function(frm){
-		frm.trigger('party');
-	},
-
 	party: function(frm) {
 		if (frm.doc.contact_email || frm.doc.contact_person) {
 			frm.set_value("contact_email", "");
@@ -1106,7 +1108,7 @@
 							if (tax.charge_type === 'On Net Total') {
 								tax.charge_type = 'On Paid Amount';
 							}
-							me.frm.add_child("taxes", tax);
+							frm.add_child("taxes", tax);
 						}
 						frm.events.apply_taxes(frm);
 						frm.events.set_unallocated_amount(frm);
@@ -1222,7 +1224,7 @@
 				tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item;
 			} else {
 				tax.grand_total_fraction_for_current_item =
-					me.frm.doc["taxes"][i-1].grand_total_fraction_for_current_item +
+					frm.doc["taxes"][i-1].grand_total_fraction_for_current_item +
 					tax.tax_fraction_for_current_item;
 			}
 
@@ -1269,7 +1271,7 @@
 			}
 		});
 
-		$.each(me.frm.doc["taxes"] || [], function(i, tax) {
+		$.each(frm.doc["taxes"] || [], function(i, tax) {
 			let current_tax_amount = frm.events.get_current_tax_amount(frm, tax);
 
 			// Adjust divisional loss to the last item
@@ -1463,4 +1465,4 @@
 			});
 		}
 	},
-})
\ No newline at end of file
+})
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index dcd7295..596881a 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -207,6 +207,20 @@
 				if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(d.outstanding_amount):
 					frappe.throw(fail_message.format(d.idx))
 
+	def term_based_allocation_enabled_for_reference(
+		self, reference_doctype: str, reference_name: str
+	) -> bool:
+		if (
+			reference_doctype
+			and reference_doctype in ["Sales Invoice", "Sales Order", "Purchase Order", "Purchase Invoice"]
+			and reference_name
+		):
+			if template := frappe.db.get_value(reference_doctype, reference_name, "payment_terms_template"):
+				return frappe.db.get_value(
+					"Payment Terms Template", template, "allocate_payment_based_on_payment_terms"
+				)
+		return False
+
 	def validate_allocated_amount_with_latest_data(self):
 		latest_references = get_outstanding_reference_documents(
 			{
@@ -226,10 +240,25 @@
 		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))
+		for idx, d in enumerate(self.get("references"), start=1):
+			latest = latest_lookup.get((d.reference_doctype, d.reference_name)) or frappe._dict()
+
+			# If term based allocation is enabled, throw
+			if (
+				d.payment_term is None or d.payment_term == ""
+			) and self.term_based_allocation_enabled_for_reference(
+				d.reference_doctype, d.reference_name
+			):
+				frappe.throw(
+					_(
+						"{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section"
+					).format(frappe.bold(d.reference_name), frappe.bold(idx))
+				)
+
+			# if no payment template is used by invoice and has a custom term(no `payment_term`), then invoice outstanding will be in 'None' key
+			latest = latest.get(d.payment_term) or latest.get(None)
 
 			# The reference has already been fully paid
 			if not latest:
@@ -251,6 +280,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))
@@ -1500,7 +1541,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
@@ -1560,8 +1603,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(
@@ -1578,6 +1640,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(
@@ -1589,6 +1659,10 @@
 										"posting_date": d.posting_date,
 										"invoice_amount": flt(d.invoice_amount),
 										"outstanding_amount": flt(d.outstanding_amount),
+										"payment_term_outstanding": payment_term_outstanding,
+										"allocated_amount": payment_term_outstanding
+										if payment_term_outstanding
+										else d.outstanding_amount,
 										"payment_amount": payment_term.payment_amount,
 										"payment_term": payment_term.payment_term,
 										"account": d.account,
@@ -2010,28 +2084,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:
@@ -2125,8 +2198,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"
@@ -2371,6 +2446,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/payment_order/payment_order.js b/erpnext/accounts/doctype/payment_order/payment_order.js
index 7d85d89..6630e71 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.js
+++ b/erpnext/accounts/doctype/payment_order/payment_order.js
@@ -124,7 +124,7 @@
 			return frappe.call({
 				method: "erpnext.accounts.doctype.payment_order.payment_order.make_payment_records",
 				args: {
-					"name": me.frm.doc.name,
+					"name": frm.doc.name,
 					"supplier": args.supplier,
 					"mode_of_payment": args.mode_of_payment
 				},
diff --git a/erpnext/accounts/doctype/payment_term/payment_term.js b/erpnext/accounts/doctype/payment_term/payment_term.js
index feecf93..0898a09 100644
--- a/erpnext/accounts/doctype/payment_term/payment_term.js
+++ b/erpnext/accounts/doctype/payment_term/payment_term.js
@@ -14,7 +14,7 @@
 		if (frm.doc.discount) {
 			let description = __("{0}% of total invoice value will be given as discount.", [frm.doc.discount]);
 			if (frm.doc.discount_type == 'Amount') {
-				description = __("{0} will be given as discount.", [fmt_money(frm.doc.discount)]);
+				description = __("{0} will be given as discount.", [frm.doc.discount]);
 			}
 			frm.set_df_property("discount", "description", description);
 		}
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/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
index 32e267f..6f0b801 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
@@ -1,9 +1,10 @@
 // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-{% include 'erpnext/selling/sales_common.js' %};
 frappe.provide("erpnext.accounts");
+erpnext.sales_common.setup_selling_controller();
 
+erpnext.accounts.pos.setup("POS Invoice");
 erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnext.selling.SellingController {
 	settings = {};
 
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js
index 813d20d..0a89aee 100755
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.js
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js
@@ -1,8 +1,6 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-{% include "erpnext/public/js/controllers/accounts.js" %}
-
 frappe.ui.form.on('POS Profile', {
 	setup: function(frm) {
 		frm.set_query("selling_price_list", function() {
@@ -148,4 +146,4 @@
 		frm.toggle_display('expense_account',
 			erpnext.is_perpetual_inventory_enabled(frm.doc.company));
 	}
-});
+});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index 08f4cf4..6193c84 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -140,7 +140,7 @@
 def get_ar_filters(doc, entry):
 	return {
 		"report_date": doc.posting_date if doc.posting_date else None,
-		"customer_name": entry.customer,
+		"customer": entry.customer,
 		"payment_terms_template": doc.payment_terms_template if doc.payment_terms_template else None,
 		"sales_partner": doc.sales_partner if doc.sales_partner else None,
 		"sales_person": doc.sales_person if doc.sales_person else None,
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html
index 07e1896..259526f 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html
@@ -10,16 +10,12 @@
 
 	<h2 class="text-center" style="margin-top:0">{{ _(report.report_name) }}</h2>
 	<h4 class="text-center">
-		{% if (filters.customer_name) %}
-			{{ filters.customer_name }}
-		{% else %}
-			{{ filters.customer ~ filters.supplier }}
-		{% endif %}
+		{{ filters.customer }}
 	</h4>
 	<h6 class="text-center">
-			{% if (filters.tax_id) %}
-			{{ _("Tax Id: ") }}{{ filters.tax_id }}
-			{% endif %}
+		{% if (filters.tax_id) %}
+		{{ _("Tax Id: ") }}{{ filters.tax_id }}
+		{% endif %}
 	</h6>
 	<h5 class="text-center">
 		{{ _(filters.ageing_based_on) }}
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 6a558ca..c19413d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -2,7 +2,11 @@
 // License: GNU General Public License v3. See license.txt
 
 frappe.provide("erpnext.accounts");
-{% include 'erpnext/public/js/controllers/buying.js' %};
+
+erpnext.accounts.payment_triggers.setup("Purchase Invoice");
+erpnext.accounts.taxes.setup_tax_filters("Purchase Taxes and Charges");
+erpnext.accounts.taxes.setup_tax_validations("Purchase Invoice");
+erpnext.buying.setup_buying_controller();
 
 erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.BuyingController {
 	setup(doc) {
@@ -506,7 +510,8 @@
 	setup: function(frm) {
 		frm.custom_make_buttons = {
 			'Purchase Invoice': 'Return / Debit Note',
-			'Payment Entry': 'Payment'
+			'Payment Entry': 'Payment',
+			'Landed Cost Voucher': function () { frm.trigger('create_landed_cost_voucher') },
 		}
 
 		frm.set_query("additional_discount_account", function() {
@@ -544,6 +549,26 @@
 		frm.events.add_custom_buttons(frm);
 	},
 
+	mode_of_payment: function(frm) {
+		erpnext.accounts.pos.get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account) {
+			frm.set_value("cash_bank_account", account);
+		})
+	},
+
+	create_landed_cost_voucher: function (frm) {
+		let lcv = frappe.model.get_new_doc('Landed Cost Voucher');
+		lcv.company = frm.doc.company;
+
+		let lcv_receipt = frappe.model.get_new_doc('Landed Cost Purchase Invoice');
+		lcv_receipt.receipt_document_type = 'Purchase Invoice';
+		lcv_receipt.receipt_document = frm.doc.name;
+		lcv_receipt.supplier = frm.doc.supplier;
+		lcv_receipt.grand_total = frm.doc.grand_total;
+		lcv.purchase_receipts = [lcv_receipt];
+
+		frappe.set_route("Form", lcv.doctype, lcv.name);
+	},
+
 	add_custom_buttons: function(frm) {
 		if (frm.doc.docstatus == 1 && frm.doc.per_received < 100) {
 			frm.add_custom_button(__('Purchase Receipt'), () => {
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.js b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.js
index eb0ea7f..78dc4be 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.js
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.js
@@ -1,30 +1,31 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-cur_frm.cscript.tax_table = "Purchase Taxes and Charges";
+erpnext.accounts.taxes.setup_tax_validations("Purchase Taxes and Charges Template");
+erpnext.accounts.taxes.setup_tax_filters("Purchase Taxes and Charges");
 
-{% include "erpnext/public/js/controllers/accounts.js" %}
+frappe.ui.form.on("Purchase Taxes and Charges", {
+	add_deduct_tax(doc, cdt, cdn) {
+		let d = locals[cdt][cdn];
 
-frappe.ui.form.on("Purchase Taxes and Charges", "add_deduct_tax", function(doc, cdt, cdn) {
-	var d = locals[cdt][cdn];
+		if(!d.category && d.add_deduct_tax) {
+			frappe.msgprint(__("Please select Category first"));
+			d.add_deduct_tax = '';
+		}
+		else if(d.category != 'Total' && d.add_deduct_tax == 'Deduct') {
+			frappe.msgprint(__("Cannot deduct when category is for 'Valuation' or 'Valuation and Total'"));
+			d.add_deduct_tax = '';
+		}
+		refresh_field('add_deduct_tax', d.name, 'taxes');
+	},
 
-	if(!d.category && d.add_deduct_tax) {
-		frappe.msgprint(__("Please select Category first"));
-		d.add_deduct_tax = '';
+	category(doc, cdt, cdn) {
+		let d = locals[cdt][cdn];
+
+		if(d.category != 'Total' && d.add_deduct_tax == 'Deduct') {
+			frappe.msgprint(__("Cannot deduct when category is for 'Valuation' or 'Valuation and Total'"));
+			d.add_deduct_tax = '';
+		}
+		refresh_field('add_deduct_tax', d.name, 'taxes');
 	}
-	else if(d.category != 'Total' && d.add_deduct_tax == 'Deduct') {
-		frappe.msgprint(__("Cannot deduct when category is for 'Valuation' or 'Valuation and Total'"));
-		d.add_deduct_tax = '';
-	}
-	refresh_field('add_deduct_tax', d.name, 'taxes');
-});
-
-frappe.ui.form.on("Purchase Taxes and Charges", "category", function(doc, cdt, cdn) {
-	var d = locals[cdt][cdn];
-
-	if (d.category != 'Total' && d.add_deduct_tax == 'Deduct') {
-		frappe.msgprint(__("Cannot deduct when category is for 'Valuation' or 'Vaulation and Total'"));
-		d.add_deduct_tax = '';
-	}
-	refresh_field('add_deduct_tax', d.name, 'taxes');
 });
diff --git a/erpnext/accounts/doctype/sales_invoice/regional/italy.js b/erpnext/accounts/doctype/sales_invoice/regional/italy.js
index 21eb8ce..2f305b9 100644
--- a/erpnext/accounts/doctype/sales_invoice/regional/italy.js
+++ b/erpnext/accounts/doctype/sales_invoice/regional/italy.js
@@ -1,3 +1,23 @@
-{% include "erpnext/regional/italy/sales_invoice.js" %}
-
-erpnext.setup_e_invoice_button('Sales Invoice')
+frappe.ui.form.on("Sales Invoice", {
+    refresh: (frm) => {
+        if(frm.doc.docstatus == 1) {
+            frm.add_custom_button(__('Generate E-Invoice'), () => {
+                frm.call({
+                    method: "erpnext.regional.italy.utils.generate_single_invoice",
+                    args: {
+                        docname: frm.doc.name
+                    },
+                    callback: function(r) {
+                        frm.reload_doc();
+                        if(r.message) {
+                            open_url_post(frappe.request.url, {
+                                cmd: 'frappe.core.doctype.file.file.download_file',
+                                file_url: r.message
+                            });
+                        }
+                    }
+                });
+            });
+        }
+    }
+});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 8753ebc..8fa0a84 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -1,10 +1,13 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-{% include 'erpnext/selling/sales_common.js' %};
 frappe.provide("erpnext.accounts");
 
-
+erpnext.accounts.taxes.setup_tax_validations("Sales Invoice");
+erpnext.accounts.payment_triggers.setup("Sales Invoice");
+erpnext.accounts.pos.setup("Sales Invoice");
+erpnext.accounts.taxes.setup_tax_filters("Sales Taxes and Charges");
+erpnext.sales_common.setup_selling_controller();
 erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends erpnext.selling.SellingController {
 	setup(doc) {
 		this.setup_posting_date_time_check();
@@ -142,9 +145,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'));
 				}
 			}
@@ -711,7 +720,7 @@
 
 		frm.set_query('pos_profile', function(doc) {
 			if(!doc.company) {
-				frappe.throw(_('Please set Company'));
+				frappe.throw(__('Please set Company'));
 			}
 
 			return {
@@ -858,7 +867,7 @@
 			kwargs = Object();
 		}
 
-		if (!kwargs.hasOwnProperty("project") && frm.doc.project) {
+		if (!Object.prototype.hasOwnProperty.call(kwargs, "project") && frm.doc.project) {
 			kwargs.project = frm.doc.project;
 		}
 
@@ -891,6 +900,8 @@
 				frm.events.append_time_log(frm, timesheet, 1.0);
 			}
 		});
+		frm.refresh_field("timesheets");
+		frm.trigger("calculate_timesheet_totals");
 	},
 
 	async get_exchange_rate(frm, from_currency, to_currency) {
@@ -930,9 +941,6 @@
 		row.billing_amount = flt(time_log.billing_amount) * flt(exchange_rate);
 		row.timesheet_detail = time_log.name;
 		row.project_name = time_log.project_name;
-
-		frm.refresh_field("timesheets");
-		frm.trigger("calculate_timesheet_totals");
 	},
 
 	calculate_timesheet_totals: function(frm) {
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/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 0280c35..41e5554 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1900,16 +1900,22 @@
 
 		si = self.create_si_to_test_tax_breakup()
 
-		itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
+		itemised_tax_data = get_itemised_tax_breakup_data(si)
 
-		expected_itemised_tax = {
-			"_Test Item": {"Service Tax": {"tax_rate": 10.0, "tax_amount": 1000.0}},
-			"_Test Item 2": {"Service Tax": {"tax_rate": 10.0, "tax_amount": 500.0}},
-		}
-		expected_itemised_taxable_amount = {"_Test Item": 10000.0, "_Test Item 2": 5000.0}
+		expected_itemised_tax = [
+			{
+				"item": "_Test Item",
+				"taxable_amount": 10000.0,
+				"Service Tax": {"tax_rate": 10.0, "tax_amount": 1000.0},
+			},
+			{
+				"item": "_Test Item 2",
+				"taxable_amount": 5000.0,
+				"Service Tax": {"tax_rate": 10.0, "tax_amount": 500.0},
+			},
+		]
 
-		self.assertEqual(itemised_tax, expected_itemised_tax)
-		self.assertEqual(itemised_taxable_amount, expected_itemised_taxable_amount)
+		self.assertEqual(itemised_tax_data, expected_itemised_tax)
 
 		frappe.flags.country = None
 
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js
index 066c4ea..00e8b62 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js
@@ -1,6 +1,5 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-cur_frm.cscript.tax_table = "Sales Taxes and Charges";
-
-{% include "erpnext/public/js/controllers/accounts.js" %}
+erpnext.accounts.taxes.setup_tax_validations("Sales Taxes and Charges Template");
+erpnext.accounts.taxes.setup_tax_filters("Sales Taxes and Charges");
\ No newline at end of file
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/party.py b/erpnext/accounts/party.py
index 03cf82a..4996203 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -33,6 +33,7 @@
 from erpnext import get_company_currency
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen
+from erpnext.utilities.regional import temporary_flag
 
 PURCHASE_TRANSACTION_TYPES = {"Purchase Order", "Purchase Receipt", "Purchase Invoice"}
 SALES_TRANSACTION_TYPES = {
@@ -261,9 +262,8 @@
 	)
 
 	if doctype in TRANSACTION_TYPES:
-		# required to set correct region
-		frappe.flags.company = company
-		get_regional_address_details(party_details, doctype, company)
+		with temporary_flag("company", company):
+			get_regional_address_details(party_details, doctype, company)
 
 	return party_address, shipping_address
 
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/account_balance/account_balance.js b/erpnext/accounts/report/account_balance/account_balance.js
index bb66951..5681be9 100644
--- a/erpnext/accounts/report/account_balance/account_balance.js
+++ b/erpnext/accounts/report/account_balance/account_balance.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Account Balance"] = {
 	"filters": [
diff --git a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js
index e1fccb6..7617ed1 100644
--- a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js
+++ b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports['Billed Items To Be Received'] = {
 	'filters': [
@@ -17,7 +17,7 @@
 			'fieldname': 'posting_date',
 			'fieldtype': 'Date',
 			'reqd': 1,
-			'default': get_today()
+			'default': frappe.datetime.get_today()
 		},
 		{
 			'label': __('Purchase Invoice'),
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 d58fd95..1afa8d5 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.require("assets/erpnext/js/financial_statements.js", function() {
 	frappe.query_reports["Consolidated Financial Statement"] = {
diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js
index a123631..74d52de 100644
--- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js
+++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Customer Ledger Summary"] = {
 	"filters": [
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 96e0c84..eec904e 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
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 function get_filters() {
 	let filters = [
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 9d416db..fc712fb 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
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.require("assets/erpnext/js/financial_statements.js", function() {
 	frappe.query_reports["Dimension-wise Accounts Balance Report"] = {
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 92cf36e..f6b0b8c 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
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Gross and Net Profit Report"] = {
 	"filters": [
diff --git a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js
index 7908c07..bd9b543 100644
--- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js
+++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Inactive Sales Items"] = {
 	"filters": [
diff --git a/erpnext/accounts/report/payment_ledger/payment_ledger.js b/erpnext/accounts/report/payment_ledger/payment_ledger.js
index a5a4108..65380cc 100644
--- a/erpnext/accounts/report/payment_ledger/payment_ledger.js
+++ b/erpnext/accounts/report/payment_ledger/payment_ledger.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 function get_filters() {
 	let filters = [
diff --git a/erpnext/accounts/report/pos_register/pos_register.js b/erpnext/accounts/report/pos_register/pos_register.js
index b8d48d9..6e5491a 100644
--- a/erpnext/accounts/report/pos_register/pos_register.js
+++ b/erpnext/accounts/report/pos_register/pos_register.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["POS Register"] = {
 	"filters": [
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
index 6caebd3..27b29ba 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
@@ -16,9 +16,30 @@
 				"fieldname": "based_on",
 				"label": __("Based On"),
 				"fieldtype": "Select",
-				"options": ["Cost Center", "Project"],
+				"options": ["Cost Center", "Project", "Accounting Dimension"],
 				"default": "Cost Center",
-				"reqd": 1
+				"reqd": 1,
+				"on_change": function(query_report){
+					let based_on = query_report.get_values().based_on;
+					if(based_on!='Accounting Dimension'){
+						frappe.query_report.set_filter_value({
+							accounting_dimension: ''
+						});
+					}
+				}
+			},
+			{
+				"fieldname": "accounting_dimension",
+				"label": __("Accounting Dimension"),
+				"fieldtype": "Link",
+				"options": "Accounting Dimension",
+				"get_query": () =>{
+					return {
+						filters: {
+							"disabled": 0
+						}
+					}
+				}
 			},
 			{
 				"fieldname": "fiscal_year",
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
index 183e279..3d6e9b5 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
@@ -6,6 +6,7 @@
 from frappe import _
 from frappe.utils import cstr, flt
 
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
 from erpnext.accounts.report.financial_statements import (
 	filter_accounts,
 	filter_out_zero_value_rows,
@@ -16,10 +17,12 @@
 
 
 def execute(filters=None):
-	if not filters.get("based_on"):
-		filters["based_on"] = "Cost Center"
+	if filters.get("based_on") == "Accounting Dimension" and not filters.get("accounting_dimension"):
+		frappe.throw(_("Select Accounting Dimension."))
 
-	based_on = filters.based_on.replace(" ", "_").lower()
+	based_on = (
+		filters.based_on if filters.based_on != "Accounting Dimension" else filters.accounting_dimension
+	)
 	validate_filters(filters)
 	accounts = get_accounts_data(based_on, filters.get("company"))
 	data = get_data(accounts, filters, based_on)
@@ -28,14 +31,14 @@
 
 
 def get_accounts_data(based_on, company):
-	if based_on == "cost_center":
+	if based_on == "Cost Center":
 		return frappe.db.sql(
 			"""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt
 			from `tabCost Center` where company=%s order by name""",
 			company,
 			as_dict=True,
 		)
-	elif based_on == "project":
+	elif based_on == "Project":
 		return frappe.get_all("Project", fields=["name"], filters={"company": company}, order_by="name")
 	else:
 		filters = {}
@@ -56,11 +59,17 @@
 
 	gl_entries_by_account = {}
 
+	accounting_dimensions = get_dimensions(with_cost_center_and_project=True)[0]
+	fieldname = ""
+	for dimension in accounting_dimensions:
+		if dimension["document_type"] == based_on:
+			fieldname = dimension["fieldname"]
+
 	set_gl_entries_by_account(
 		filters.get("company"),
 		filters.get("from_date"),
 		filters.get("to_date"),
-		based_on,
+		fieldname,
 		gl_entries_by_account,
 		ignore_closing_entries=not flt(filters.get("with_period_closing_entry")),
 	)
diff --git a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js
index 44e20e8..92e6f7f 100644
--- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js
+++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js
@@ -29,7 +29,6 @@
 			"label": __("Owner"),
 			"fieldtype": "Link",
 			"options": "User",
-			"defaults": user
 		},
 		{
 			"fieldname":"is_pos",
diff --git a/erpnext/accounts/report/share_balance/share_balance.js b/erpnext/accounts/report/share_balance/share_balance.js
index 6db5bdd..ac64a0b 100644
--- a/erpnext/accounts/report/share_balance/share_balance.js
+++ b/erpnext/accounts/report/share_balance/share_balance.js
@@ -1,7 +1,7 @@
 // -*- coding: utf-8 -*-
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Share Balance"] = {
 	"filters": [
diff --git a/erpnext/accounts/report/share_ledger/share_ledger.js b/erpnext/accounts/report/share_ledger/share_ledger.js
index 6d1c44a..4f2d2cc 100644
--- a/erpnext/accounts/report/share_ledger/share_ledger.js
+++ b/erpnext/accounts/report/share_ledger/share_ledger.js
@@ -1,7 +1,7 @@
 // -*- coding: utf-8 -*-
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Share Ledger"] = {
 	"filters": [
diff --git a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js
index 5dc4c3d..8e3c8ac 100644
--- a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js
+++ b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Supplier Ledger Summary"] = {
 	"filters": [
diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.js b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.js
index c29a801..e330d2f 100644
--- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.js
+++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Tax Withholding Details"] = {
 	"filters": [
diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js
index d3d45b3..d334846 100644
--- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js
+++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["TDS Computation Summary"] = {
 	"filters": [
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 39917f9..599c8a3 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -231,6 +231,9 @@
 				(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
 		and report_type == "Profit and Loss"
diff --git a/erpnext/accounts/report/voucher_wise_balance/voucher_wise_balance.js b/erpnext/accounts/report/voucher_wise_balance/voucher_wise_balance.js
index 0c148f8..f7ab029 100644
--- a/erpnext/accounts/report/voucher_wise_balance/voucher_wise_balance.js
+++ b/erpnext/accounts/report/voucher_wise_balance/voucher_wise_balance.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Voucher-wise Balance"] = {
 	"filters": [
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 4b54483..e354663 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -1112,7 +1112,8 @@
 
 def parse_naming_series_variable(doc, variable):
 	if variable == "FY":
-		return get_fiscal_year(date=doc.get("posting_date"), company=doc.get("company"))[0]
+		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()
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index 43920ad..97a41de 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -473,7 +473,7 @@
 		}
 		const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code);
 		if (!item) {
-			doctype_field = frappe.scrub(doctype)
+			let doctype_field = frappe.scrub(doctype)
 			frm.set_value(doctype_field, '');
 			frappe.msgprint({
 				title: __('Invalid {0}', [__(doctype)]),
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 42f5311..7dc438e 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -933,6 +933,8 @@
 	)
 
 	new_asset.gross_purchase_amount = new_gross_purchase_amount
+	if asset.purchase_receipt_amount:
+		new_asset.purchase_receipt_amount = new_gross_purchase_amount
 	new_asset.opening_accumulated_depreciation = opening_accumulated_depreciation
 	new_asset.asset_quantity = split_qty
 	new_asset.split_from = asset.name
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py
index 22055dc..b85f719 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.py
@@ -62,20 +62,21 @@
 					frappe.throw(_("Source and Target Location cannot be same"))
 
 			if self.purpose == "Receipt":
-				if not (d.source_location or d.from_employee) and not (d.target_location or d.to_employee):
+				if not (d.source_location) 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.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)
-					)
+				elif d.source_location:
+					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)
+						)
+					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 48b17f5..48d3331 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Fixed Asset Register"] = {
 	"filters": [
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/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 8fa8f30..db3a60e 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -3,7 +3,10 @@
 
 frappe.provide("erpnext.buying");
 frappe.provide("erpnext.accounts.dimensions");
-{% include 'erpnext/public/js/controllers/buying.js' %};
+
+erpnext.accounts.taxes.setup_tax_filters("Purchase Taxes and Charges");
+erpnext.accounts.taxes.setup_tax_validations("Purchase Order");
+erpnext.buying.setup_buying_controller();
 
 frappe.ui.form.on("Purchase Order", {
 	setup: function(frm) {
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index 2f0b786..0cdb915 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -1,11 +1,10 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-
-{% include 'erpnext/public/js/controllers/buying.js' %};
-
 cur_frm.add_fetch('contact', 'email_id', 'email_id')
 
+erpnext.buying.setup_buying_controller();
+
 frappe.ui.form.on("Request for Quotation",{
 	setup: function(frm) {
 		frm.custom_make_buttons = {
@@ -436,7 +435,7 @@
 
 				//Remove blanks
 				for (var j = 0; j < frm.doc.suppliers.length; j++) {
-					if(!frm.doc.suppliers[j].hasOwnProperty("supplier")) {
+					if(!Object.prototype.hasOwnProperty.call(frm.doc.suppliers[j], "supplier")) {
 						frm.get_field("suppliers").grid.grid_rows[j].remove();
 					}
 				}
@@ -445,10 +444,11 @@
 					if(r.message) {
 						for (var i = 0; i < r.message.length; i++) {
 							var exists = false;
+							let supplier = "";
 							if (r.message[i].constructor === Array){
-								var supplier = r.message[i][0];
+								supplier = r.message[i][0];
 							} else {
-								var supplier = r.message[i].name;
+								supplier = r.message[i].name;
 							}
 
 							for (var j = 0; j < doc.suppliers.length;j++) {
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
index dc9c590..addf5a5 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
@@ -1,9 +1,7 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-// attach required files
-{% include 'erpnext/public/js/controllers/buying.js' %};
-
+erpnext.buying.setup_buying_controller();
 erpnext.buying.SupplierQuotationController = class SupplierQuotationController extends erpnext.buying.BuyingController {
 	setup() {
 		this.frm.custom_make_buttons = {
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js
index b4cd852..e9d5678 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js
@@ -1,8 +1,6 @@
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-/* global frappe, refresh_field */
-
 frappe.ui.form.on("Supplier Scorecard", {
 	setup: function(frm) {
 		if (frm.doc.indicator_color !== "")	{
@@ -79,7 +77,7 @@
 		callback: function(r) {
 			for (var j = 0; j < frm.doc.standings.length; j++)
 			{
-				if(!frm.doc.standings[j].hasOwnProperty("standing_name")) {
+				if(!Object.prototype.hasOwnProperty.call(frm.doc.standings[j], "standing_name")) {
 					frm.get_field("standings").grid.grid_rows[j].remove();
 				}
 			}
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js
index dc5474e..edf0b04 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js
@@ -1,7 +1,6 @@
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-/* global frappe, __ */
 
 frappe.listview_settings["Supplier Scorecard"] = {
 	add_fields: ["indicator_color", "status"],
@@ -14,4 +13,4 @@
 		}
 	},
 
-};
+}
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js
index 9f8a2de..2186cd8 100644
--- a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js
@@ -1,8 +1,6 @@
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-/* global frappe */
-
 frappe.ui.form.on("Supplier Scorecard Criteria", {
 	refresh: function() {}
 });
diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js
index a4cdeb3..62079cc 100644
--- a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js
+++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js
@@ -1,9 +1,6 @@
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-/* global frappe */
-
-
 frappe.ui.form.on("Supplier Scorecard Period", {
 	onload: function(frm) {
 		let criteria_grid = frm.get_field("criteria").grid;
diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js
index dccfcc3..22756e7 100644
--- a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js
+++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js
@@ -1,7 +1,6 @@
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-/* global frappe */
 
 frappe.ui.form.on("Supplier Scorecard Standing", {
 	refresh: function() {
diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js
index 2d74fdd..b3b4321 100644
--- a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js
@@ -1,8 +1,6 @@
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-/* global frappe */
-
 frappe.ui.form.on("Supplier Scorecard Variable", {
 	refresh: function() {
 
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.js b/erpnext/buying/report/procurement_tracker/procurement_tracker.js
index 283d56c..416655d 100644
--- a/erpnext/buying/report/procurement_tracker/procurement_tracker.js
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Procurement Tracker"] = {
 	"filters": [
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.js b/erpnext/buying/report/purchase_analytics/purchase_analytics.js
index a884f06..a0faa68 100644
--- a/erpnext/buying/report/purchase_analytics/purchase_analytics.js
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Purchase Analytics"] = {
 	"filters": [
@@ -81,8 +81,9 @@
 					const tree_type = frappe.query_report.filters[0].value;
 					if (data_doctype != tree_type) return;
 
-					row_name = data[2].content;
-					length = data.length;
+					let row_name = data[2].content;
+					let length = data.length;
+					let row_values = '';
 
 					if (tree_type == "Supplier") {
 						row_values = data
@@ -104,7 +105,7 @@
 							});
 					}
 
-					entry = {
+					let entry = {
 						name: row_name,
 						values: row_values,
 					};
diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
index 721e54e..91506c0 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Purchase Order Analysis"] = {
 	"filters": [
diff --git a/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.js b/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.js
index d727584..cb05109 100644
--- a/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.js
+++ b/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Requested Items to Order and Receive"] = {
 	"filters": [
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
index 075671f..800b8ab 100644
--- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Subcontract Order Summary"] = {
 	"filters": [
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js
index 9db769d..35be2a9 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Subcontracted Item To Be Received"] = {
 	"filters": [
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js
index 7e5338f..33b26dc 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Subcontracted Raw Materials To Be Transferred"] = {
 	"filters": [
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 4193b53..7940489 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -56,6 +56,7 @@
 	get_item_tax_map,
 	get_item_warehouse,
 )
+from erpnext.utilities.regional import temporary_flag
 from erpnext.utilities.transaction_base import TransactionBase
 
 
@@ -760,7 +761,9 @@
 			}
 		)
 
-		update_gl_dict_with_regional_fields(self, gl_dict)
+		with temporary_flag("company", self.company):
+			update_gl_dict_with_regional_fields(self, gl_dict)
+
 		accounting_dimensions = get_accounting_dimensions()
 		dimension_dict = frappe._dict()
 
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 3bb1128..5ec2474 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"))
@@ -865,3 +874,18 @@
 		fields.insert(1, meta.title_field.strip())
 
 	return unique(fields)
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_payment_terms_for_references(doctype, txt, searchfield, start, page_len, filters) -> list:
+	terms = []
+	if filters:
+		terms = frappe.db.get_all(
+			"Payment Schedule",
+			filters={"parent": filters.get("reference")},
+			fields=["payment_term"],
+			limit=page_len,
+			as_list=1,
+		)
+	return terms
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 4661c5c..62d4c53 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -18,6 +18,7 @@
 	validate_taxes_and_charges,
 )
 from erpnext.stock.get_item_details import _get_item_tax_template
+from erpnext.utilities.regional import temporary_flag
 
 
 class calculate_taxes_and_totals(object):
@@ -942,7 +943,6 @@
 def get_itemised_tax_breakup_html(doc):
 	if not doc.taxes:
 		return
-	frappe.flags.company = doc.company
 
 	# get headers
 	tax_accounts = []
@@ -952,22 +952,17 @@
 		if tax.description not in tax_accounts:
 			tax_accounts.append(tax.description)
 
-	headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
-
-	# get tax breakup data
-	itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
-
-	get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
-
-	update_itemised_tax_data(doc)
-	frappe.flags.company = None
+	with temporary_flag("company", doc.company):
+		headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
+		itemised_tax_data = get_itemised_tax_breakup_data(doc)
+		get_rounded_tax_amount(itemised_tax_data, doc.precision("tax_amount", "taxes"))
+		update_itemised_tax_data(doc)
 
 	return frappe.render_template(
 		"templates/includes/itemised_tax_breakup.html",
 		dict(
 			headers=headers,
-			itemised_tax=itemised_tax,
-			itemised_taxable_amount=itemised_taxable_amount,
+			itemised_tax_data=itemised_tax_data,
 			tax_accounts=tax_accounts,
 			doc=doc,
 		),
@@ -977,10 +972,8 @@
 @frappe.whitelist()
 def get_round_off_applicable_accounts(company, account_list):
 	# required to set correct region
-	frappe.flags.company = company
-	account_list = get_regional_round_off_accounts(company, account_list)
-
-	return account_list
+	with temporary_flag("company", company):
+		return get_regional_round_off_accounts(company, account_list)
 
 
 @erpnext.allow_regional
@@ -1005,7 +998,15 @@
 
 	itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
 
-	return itemised_tax, itemised_taxable_amount
+	itemised_tax_data = []
+	for item_code, taxes in itemised_tax.items():
+		itemised_tax_data.append(
+			frappe._dict(
+				{"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code), **taxes}
+			)
+		)
+
+	return itemised_tax_data
 
 
 def get_itemised_tax(taxes, with_tax_account=False):
@@ -1050,9 +1051,10 @@
 
 def get_rounded_tax_amount(itemised_tax, precision):
 	# Rounding based on tax_amount precision
-	for taxes in itemised_tax.values():
-		for tax_account in taxes:
-			taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
+	for taxes in itemised_tax:
+		for row in taxes.values():
+			if isinstance(row, dict) and isinstance(row["tax_amount"], float):
+				row["tax_amount"] = flt(row["tax_amount"], precision)
 
 
 class init_landed_taxes_and_totals(object):
diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js
index 9ac5418..b1799ce 100644
--- a/erpnext/crm/doctype/lead/lead.js
+++ b/erpnext/crm/doctype/lead/lead.js
@@ -54,6 +54,7 @@
 	}
 
 	add_lead_to_prospect () {
+		let me = this;
 		frappe.prompt([
 			{
 				fieldname: 'prospect',
@@ -67,12 +68,12 @@
 			frappe.call({
 				method: 'erpnext.crm.doctype.lead.lead.add_lead_to_prospect',
 				args: {
-					'lead': cur_frm.doc.name,
+					'lead': me.frm.doc.name,
 					'prospect': data.prospect
 				},
 				callback: function(r) {
 					if (!r.exc) {
-						frm.reload_doc();
+						me.frm.reload_doc();
 					}
 				},
 				freeze: true,
diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js
index b261795..6ef8297 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.js
+++ b/erpnext/crm/doctype/opportunity/opportunity.js
@@ -1,10 +1,10 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
-
-{% include 'erpnext/selling/sales_common.js' %}
 frappe.provide("erpnext.crm");
+erpnext.pre_sales.set_as_lost("Quotation");
+erpnext.sales_common.setup_selling_controller();
 
-cur_frm.email_field = "contact_email";
+
 frappe.ui.form.on("Opportunity", {
 	setup: function(frm) {
 		frm.custom_make_buttons = {
@@ -19,6 +19,8 @@
 				}
 			}
 		});
+
+		frm.email_field = "contact_email";
 	},
 
 	validate: function(frm) {
@@ -46,10 +48,6 @@
 		}
 	},
 
-	onload_post_render: function(frm) {
-		frm.get_field("items").grid.set_multiple_add("item_code", "qty");
-	},
-
 	status:function(frm){
 		if (frm.doc.status == "Lost"){
 			frm.trigger('set_as_lost_dialog');
@@ -252,13 +250,13 @@
 	onload() {
 
 		if(!this.frm.doc.status) {
-			frm.set_value('status', 'Open');
+			this.frm.set_value('status', 'Open');
 		}
 		if(!this.frm.doc.company && frappe.defaults.get_user_default("Company")) {
-			frm.set_value('company', frappe.defaults.get_user_default("Company"));
+			this.frm.set_value('company', frappe.defaults.get_user_default("Company"));
 		}
 		if(!this.frm.doc.currency) {
-			frm.set_value('currency', frappe.defaults.get_user_default("Currency"));
+			this.frm.set_value('currency', frappe.defaults.get_user_default("Currency"));
 		}
 
 		this.setup_queries();
diff --git a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js
index fe5707a..4bf8247 100644
--- a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js
+++ b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["First Response Time for Opportunity"] = {
 	"filters": [
diff --git a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js
index eeb8984..d7ff9ad 100644
--- a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js
+++ b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Lead Conversion Time"] = {
 	"filters": [
diff --git a/erpnext/crm/report/lead_details/lead_details.js b/erpnext/crm/report/lead_details/lead_details.js
index 2f6d242..66611f6 100644
--- a/erpnext/crm/report/lead_details/lead_details.js
+++ b/erpnext/crm/report/lead_details/lead_details.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Lead Details"] = {
 	"filters": [
diff --git a/erpnext/crm/report/lost_opportunity/lost_opportunity.js b/erpnext/crm/report/lost_opportunity/lost_opportunity.js
index 927c54d..8d59239 100644
--- a/erpnext/crm/report/lost_opportunity/lost_opportunity.js
+++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Lost Opportunity"] = {
 	"filters": [
diff --git a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js
index 7cd1710..0aa2143 100644
--- a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js
+++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Opportunity Summary by Sales Stage"] = {
 	"filters": [
diff --git a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js
index 1426f4b..3111121 100644
--- a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js
+++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Sales Pipeline Analytics"] = {
 	"filters": [
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
index 3ba6bb9..015e943 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
@@ -96,7 +96,7 @@
 	}
 
 	onScriptLoaded(me) {
-		me.linkHandler = Plaid.create({
+		me.linkHandler = Plaid.create({ // eslint-disable-line no-undef
 			clientName: me.client_name,
 			product: me.product,
 			env: me.plaid_env,
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 316d943..d8b40e3 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -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": {
@@ -464,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",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js
index 8e9f542..f1e6094 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card.js
@@ -344,6 +344,28 @@
 		if(frm.doc.__islocal)
 			return;
 
+		function setCurrentIncrement() {
+			currentIncrement += 1;
+			return currentIncrement;
+		}
+
+		function updateStopwatch(increment) {
+			var hours = Math.floor(increment / 3600);
+			var minutes = Math.floor((increment - (hours * 3600)) / 60);
+			var seconds = increment - (hours * 3600) - (minutes * 60);
+
+			$(section).find(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString());
+			$(section).find(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString());
+			$(section).find(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString());
+		}
+
+		function initialiseTimer() {
+			const interval = setInterval(function() {
+				var current = setCurrentIncrement();
+				updateStopwatch(current);
+			}, 1000);
+		}
+
 		frm.dashboard.refresh();
 		const timer = `
 			<div class="stopwatch" style="font-weight:bold;margin:0px 13px 0px 2px;
@@ -365,28 +387,6 @@
 				currentIncrement += moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds");
 				initialiseTimer();
 			}
-
-			function initialiseTimer() {
-				const interval = setInterval(function() {
-					var current = setCurrentIncrement();
-					updateStopwatch(current);
-				}, 1000);
-			}
-
-			function updateStopwatch(increment) {
-				var hours = Math.floor(increment / 3600);
-				var minutes = Math.floor((increment - (hours * 3600)) / 60);
-				var seconds = increment - (hours * 3600) - (minutes * 60);
-
-				$(section).find(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString());
-				$(section).find(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString());
-				$(section).find(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString());
-			}
-
-			function setCurrentIncrement() {
-				currentIncrement += 1;
-				return currentIncrement;
-			}
 		}
 	},
 
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index c1a078d..58945bb 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -624,7 +624,7 @@
 						if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) {
 							frm.has_finish_btn = true;
 
-							var finish_btn = frm.add_custom_button(__('Finish'), function() {
+							let finish_btn = frm.add_custom_button(__('Finish'), function() {
 								erpnext.work_order.make_se(frm, 'Manufacture');
 							});
 
@@ -648,7 +648,7 @@
 					}
 				} else {
 					if ((flt(doc.produced_qty) < flt(doc.qty))) {
-						var finish_btn = frm.add_custom_button(__('Finish'), function() {
+						let finish_btn = frm.add_custom_button(__('Finish'), function() {
 							erpnext.work_order.make_se(frm, 'Manufacture');
 						});
 						finish_btn.addClass('btn-primary');
@@ -756,13 +756,14 @@
 	},
 
 	make_consumption_se: function(frm, backflush_raw_materials_based_on) {
+		let max = 0;
 		if(!frm.doc.skip_transfer){
-			var max = (backflush_raw_materials_based_on === "Material Transferred for Manufacture") ?
+			max = (backflush_raw_materials_based_on === "Material Transferred for Manufacture") ?
 				flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty) :
 				flt(frm.doc.qty) - flt(frm.doc.produced_qty);
 				// flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing);
 		} else {
-			var max = flt(frm.doc.qty) - flt(frm.doc.produced_qty);
+			max = flt(frm.doc.qty) - flt(frm.doc.produced_qty);
 		}
 
 		frappe.call({
diff --git a/erpnext/manufacturing/doctype/workstation/_test_workstation.js b/erpnext/manufacturing/doctype/workstation/_test_workstation.js
index 0f09bd1..f2dced8 100644
--- a/erpnext/manufacturing/doctype/workstation/_test_workstation.js
+++ b/erpnext/manufacturing/doctype/workstation/_test_workstation.js
@@ -1,4 +1,4 @@
-/* eslint-disable */
+
 // rename this file from _test_[name] to test_[name] to activate
 // and remove above this line
 
diff --git a/erpnext/manufacturing/doctype/workstation/workstation_list.js b/erpnext/manufacturing/doctype/workstation/workstation_list.js
index 6a89d21..61f2062 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation_list.js
+++ b/erpnext/manufacturing/doctype/workstation/workstation_list.js
@@ -1,4 +1,4 @@
-/* eslint-disable */
+
 frappe.listview_settings['Workstation'] = {
 	// add_fields: ["status"],
 	// filters:[["status","=", "Open"]]
diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.js b/erpnext/manufacturing/report/bom_explorer/bom_explorer.js
index b94d3f3..50191bf 100644
--- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.js
+++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["BOM Explorer"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js
index 0eb22a2..34edb9d 100644
--- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js
+++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["BOM Operations Time"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js
index a0fd91e..8e66f70 100644
--- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js
+++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Epoch Consulting and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["BOM Stock Calculated"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.js b/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.js
index c6ecaef..db6f4d7 100644
--- a/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.js
+++ b/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["BOM Variance Report"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js
index 72eed5e..d0249ba 100644
--- a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js
+++ b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Cost of Poor Quality Report"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js
index f648674..0589260 100644
--- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js
+++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Downtime Analysis"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js
index a3f0d00..3795bd3 100644
--- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js
+++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Exponential Smoothing Forecasting"] = {
 	"filters": [
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 a874f22..f005a8a 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Job Card Summary"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/process_loss_report/process_loss_report.js b/erpnext/manufacturing/report/process_loss_report/process_loss_report.js
index b0c2b94..c08413d 100644
--- a/erpnext/manufacturing/report/process_loss_report/process_loss_report.js
+++ b/erpnext/manufacturing/report/process_loss_report/process_loss_report.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Process Loss Report"] = {
 	filters: [
diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.js b/erpnext/manufacturing/report/production_analytics/production_analytics.js
index 99f9b12..72f06ed 100644
--- a/erpnext/manufacturing/report/production_analytics/production_analytics.js
+++ b/erpnext/manufacturing/report/production_analytics/production_analytics.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Production Analytics"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js
index 59396fe..521543a 100644
--- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Production Plan Summary"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.js b/erpnext/manufacturing/report/production_planning_report/production_planning_report.js
index 675b8a1..4225832 100644
--- a/erpnext/manufacturing/report/production_planning_report/production_planning_report.js
+++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Production Planning Report"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js
index d4587aa..905d185 100644
--- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js
+++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Quality Inspection Summary"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js
index 2fb4ec6..70d7f92 100644
--- a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js
+++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Work Order Consumed Materials"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.js b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.js
index dbb7c23..cf651cb 100644
--- a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.js
+++ b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Work Order Stock Report"] = {
 	"filters": [
diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js
index 67bd24d..d0242f4 100644
--- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js
+++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Work Order Summary"] = {
 	"filters": [
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 17add82..0f4238c 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -337,3 +337,4 @@
 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..3b01871
--- /dev/null
+++ b/erpnext/patches/v14_0/single_to_multi_dunning.py
@@ -0,0 +1,78 @@
+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")
+
+	# Migrate schema of all uncancelled dunnings
+	filters = {"docstatus": ("!=", 2)}
+
+	can_edit_accounts_after = get_accounts_closing_date()
+	if can_edit_accounts_after:
+		# Get dunnings after the date when accounts were frozen/closed
+		filters["posting_date"] = (">", can_edit_accounts_after)
+
+	all_dunnings = frappe.get_all("Dunning", filters=filters, 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()
+
+		# Reverse entries only if dunning is submitted and not resolved
+		if dunning.docstatus == 1 and 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)
+
+
+def get_accounts_closing_date():
+	"""Get the date when accounts were frozen/closed"""
+	accounts_frozen_till = frappe.db.get_single_value(
+		"Accounts Settings", "acc_frozen_upto"
+	)  # always returns datetime.date
+
+	period_closing_date = frappe.db.get_value(
+		"Period Closing Voucher", {"docstatus": 1}, "posting_date", order_by="posting_date desc"
+	)
+
+	# Set most recent frozen/closing date as filter
+	if accounts_frozen_till and period_closing_date:
+		can_edit_accounts_after = max(accounts_frozen_till, period_closing_date)
+	else:
+		can_edit_accounts_after = accounts_frozen_till or period_closing_date
+
+	return can_edit_accounts_after
diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py
index 2947b98..2c84281 100644
--- a/erpnext/patches/v14_0/update_closing_balances.py
+++ b/erpnext/patches/v14_0/update_closing_balances.py
@@ -69,7 +69,6 @@
 
 				entries = gl_entries + closing_entries
 
-				if entries:
-					make_closing_entries(entries, voucher_name=pcv.name)
-					i += 1
-					company_wise_order[pcv.company].append(pcv.posting_date)
+				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/projects/report/delayed_tasks_summary/delayed_tasks_summary.js b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js
index 5aa44c0..fa70b93 100644
--- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js
+++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Delayed Tasks Summary"] = {
 	"filters": [
diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js
index 13f49ed..8566b1f 100644
--- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js
+++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Employee Billing Summary"] = {
 	"filters": [
diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.js b/erpnext/projects/report/project_billing_summary/project_billing_summary.js
index caac1d8..0242036 100644
--- a/erpnext/projects/report/project_billing_summary/project_billing_summary.js
+++ b/erpnext/projects/report/project_billing_summary/project_billing_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Project Billing Summary"] = {
 	"filters": [
diff --git a/erpnext/projects/report/project_summary/project_summary.js b/erpnext/projects/report/project_summary/project_summary.js
index 414b7b2..21dbfda 100644
--- a/erpnext/projects/report/project_summary/project_summary.js
+++ b/erpnext/projects/report/project_summary/project_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Project Summary"] = {
 	"filters": [
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 1bed541..b9b48ab 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -26,7 +26,6 @@
 		"public/js/templates/item_selector.html",
 		"public/js/utils/item_selector.js",
 		"public/js/help_links.js",
-		"public/js/agriculture/ternary_plot.js",
 		"public/js/templates/item_quick_entry.html",
 		"public/js/utils/customer_quick_entry.js",
 		"public/js/utils/supplier_quick_entry.js",
diff --git a/erpnext/public/js/account_tree_grid.js b/erpnext/public/js/account_tree_grid.js
deleted file mode 100644
index 413a5ee..0000000
--- a/erpnext/public/js/account_tree_grid.js
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// License: GNU General Public License v3. See license.txt
-
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program.	If not, see <http://www.gnu.org/licenses/>.
-
-erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridReport {
-	constructor(wrapper, title) {
-		super({
-			title: title,
-			parent: $(wrapper).find('.layout-main'),
-			page: wrapper.page,
-			doctypes: ["Company", "Fiscal Year", "Account", "GL Entry", "Cost Center"],
-			tree_grid: {
-				show: true,
-				parent_field: "parent_account",
-				formatter: function(item) {
-					return repl("<a \
-						onclick='frappe.cur_grid_report.show_general_ledger(\"%(value)s\")'>\
-						%(value)s</a>", {
-							value: item.name,
-						});
-				}
-			},
-		});
-
-		this.filters = [
-			{fieldtype: "Select", label: __("Company"), link:"Company", fieldname: "company",
-				default_value: __("Select Company..."),
-				filter: function(val, item, opts, me) {
-					if (item.company == val || val == opts.default_value) {
-						return me.apply_zero_filter(val, item, opts, me);
-					}
-					return false;
-				}},
-			{fieldtype: "Select", label: "Fiscal Year", link:"Fiscal Year", fieldname: "fiscal_year",
-				default_value: __("Select Fiscal Year...")},
-			{fieldtype: "Date", label: __("From Date"), fieldname: "from_date"},
-			{fieldtype: "Label", label: __("To")},
-			{fieldtype: "Date", label: __("To Date"), fieldname: "to_date"}
-		]
-	}
-	setup_columns() {
-		this.columns = [
-			{id: "name", name: __("Account"), field: "name", width: 300, cssClass: "cell-title"},
-			{id: "opening_dr", name: __("Opening (Dr)"), field: "opening_dr", width: 100,
-				formatter: this.currency_formatter},
-			{id: "opening_cr", name: __("Opening (Cr)"), field: "opening_cr", width: 100,
-				formatter: this.currency_formatter},
-			{id: "debit", name: __("Debit"), field: "debit", width: 100,
-				formatter: this.currency_formatter},
-			{id: "credit", name: __("Credit"), field: "credit", width: 100,
-				formatter: this.currency_formatter},
-			{id: "closing_dr", name: __("Closing (Dr)"), field: "closing_dr", width: 100,
-				formatter: this.currency_formatter},
-			{id: "closing_cr", name: __("Closing (Cr)"), field: "closing_cr", width: 100,
-				formatter: this.currency_formatter}
-		];
-	}
-
-	setup_filters() {
-		super.setup_filters();
-		var me = this;
-		// default filters
-		this.filter_inputs.fiscal_year.change(function() {
-			var fy = $(this).val();
-			$.each(frappe.report_dump.data["Fiscal Year"], function(i, v) {
-				if (v.name==fy) {
-					me.filter_inputs.from_date.val(frappe.datetime.str_to_user(v.year_start_date));
-					me.filter_inputs.to_date.val(frappe.datetime.str_to_user(v.year_end_date));
-				}
-			});
-			me.refresh();
-		});
-		me.show_zero_check()
-		if(me.ignore_closing_entry) me.ignore_closing_entry();
-	}
-	prepare_data() {
-		var me = this;
-		if(!this.primary_data) {
-			// make accounts list
-			me.data = [];
-			me.parent_map = {};
-			me.item_by_name = {};
-
-			$.each(frappe.report_dump.data["Account"], function(i, v) {
-				var d = copy_dict(v);
-
-				me.data.push(d);
-				me.item_by_name[d.name] = d;
-				if(d.parent_account) {
-					me.parent_map[d.name] = d.parent_account;
-				}
-			});
-
-			me.primary_data = [].concat(me.data);
-		}
-
-		me.data = [].concat(me.primary_data);
-		$.each(me.data, function(i, d) {
-			me.init_account(d);
-		});
-
-		this.set_indent();
-		this.prepare_balances();
-
-	}
-	init_account(d) {
-		this.reset_item_values(d);
-	}
-
-	prepare_balances() {
-		var gl = frappe.report_dump.data['GL Entry'];
-		var me = this;
-
-		this.opening_date = frappe.datetime.user_to_obj(this.filter_inputs.from_date.val());
-		this.closing_date = frappe.datetime.user_to_obj(this.filter_inputs.to_date.val());
-		this.set_fiscal_year();
-		if (!this.fiscal_year) return;
-
-		$.each(this.data, function(i, v) {
-			v.opening_dr = v.opening_cr = v.debit
-				= v.credit = v.closing_dr = v.closing_cr = 0;
-		});
-
-		$.each(gl, function(i, v) {
-			var posting_date = frappe.datetime.str_to_obj(v.posting_date);
-			var account = me.item_by_name[v.account];
-			me.update_balances(account, posting_date, v);
-		});
-
-		this.update_groups();
-	}
-	update_balances(account, posting_date, v) {
-		// opening
-		if (posting_date < this.opening_date || v.is_opening === "Yes") {
-			if (account.report_type === "Profit and Loss" &&
-				posting_date <= frappe.datetime.str_to_obj(this.fiscal_year[1])) {
-				// balance of previous fiscal_year should
-				//	not be part of opening of pl account balance
-			} else {
-				var opening_bal = flt(account.opening_dr) - flt(account.opening_cr) +
-					flt(v.debit) - flt(v.credit);
-				this.set_debit_or_credit(account, "opening", opening_bal);
-			}
-		} else if (this.opening_date <= posting_date && posting_date <= this.closing_date) {
-			// in between
-			account.debit += flt(v.debit);
-			account.credit += flt(v.credit);
-		}
-		// closing
-		var closing_bal = flt(account.opening_dr) - flt(account.opening_cr) +
-			flt(account.debit) - flt(account.credit);
-		this.set_debit_or_credit(account, "closing", closing_bal);
-	}
-	set_debit_or_credit(account, field, balance) {
-		if(balance > 0) {
-			account[field+"_dr"] = balance;
-			account[field+"_cr"] = 0;
-		} else {
-			account[field+"_cr"] = Math.abs(balance);
-			account[field+"_dr"] = 0;
-		}
-	}
-	update_groups() {
-		// update groups
-		var me= this;
-		$.each(this.data, function(i, account) {
-			// update groups
-			if((account.is_group == 0) || (account.rgt - account.lft == 1)) {
-				var parent = me.parent_map[account.name];
-				while(parent) {
-					var parent_account = me.item_by_name[parent];
-					$.each(me.columns, function(c, col) {
-						if (col.formatter == me.currency_formatter) {
-							if(col.field=="opening_dr") {
-								var bal = flt(parent_account.opening_dr) -
-									flt(parent_account.opening_cr) +
-									flt(account.opening_dr) - flt(account.opening_cr);
-								me.set_debit_or_credit(parent_account, "opening", bal);
-							} else if(col.field=="closing_dr") {
-								var bal = flt(parent_account.closing_dr) -
-									flt(parent_account.closing_cr) +
-									flt(account.closing_dr) - flt(account.closing_cr);
-								me.set_debit_or_credit(parent_account, "closing", bal);
-							} else if(in_list(["debit", "credit"], col.field)) {
-								parent_account[col.field] = flt(parent_account[col.field]) +
-									flt(account[col.field]);
-							}
-						}
-					});
-					parent = me.parent_map[parent];
-				}
-			}
-		});
-	}
-
-	set_fiscal_year() {
-		if (this.opening_date > this.closing_date) {
-			frappe.msgprint(__("Opening Date should be before Closing Date"));
-			return;
-		}
-
-		this.fiscal_year = null;
-		var me = this;
-		$.each(frappe.report_dump.data["Fiscal Year"], function(i, v) {
-			if (me.opening_date >= frappe.datetime.str_to_obj(v.year_start_date) &&
-				me.closing_date <= frappe.datetime.str_to_obj(v.year_end_date)) {
-				me.fiscal_year = v;
-			}
-		});
-
-		if (!this.fiscal_year) {
-			frappe.msgprint(__("Opening Date and Closing Date should be within same Fiscal Year"));
-			return;
-		}
-	}
-
-	show_general_ledger(account) {
-		frappe.route_options = {
-			account: account,
-			company: this.company,
-			from_date: this.from_date,
-			to_date: this.to_date
-		};
-		frappe.set_route("query-report", "General Ledger");
-	}
-};
diff --git a/erpnext/public/js/agriculture/ternary_plot.js b/erpnext/public/js/agriculture/ternary_plot.js
deleted file mode 100644
index b06a1fd..0000000
--- a/erpnext/public/js/agriculture/ternary_plot.js
+++ /dev/null
@@ -1,232 +0,0 @@
-frappe.provide('agriculture');
-
-agriculture.TernaryPlot = class TernaryPlot {
-	constructor(opts) {
-		Object.assign(this, opts);
-
-		frappe.require('assets/frappe/js/lib/snap.svg-min.js', () => {
-			this.make_svg();
-			this.init_snap();
-			this.init_config();
-			this.make_plot();
-			this.make_plot_marking();
-			this.make_legend();
-			this.mark_blip();
-		});
-	}
-
-	make_svg() {
-		this.$svg = $('<svg height="350" width="400">');
-		$(this.parent).append(this.$svg);
-	}
-
-	init_snap() {
-		this.paper = new Snap(this.$svg.get(0));
-	}
-
-	init_config() {
-		this.config = {
-			triangle_side: 300,
-			spacing: 50,
-			strokeWidth: 1,
-			stroke: frappe.ui.color.get('black')
-		};
-		this.config.scaling_factor = this.config.triangle_side / 100;
-		let { triangle_side: t, spacing: s, scaling_factor: p } = this.config;
-
-		this.coords = {
-			sand: {
-				points: [
-					s + t * Snap.cos(60), s,
-					s, s + t * Snap.cos(30),
-					s + t, s + t * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('peach')
-			},
-			loamy_sand: {
-				points: [
-					s + 15 * p * Snap.cos(60), s + (100 - 15) * p * Snap.cos(30),
-					s + 10 * p * Snap.cos(60), s + (100 - 10) * p * Snap.cos(30),
-					s + (100 - 85) * p, s + t * Snap.cos(30),
-					s + (100 - 70) * p, s + t * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('pink')
-			},
-			sandy_loam: {
-				points: [
-					s + 20 * p * Snap.cos(60) + 27.5 * p, s + (100 - 20) * p * Snap.cos(30),
-					s + 20 * p * Snap.cos(60), s + (100 - 20) * p * Snap.cos(30),
-					s + 15 * p * Snap.cos(60), s + (100 - 15) * p * Snap.cos(30),
-					s + (100 - 75) * p, s + t * Snap.cos(30),
-					s + (100 - 50) * p, s + t * Snap.cos(30),
-					s + (100 - 50) * p + 7.5 * p * Snap.cos(60), s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30),
-					s + (100 - 50) * p + 7.5 * p * Snap.cos(60) - 10 * p, s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('pink', 'light')
-			},
-			loam: {
-				points: [
-					s + (100 - 50) * p + 27.5 * p * Snap.cos(60), s + t * Snap.cos(30) - 27.5 * p * Snap.cos(30),
-					s + (100 - 50) * p + 27.5 * p * Snap.cos(60) - 22.5 * p, s + t * Snap.cos(30) - 27.5 * p * Snap.cos(30),
-					s + 20 * p * Snap.cos(60) + 27.5 * p, s + (100 - 20) * p * Snap.cos(30),
-					s + (100 - 50) * p + 7.5 * p * Snap.cos(60) - 10 * p, s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30),
-					s + (100 - 50) * p + 7.5 * p * Snap.cos(60), s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('brown')
-			},
-			silt_loam: {
-				points: [
-					s + t - 27.5 * p * Snap.cos(60), s + 72.5 * p * Snap.cos(30),
-					s + (100 - 50) * p + 27.5 * p * Snap.cos(60), s + t * Snap.cos(30) - 27.5 * p * Snap.cos(30),
-					s + (100 - 50) * p, s + t * Snap.cos(30),
-					s + (100 - 20) * p, s + t * Snap.cos(30),
-					s + (100 - 20) * p + 12.5 * p * Snap.cos(60), s + 90 * p * Snap.cos(30),
-					s + t - 12.5 * p * Snap.cos(60), s + (100 - 12.5) * p * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('green', 'dark')
-			},
-			silt: {
-				points: [
-					s + t - 12.5 * p * Snap.cos(60), s + (100 - 12.5) * p * Snap.cos(30),
-					s + (100 - 20) * p + 12.5 * p * Snap.cos(60), s + 90 * p * Snap.cos(30),
-					s + (100 - 20) * p, s + t * Snap.cos(30),
-					s + t, s + t * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('green')
-			},
-			silty_clay_loam: {
-				points: [
-					s + t - 40 * p * Snap.cos(60), s + 60 * p * Snap.cos(30),
-					s + t - 40 * p * Snap.cos(60) - 20 * p, s + 60 * p * Snap.cos(30),
-					s + t - 27.5 * p * Snap.cos(60) - 20 * p, s + 72.5 * p * Snap.cos(30),
-					s + t - 27.5 * p * Snap.cos(60), s + 72.5 * p * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('cyan', 'dark')
-			},
-			silty_clay: {
-				points: [
-					s + t - 60 * p * Snap.cos(60), s + 40 * p * Snap.cos(30),
-					s + t - 40 * p * Snap.cos(60) - 20 * p, s + 60 * p * Snap.cos(30),
-					s + t - 40 * p * Snap.cos(60), s + 60 * p * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('cyan')
-			},
-			clay_loam: {
-				points: [
-					s + t - 40 * p * Snap.cos(60) - 20 * p, s + 60 * p * Snap.cos(30),
-					s + t - 40 * p * Snap.cos(60) - 45 * p, s + 60 * p * Snap.cos(30),
-					s + t - 27.5 * p * Snap.cos(60) - 45 * p, s + 72.5 * p * Snap.cos(30),
-					s + t - 27.5 * p * Snap.cos(60) - 20 * p, s + 72.5 * p * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('green', 'light')
-			},
-			sandy_clay_loam: {
-				points: [
-					s + 35 * p * Snap.cos(60) + 20 * p, s + (100 - 35) * p * Snap.cos(30),
-					s + 35 * p * Snap.cos(60), s + (100 - 35) * p * Snap.cos(30),
-					s + 20 * p * Snap.cos(60), s + (100 - 20) * p * Snap.cos(30),
-					s + 20 * p * Snap.cos(60) + 27.5 * p, s + (100 - 20) * p * Snap.cos(30),
-					s + t - 27.5 * p * Snap.cos(60) - 45 * p, s + 72.5 * p * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('pink', 'dark')
-			},
-			sandy_clay: {
-				points: [
-					s + 55 * p * Snap.cos(60), s + (100 - 55) * p * Snap.cos(30),
-					s + 35 * p * Snap.cos(60), s + (100 - 35) * p * Snap.cos(30),
-					s + 35 * p * Snap.cos(60) + 20 * p, s + (100 - 35) * p * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('red')
-			},
-			clay: {
-				points: [
-					s + t * Snap.cos(60), s,
-					s + 55 * p * Snap.cos(60), s + (100 - 55) * p * Snap.cos(30),
-					s + t - 40 * p * Snap.cos(60) - 45 * p, s + 60 * p * Snap.cos(30),
-					s + t - 40 * p * Snap.cos(60) - 20 * p, s + 60 * p * Snap.cos(30),
-					s + t - 60 * p * Snap.cos(60), s + 40 * p * Snap.cos(30)
-				],
-				color: frappe.ui.color.get('yellow')
-			},
-		};
-	}
-
-	get_coords(soil_type) {
-		return this.coords[soil_type].points;
-	}
-
-	get_color(soil_type) {
-		return this.coords[soil_type].color;
-	}
-
-	make_plot() {
-		for (let soil_type in this.coords) {
-			this.paper.polygon(this.get_coords(soil_type)).attr({
-				fill: this.get_color(soil_type),
-				stroke: this.config.stroke,
-				strokeWidth: this.config.strokeWidth
-			});
-		}
-	}
-
-	make_plot_marking() {
-		let { triangle_side: t, spacing: s, scaling_factor: p } = this.config;
-
-		let clay = this.paper.text(t * Snap.cos(60) / 2, s + t * Snap.cos(30) / 2, __("Clay")).attr({
-			fill: frappe.ui.color.get('black')
-		});
-		clay.transform("r300");
-
-		let silt = this.paper.text(t, s + t * Snap.cos(30) / 2, __("Silt")).attr({
-			fill: frappe.ui.color.get('black')
-		});
-		silt.transform("r60");
-
-		let sand = this.paper.text(35 + t * Snap.cos(60), 90 + t * Snap.cos(30), __("Sand")).attr({
-			fill: frappe.ui.color.get('black')
-		});
-		sand.transform("r0");
-	}
-
-	make_legend() {
-		// let side = len(this.coords)/2;
-		let index = 1;
-		let offset = 0;
-		let exec_once = true;
-		for (let soil_type in this.coords) {
-			if (index > 6 && exec_once){
-				offset = 300;
-				index = 1;
-				exec_once = false;
-			}
-			let rect = this.paper.rect(0+offset, 0+index*20, 100, 19, 5, 5).attr({
-				fill: this.get_color(soil_type),
-				stroke: frappe.ui.color.get('black')
-			});
-			let text = this.paper.text(5+offset, 16+index*20, soil_type).attr({
-				fill: frappe.ui.color.get('black'),
-				'font-size': 12
-			});
-			index++;
-		}
-	}
-
-	mark_blip({clay, sand, silt} = this) {
-		if (clay + sand + silt != 0){
-			let { triangle_side: t, spacing: s, scaling_factor: p } = this.config;
-
-			let x_blip = s + clay * p * Snap.cos(60) + silt * p;
-			let y_blip = s + silt * p * Snap.cos(30) + sand * p * Snap.sin(60);
-			this.blip = this.paper.circle(x_blip, y_blip, 4).attr({
-				fill: frappe.ui.color.get("orange"),
-				stroke: frappe.ui.color.get("orange"),
-				strokeWidth: 2
-			});
-		}
-	}
-
-	remove_blip() {
-		if (typeof this.blip !== 'undefined')
-			this.blip.remove();
-	}
-};
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index 47b88a0..a2e4bda 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -3,319 +3,267 @@
 
 // get tax rate
 frappe.provide("erpnext.taxes");
-frappe.provide("erpnext.taxes.flags");
 
-frappe.ui.form.on(cur_frm.doctype, {
-	setup: function(frm) {
-		// set conditional display for rate column in taxes
-		$(frm.wrapper).on('grid-row-render', function(e, grid_row) {
-			if(in_list(['Sales Taxes and Charges', 'Purchase Taxes and Charges'], grid_row.doc.doctype)) {
-				erpnext.taxes.set_conditional_mandatory_rate_or_amount(grid_row);
+erpnext.accounts.taxes = {
+	setup_tax_validations: function(doctype) {
+		let me = this;
+		frappe.ui.form.on(doctype, {
+			setup: function(frm) {
+				// set conditional display for rate column in taxes
+				$(frm.wrapper).on('grid-row-render', function(e, grid_row) {
+					if(in_list(['Sales Taxes and Charges', 'Purchase Taxes and Charges'], grid_row.doc.doctype)) {
+						me.set_conditional_mandatory_rate_or_amount(grid_row);
+					}
+				});
+			},
+			onload: function(frm) {
+				if(frm.get_field("taxes")) {
+					frm.set_query("account_head", "taxes", function(doc) {
+						if(frm.cscript.tax_table == "Sales Taxes and Charges") {
+							var account_type = ["Tax", "Chargeable", "Expense Account"];
+						} else {
+							var account_type = ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation"];
+						}
+
+						return {
+							query: "erpnext.controllers.queries.tax_account_query",
+							filters: {
+								"account_type": account_type,
+								"company": doc.company,
+								"disabled": 0
+							}
+						}
+					});
+					frm.set_query("cost_center", "taxes", function(doc) {
+						return {
+							filters: {
+								"company": doc.company,
+								"is_group": 0
+							}
+						};
+					});
+				}
+			},
+			validate: function(frm) {
+				// neither is absolutely mandatory
+				if(frm.get_docfield("taxes")) {
+					frm.get_docfield("taxes", "rate").reqd = 0;
+					frm.get_docfield("taxes", "tax_amount").reqd = 0;
+				}
+
+			},
+			taxes_on_form_rendered: function(frm) {
+				me.set_conditional_mandatory_rate_or_amount(frm.open_grid_row());
+			},
+		});
+	},
+
+	set_conditional_mandatory_rate_or_amount: function(grid_row) {
+		if(grid_row) {
+			if(grid_row.doc.charge_type==="Actual") {
+				grid_row.toggle_editable("tax_amount", true);
+				grid_row.toggle_reqd("tax_amount", true);
+				grid_row.toggle_editable("rate", false);
+				grid_row.toggle_reqd("rate", false);
+			} else {
+				grid_row.toggle_editable("rate", true);
+				grid_row.toggle_reqd("rate", true);
+				grid_row.toggle_editable("tax_amount", false);
+				grid_row.toggle_reqd("tax_amount", false);
+			}
+		}
+	},
+
+	validate_taxes_and_charges: function(cdt, cdn) {
+		let d = locals[cdt][cdn];
+		let msg = "";
+
+		if (d.account_head && !d.description) {
+			// set description from account head
+			d.description = d.account_head.split(' - ').slice(0, -1).join(' - ');
+		}
+
+		if (!d.charge_type && (d.row_id || d.rate || d.tax_amount)) {
+			msg = __("Please select Charge Type first");
+			d.row_id = "";
+			d.rate = d.tax_amount = 0.0;
+		} else if ((d.charge_type == 'Actual' || d.charge_type == 'On Net Total' || d.charge_type == 'On Paid Amount') && d.row_id) {
+			msg = __("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'");
+			d.row_id = "";
+		} else if ((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) {
+			if (d.idx == 1) {
+				msg = __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row");
+				d.charge_type = '';
+			} else if (!d.row_id) {
+				msg = __("Please specify a valid Row ID for row {0} in table {1}", [d.idx, __(d.doctype)]);
+				d.row_id = "";
+			} else if (d.row_id && d.row_id >= d.idx) {
+				msg = __("Cannot refer row number greater than or equal to current row number for this Charge type");
+				d.row_id = "";
+			}
+		}
+		if (msg) {
+			frappe.validated = false;
+			refresh_field("taxes");
+			frappe.throw(msg);
+		}
+
+	},
+
+	setup_tax_filters: function(doctype) {
+		let me = this;
+		frappe.ui.form.on(doctype, {
+			account_head: function(frm, cdt, cdn) {
+				let d = locals[cdt][cdn];
+
+				if (doc.docstatus == 1) {
+					// Should not trigger any changes on change post submit
+					return;
+				}
+
+				if(!d.charge_type && d.account_head){
+					frappe.msgprint(__("Please select Charge Type first"));
+					frappe.model.set_value(cdt, cdn, "account_head", "");
+				} else if (d.account_head) {
+					frappe.call({
+						type:"GET",
+						method: "erpnext.controllers.accounts_controller.get_tax_rate",
+						args: {"account_head":d.account_head},
+						callback: function(r) {
+							if (d.charge_type!=="Actual") {
+								frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0);
+							}
+							frappe.model.set_value(cdt, cdn, "description", r.message.account_name);
+						}
+					})
+				}
+			},
+			row_id: function(frm, cdt, cdn) {
+				me.validate_taxes_and_charges(cdt, cdn);
+			},
+			rate: function(frm, cdt, cdn) {
+				me.validate_taxes_and_charges(cdt, cdn);
+			},
+			tax_amount: function(frm, cdt, cdn) {
+				me.validate_taxes_and_charges(cdt, cdn);
+			},
+			charge_type: function(frm, cdt, cdn) {
+				me.validate_taxes_and_charges(cdt, cdn);
+				let open_form = frm.open_grid_row();
+				if(open_form) {
+					me.set_conditional_mandatory_rate_or_amount(open_form);
+				} else {
+					// apply in current row
+					me.set_conditional_mandatory_rate_or_amount(frm.get_field('taxes').grid.get_row(cdn));
+				}
+			},
+			included_in_print_rate: function(frm, cdt, cdn) {
+				let tax = frappe.get_doc(cdt, cdn);
+				try {
+					me.validate_taxes_and_charges(cdt, cdn);
+					me.validate_inclusive_tax(tax);
+				} catch(e) {
+					tax.included_in_print_rate = 0;
+					refresh_field("included_in_print_rate", tax.name, tax.parentfield);
+					throw e;
+				}
 			}
 		});
 	},
-	onload: function(frm) {
-		if(frm.get_field("taxes")) {
-			frm.set_query("account_head", "taxes", function(doc) {
-				if(frm.cscript.tax_table == "Sales Taxes and Charges") {
-					var account_type = ["Tax", "Chargeable", "Expense Account"];
-				} else {
-					var account_type = ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation"];
+
+	validate_inclusive_tax: function(tax) {
+		let actual_type_error = function() {
+			var msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx])
+			frappe.throw(msg);
+		};
+
+		let on_previous_row_error = function(row_range) {
+			var msg = __("For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included",
+				[tax.idx, __(tax.doctype), tax.charge_type, row_range])
+			frappe.throw(msg);
+		};
+
+		if(cint(tax.included_in_print_rate)) {
+			if(tax.charge_type == "Actual") {
+				// inclusive tax cannot be of type Actual
+				actual_type_error();
+			} else if(tax.charge_type == "On Previous Row Amount" &&
+				!cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_print_rate)
+			) {
+				// referred row should also be an inclusive tax
+				on_previous_row_error(tax.row_id);
+			} else if(tax.charge_type == "On Previous Row Total") {
+				var taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id),
+					function(t) { return cint(t.included_in_print_rate) ? null : t; });
+				if(taxes_not_included.length > 0) {
+					// all rows above this tax should be inclusive
+					on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id);
 				}
-
-				return {
-					query: "erpnext.controllers.queries.tax_account_query",
-					filters: {
-						"account_type": account_type,
-						"company": doc.company,
-						"disabled": 0
-					}
-				}
-			});
-			frm.set_query("cost_center", "taxes", function(doc) {
-				return {
-					filters: {
-						"company": doc.company,
-						"is_group": 0
-					}
-				};
-			});
-		}
-	},
-	validate: function(frm) {
-		// neither is absolutely mandatory
-		if(frm.get_docfield("taxes")) {
-			frm.get_docfield("taxes", "rate").reqd = 0;
-			frm.get_docfield("taxes", "tax_amount").reqd = 0;
-		}
-
-	},
-	taxes_on_form_rendered: function(frm) {
-		erpnext.taxes.set_conditional_mandatory_rate_or_amount(frm.open_grid_row());
-	},
-
-	allocate_advances_automatically: function(frm) {
-		frm.trigger('fetch_advances');
-	},
-
-	only_include_allocated_payments: function(frm) {
-		frm.trigger('fetch_advances');
-	},
-
-	fetch_advances: function(frm) {
-		if(frm.doc.allocate_advances_automatically) {
-			frappe.call({
-				doc: frm.doc,
-				method: "set_advances",
-				callback: function(r, rt) {
-					refresh_field("advances");
-				}
-			})
-		}
-	}
-});
-
-frappe.ui.form.on('Sales Invoice Payment', {
-	mode_of_payment: function(frm, cdt, cdn) {
-		var d = locals[cdt][cdn];
-		get_payment_mode_account(frm, d.mode_of_payment, function(account){
-			frappe.model.set_value(cdt, cdn, 'account', account)
-		})
-	}
-});
-
-frappe.ui.form.on("Sales Invoice", {
-	payment_terms_template: function() {
-		cur_frm.trigger("disable_due_date");
-	}
-});
-
-frappe.ui.form.on('Purchase Invoice', {
-	setup: (frm) => {
-		frm.make_methods = {
-			'Landed Cost Voucher': function () { frm.trigger('create_landedcost_voucher') },
-		}
-	},
-
-	mode_of_payment: function(frm) {
-		get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){
-			frm.set_value('cash_bank_account', account);
-		})
-	},
-
-	payment_terms_template: function() {
-		cur_frm.trigger("disable_due_date");
-	},
-
-	create_landedcost_voucher: function (frm) {
-		let lcv = frappe.model.get_new_doc('Landed Cost Voucher');
-		lcv.company = frm.doc.company;
-
-		let lcv_receipt = frappe.model.get_new_doc('Landed Cost Purchase Invoice');
-		lcv_receipt.receipt_document_type = 'Purchase Invoice';
-		lcv_receipt.receipt_document = frm.doc.name;
-		lcv_receipt.supplier = frm.doc.supplier;
-		lcv_receipt.grand_total = frm.doc.grand_total;
-		lcv.purchase_receipts = [lcv_receipt];
-
-		frappe.set_route("Form", lcv.doctype, lcv.name);
-	}
-});
-
-frappe.ui.form.on("Payment Schedule", {
-	payment_schedule_remove: function() {
-		cur_frm.trigger("disable_due_date");
-	},
-
-});
-
-frappe.ui.form.on('Payment Entry', {
-	mode_of_payment: function(frm) {
-		get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){
-			var payment_account_field = frm.doc.payment_type == "Receive" ? "paid_to" : "paid_from";
-			frm.set_value(payment_account_field, account);
-		})
-	}
-})
-
-frappe.ui.form.on('Salary Structure', {
-	mode_of_payment: function(frm) {
-		get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){
-			frm.set_value("payment_account", account);
-		})
-	}
-})
-
-var get_payment_mode_account = function(frm, mode_of_payment, callback) {
-	if(!frm.doc.company) {
-		frappe.throw({message:__("Please select a Company first."), title: __("Mandatory")});
-	}
-
-	if(!mode_of_payment) {
-		return;
-	}
-
-	return  frappe.call({
-		method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
-		args: {
-			"mode_of_payment": mode_of_payment,
-			"company": frm.doc.company
-		},
-		callback: function(r, rt) {
-			if(r.message) {
-				callback(r.message.account)
+			} else if(tax.category == "Valuation") {
+				frappe.throw(__("Valuation type charges can not marked as Inclusive"));
 			}
 		}
-	});
+	}
 }
 
-cur_frm.cscript.account_head = function(doc, cdt, cdn) {
-	var d = locals[cdt][cdn];
+erpnext.accounts.payment_triggers = {
+	setup: function(doctype) {
+		frappe.ui.form.on(doctype, {
+			allocate_advances_automatically(frm) {
+				frm.trigger('fetch_advances');
+			},
 
-	if (doc.docstatus == 1) {
-		// Should not trigger any changes on change post submit
-		return;
-	}
+			only_include_allocated_payments(frm) {
+				frm.trigger('fetch_advances');
+			},
 
-	if(!d.charge_type && d.account_head){
-		frappe.msgprint(__("Please select Charge Type first"));
-		frappe.model.set_value(cdt, cdn, "account_head", "");
-	} else if (d.account_head) {
-		frappe.call({
-			type:"GET",
-			method: "erpnext.controllers.accounts_controller.get_tax_rate",
-			args: {"account_head":d.account_head},
-			callback: function(r) {
-				if (d.charge_type!=="Actual") {
-					frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0);
+			fetch_advances(frm) {
+				if(frm.doc.allocate_advances_automatically) {
+					frappe.call({
+						doc: frm.doc,
+						method: "set_advances",
+						callback: function(r, rt) {
+							refresh_field("advances");
+						}
+					})
 				}
-				frappe.model.set_value(cdt, cdn, "description", r.message.account_name);
 			}
-		})
-	}
+		});
+	},
 }
 
-cur_frm.cscript.validate_taxes_and_charges = function(cdt, cdn) {
-	var d = locals[cdt][cdn];
-	var msg = "";
-
-	if (d.account_head && !d.description) {
-		// set description from account head
-		d.description = d.account_head.split(' - ').slice(0, -1).join(' - ');
-	}
-
-	if (!d.charge_type && (d.row_id || d.rate || d.tax_amount)) {
-		msg = __("Please select Charge Type first");
-		d.row_id = "";
-		d.rate = d.tax_amount = 0.0;
-	} else if ((d.charge_type == 'Actual' || d.charge_type == 'On Net Total' || d.charge_type == 'On Paid Amount') && d.row_id) {
-		msg = __("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'");
-		d.row_id = "";
-	} else if ((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) {
-		if (d.idx == 1) {
-			msg = __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row");
-			d.charge_type = '';
-		} else if (!d.row_id) {
-			msg = __("Please specify a valid Row ID for row {0} in table {1}", [d.idx, __(d.doctype)]);
-			d.row_id = "";
-		} else if (d.row_id && d.row_id >= d.idx) {
-			msg = __("Cannot refer row number greater than or equal to current row number for this Charge type");
-			d.row_id = "";
-		}
-	}
-	if (msg) {
-		frappe.validated = false;
-		refresh_field("taxes");
-		frappe.throw(msg);
-	}
-
-}
-
-cur_frm.cscript.validate_inclusive_tax = function(tax) {
-	var actual_type_error = function() {
-		var msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx])
-		frappe.throw(msg);
-	};
-
-	var on_previous_row_error = function(row_range) {
-		var msg = __("For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included",
-			[tax.idx, __(tax.doctype), tax.charge_type, row_range])
-		frappe.throw(msg);
-	};
-
-	if(cint(tax.included_in_print_rate)) {
-		if(tax.charge_type == "Actual") {
-			// inclusive tax cannot be of type Actual
-			actual_type_error();
-		} else if(tax.charge_type == "On Previous Row Amount" &&
-			!cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_print_rate)
-		) {
-			// referred row should also be an inclusive tax
-			on_previous_row_error(tax.row_id);
-		} else if(tax.charge_type == "On Previous Row Total") {
-			var taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id),
-				function(t) { return cint(t.included_in_print_rate) ? null : t; });
-			if(taxes_not_included.length > 0) {
-				// all rows above this tax should be inclusive
-				on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id);
+erpnext.accounts.pos = {
+	setup: function(doctype) {
+		frappe.ui.form.on(doctype, {
+			mode_of_payment: function(frm, cdt, cdn) {
+				var d = locals[cdt][cdn];
+				get_payment_mode_account(frm, d.mode_of_payment, function(account){
+					frappe.model.set_value(cdt, cdn, 'account', account)
+				})
 			}
-		} else if(tax.category == "Valuation") {
-			frappe.throw(__("Valuation type charges can not marked as Inclusive"));
+		});
+	},
+
+	get_payment_mode_account: function(frm, mode_of_payment, callback) {
+		if(!frm.doc.company) {
+			frappe.throw({message:__("Please select a Company first."), title: __("Mandatory")});
 		}
-	}
-}
 
-if(!erpnext.taxes.flags[cur_frm.cscript.tax_table]) {
-	erpnext.taxes.flags[cur_frm.cscript.tax_table] = true;
-
-	frappe.ui.form.on(cur_frm.cscript.tax_table, "row_id", function(frm, cdt, cdn) {
-		cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
-	});
-
-	frappe.ui.form.on(cur_frm.cscript.tax_table, "rate", function(frm, cdt, cdn) {
-		cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
-	});
-
-	frappe.ui.form.on(cur_frm.cscript.tax_table, "tax_amount", function(frm, cdt, cdn) {
-		cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
-	});
-
-	frappe.ui.form.on(cur_frm.cscript.tax_table, "charge_type", function(frm, cdt, cdn) {
-		frm.cscript.validate_taxes_and_charges(cdt, cdn);
-		var open_form = frm.open_grid_row();
-		if(open_form) {
-			erpnext.taxes.set_conditional_mandatory_rate_or_amount(open_form);
-		} else {
-			// apply in current row
-			erpnext.taxes.set_conditional_mandatory_rate_or_amount(frm.get_field('taxes').grid.get_row(cdn));
+		if(!mode_of_payment) {
+			return;
 		}
-	});
 
-	frappe.ui.form.on(cur_frm.cscript.tax_table, "included_in_print_rate", function(frm, cdt, cdn) {
-		var tax = frappe.get_doc(cdt, cdn);
-		try {
-			cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
-			cur_frm.cscript.validate_inclusive_tax(tax);
-		} catch(e) {
-			tax.included_in_print_rate = 0;
-			refresh_field("included_in_print_rate", tax.name, tax.parentfield);
-			throw e;
-		}
-	});
-}
-
-erpnext.taxes.set_conditional_mandatory_rate_or_amount = function(grid_row) {
-	if(grid_row) {
-		if(grid_row.doc.charge_type==="Actual") {
-			grid_row.toggle_editable("tax_amount", true);
-			grid_row.toggle_reqd("tax_amount", true);
-			grid_row.toggle_editable("rate", false);
-			grid_row.toggle_reqd("rate", false);
-		} else {
-			grid_row.toggle_editable("rate", true);
-			grid_row.toggle_reqd("rate", true);
-			grid_row.toggle_editable("tax_amount", false);
-			grid_row.toggle_reqd("tax_amount", false);
-		}
+		return  frappe.call({
+			method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
+			args: {
+				"mode_of_payment": mode_of_payment,
+				"company": frm.doc.company
+			},
+			callback: function(r, rt) {
+				if(r.message) {
+					callback(r.message.account)
+				}
+			}
+		});
 	}
 }
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index c001b4e..54f0aad 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -2,422 +2,419 @@
 // License: GNU General Public License v3. See license.txt
 
 frappe.provide("erpnext.buying");
+// cur_frm.add_fetch('project', 'cost_center', 'cost_center');
 
-cur_frm.cscript.tax_table = "Purchase Taxes and Charges";
+erpnext.buying = {
+	setup_buying_controller: function() {
+		erpnext.buying.BuyingController = class BuyingController extends erpnext.TransactionController {
+			setup() {
+				super.setup();
+				this.frm.email_field = "contact_email";
+			}
 
-{% include 'erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.js' %}
+			onload(doc, cdt, cdn) {
+				this.setup_queries(doc, cdt, cdn);
+				super.onload();
 
-cur_frm.email_field = "contact_email";
-
-erpnext.buying.BuyingController = class BuyingController extends erpnext.TransactionController {
-	setup() {
-		super.setup();
-	}
-
-	onload(doc, cdt, cdn) {
-		this.setup_queries(doc, cdt, cdn);
-		super.onload();
-
-		this.frm.set_query('shipping_rule', function() {
-			return {
-				filters: {
-					"shipping_rule_type": "Buying"
-				}
-			};
-		});
-
-		if (this.frm.doc.__islocal
-			&& frappe.meta.has_field(this.frm.doc.doctype, "disable_rounded_total")) {
-
-				var df = frappe.meta.get_docfield(this.frm.doc.doctype, "disable_rounded_total");
-				var disable = cint(df.default) || cint(frappe.sys_defaults.disable_rounded_total);
-				this.frm.set_value("disable_rounded_total", disable);
-		}
-
-		/* eslint-disable */
-		// no idea where me is coming from
-		if(this.frm.get_field('shipping_address')) {
-			this.frm.set_query("shipping_address", function() {
-				if(me.frm.doc.customer) {
+				this.frm.set_query('shipping_rule', function() {
 					return {
-						query: 'frappe.contacts.doctype.address.address.address_query',
-						filters: { link_doctype: 'Customer', link_name: me.frm.doc.customer }
+						filters: {
+							"shipping_rule_type": "Buying"
+						}
 					};
-				} else
-					return erpnext.queries.company_address_query(me.frm.doc)
-			});
-		}
-		/* eslint-enable */
-	}
+				});
 
-	setup_queries(doc, cdt, cdn) {
-		var me = this;
+				if (this.frm.doc.__islocal
+					&& frappe.meta.has_field(this.frm.doc.doctype, "disable_rounded_total")) {
 
-		if(this.frm.fields_dict.buying_price_list) {
-			this.frm.set_query("buying_price_list", function() {
-				return{
-					filters: { 'buying': 1 }
-				}
-			});
-		}
-
-		if(this.frm.fields_dict.tc_name) {
-			this.frm.set_query("tc_name", function() {
-				return{
-					filters: { 'buying': 1 }
-				}
-			});
-		}
-
-		me.frm.set_query('supplier', erpnext.queries.supplier);
-		me.frm.set_query('contact_person', erpnext.queries.contact_query);
-		me.frm.set_query('supplier_address', erpnext.queries.address_query);
-
-		me.frm.set_query('billing_address', erpnext.queries.company_address_query);
-		erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype);
-
-		if(this.frm.fields_dict.supplier) {
-			this.frm.set_query("supplier", function() {
-				return{	query: "erpnext.controllers.queries.supplier_query" }});
-		}
-
-		this.frm.set_query("item_code", "items", function() {
-			if (me.frm.doc.is_subcontracted) {
-				var filters = {'supplier': me.frm.doc.supplier};
-				if (me.frm.doc.is_old_subcontracting_flow) {
-					filters["is_sub_contracted_item"] = 1;
-				}
-				else {
-					filters["is_stock_item"] = 0;
+						var df = frappe.meta.get_docfield(this.frm.doc.doctype, "disable_rounded_total");
+						var disable = cint(df.default) || cint(frappe.sys_defaults.disable_rounded_total);
+						this.frm.set_value("disable_rounded_total", disable);
 				}
 
-				return{
-					query: "erpnext.controllers.queries.item_query",
-					filters: filters
+
+				// no idea where me is coming from
+				if(this.frm.get_field('shipping_address')) {
+					this.frm.set_query("shipping_address", function() {
+						if(me.frm.doc.customer) {
+							return {
+								query: 'frappe.contacts.doctype.address.address.address_query',
+								filters: { link_doctype: 'Customer', link_name: me.frm.doc.customer }
+							};
+						} else
+							return erpnext.queries.company_address_query(me.frm.doc)
+					});
 				}
 			}
-			else {
-				return{
-					query: "erpnext.controllers.queries.item_query",
-					filters: { 'supplier': me.frm.doc.supplier, 'is_purchase_item': 1, 'has_variants': 0}
+
+			setup_queries(doc, cdt, cdn) {
+				var me = this;
+
+				if(this.frm.fields_dict.buying_price_list) {
+					this.frm.set_query("buying_price_list", function() {
+						return{
+							filters: { 'buying': 1 }
+						}
+					});
 				}
-			}
-		});
 
-
-		this.frm.set_query("manufacturer", "items", function(doc, cdt, cdn) {
-			const row = locals[cdt][cdn];
-			return {
-				query: "erpnext.controllers.queries.item_manufacturer_query",
-				filters:{ 'item_code': row.item_code }
-			}
-		});
-
-		if(this.frm.fields_dict["items"].grid.get_field('item_code')) {
-			this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) {
-				return me.set_query_for_item_tax_template(doc, cdt, cdn)
-			});
-		}
-	}
-
-	refresh(doc) {
-		frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier'};
-
-		this.frm.toggle_display("supplier_name",
-			(this.frm.doc.supplier_name && this.frm.doc.supplier_name!==this.frm.doc.supplier));
-
-		if(this.frm.doc.docstatus==0 &&
-			(this.frm.doctype==="Purchase Order" || this.frm.doctype==="Material Request")) {
-			this.set_from_product_bundle();
-		}
-
-		this.toggle_subcontracting_fields();
-		super.refresh();
-	}
-
-	toggle_subcontracting_fields() {
-		if (in_list(['Purchase Receipt', 'Purchase Invoice'], this.frm.doc.doctype)) {
-			this.frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty',
-				'read_only', this.frm.doc.__onload && this.frm.doc.__onload.backflush_based_on === 'BOM');
-
-			this.frm.set_df_property('supplied_items', 'cannot_add_rows', 1);
-			this.frm.set_df_property('supplied_items', 'cannot_delete_rows', 1);
-		}
-	}
-
-	supplier() {
-		var me = this;
-		erpnext.utils.get_party_details(this.frm, null, null, function(){
-			me.apply_price_list();
-		});
-	}
-
-	supplier_address() {
-		erpnext.utils.get_address_display(this.frm);
-		erpnext.utils.set_taxes_from_address(this.frm, "supplier_address", "supplier_address", "supplier_address");
-	}
-
-	buying_price_list() {
-		this.apply_price_list();
-	}
-
-	discount_percentage(doc, cdt, cdn) {
-		var item = frappe.get_doc(cdt, cdn);
-		item.discount_amount = 0.0;
-		this.price_list_rate(doc, cdt, cdn);
-	}
-
-	discount_amount(doc, cdt, cdn) {
-		var item = frappe.get_doc(cdt, cdn);
-		item.discount_percentage = 0.0;
-		this.price_list_rate(doc, cdt, cdn);
-	}
-
-	qty(doc, cdt, cdn) {
-		if ((doc.doctype == "Purchase Receipt") || (doc.doctype == "Purchase Invoice" && (doc.update_stock || doc.is_return))) {
-			this.calculate_received_qty(doc, cdt, cdn)
-		}
-		super.qty(doc, cdt, cdn);
-	}
-
-	rejected_qty(doc, cdt, cdn) {
-		this.calculate_received_qty(doc, cdt, cdn)
-	}
-
-	calculate_received_qty(doc, cdt, cdn){
-		var item = frappe.get_doc(cdt, cdn);
-		frappe.model.round_floats_in(item, ["qty", "rejected_qty"]);
-
-		if(!doc.is_return && this.validate_negative_quantity(cdt, cdn, item, ["qty", "rejected_qty"])){ return }
-
-		let received_qty = flt(item.qty + item.rejected_qty, precision("received_qty", item));
-		let received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(received_qty);
-
-		frappe.model.set_value(cdt, cdn, "received_qty", received_qty);
-		frappe.model.set_value(cdt, cdn, "received_stock_qty", received_stock_qty);
-	}
-
-	batch_no(doc, cdt, cdn) {
-		super.batch_no(doc, cdt, cdn);
-	}
-
-	validate_negative_quantity(cdt, cdn, item, fieldnames){
-		if(!item || !fieldnames) { return }
-
-		var is_negative_qty = false;
-		for(var i = 0; i<fieldnames.length; i++) {
-			if(item[fieldnames[i]] < 0){
-				frappe.msgprint(__("Row #{0}: {1} can not be negative for item {2}", [item.idx,__(frappe.meta.get_label(cdt, fieldnames[i], cdn)), item.item_code]));
-				is_negative_qty = true;
-				break;
-			}
-		}
-
-		return is_negative_qty
-	}
-
-	warehouse(doc, cdt, cdn) {
-		var item = frappe.get_doc(cdt, cdn);
-		if(item.item_code && item.warehouse) {
-			return this.frm.call({
-				method: "erpnext.stock.get_item_details.get_bin_details",
-				child: item,
-				args: {
-					item_code: item.item_code,
-					warehouse: item.warehouse,
-					company: doc.company,
-					include_child_warehouses: true
+				if(this.frm.fields_dict.tc_name) {
+					this.frm.set_query("tc_name", function() {
+						return{
+							filters: { 'buying': 1 }
+						}
+					});
 				}
-			});
-		}
-	}
 
-	project(doc, cdt, cdn) {
-		var item = frappe.get_doc(cdt, cdn);
-		if(item.project) {
-			$.each(this.frm.doc["items"] || [],
-				function(i, other_item) {
-					if(!other_item.project) {
-						other_item.project = item.project;
-						refresh_field("project", other_item.name, other_item.parentfield);
+				me.frm.set_query('supplier', erpnext.queries.supplier);
+				me.frm.set_query('contact_person', erpnext.queries.contact_query);
+				me.frm.set_query('supplier_address', erpnext.queries.address_query);
+
+				me.frm.set_query('billing_address', erpnext.queries.company_address_query);
+				erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype);
+
+				if(this.frm.fields_dict.supplier) {
+					this.frm.set_query("supplier", function() {
+						return{	query: "erpnext.controllers.queries.supplier_query" }});
+				}
+
+				this.frm.set_query("item_code", "items", function() {
+					if (me.frm.doc.is_subcontracted) {
+						var filters = {'supplier': me.frm.doc.supplier};
+						if (me.frm.doc.is_old_subcontracting_flow) {
+							filters["is_sub_contracted_item"] = 1;
+						}
+						else {
+							filters["is_stock_item"] = 0;
+						}
+
+						return{
+							query: "erpnext.controllers.queries.item_query",
+							filters: filters
+						}
+					}
+					else {
+						return{
+							query: "erpnext.controllers.queries.item_query",
+							filters: { 'supplier': me.frm.doc.supplier, 'is_purchase_item': 1, 'has_variants': 0}
+						}
 					}
 				});
-		}
-	}
 
-	rejected_warehouse(doc, cdt) {
-		// trigger autofill_warehouse only if parent rejected_warehouse field is triggered
-		if (["Purchase Invoice", "Purchase Receipt"].includes(cdt)) {
-			this.autofill_warehouse(doc.items, "rejected_warehouse", doc.rejected_warehouse);
-		}
-	}
 
-	category(doc, cdt, cdn) {
-		// should be the category field of tax table
-		if(cdt != doc.doctype) {
-			this.calculate_taxes_and_totals();
-		}
-	}
-	add_deduct_tax(doc, cdt, cdn) {
-		this.calculate_taxes_and_totals();
-	}
+				this.frm.set_query("manufacturer", "items", function(doc, cdt, cdn) {
+					const row = locals[cdt][cdn];
+					return {
+						query: "erpnext.controllers.queries.item_manufacturer_query",
+						filters:{ 'item_code': row.item_code }
+					}
+				});
 
-	set_from_product_bundle() {
-		var me = this;
-		this.frm.add_custom_button(__("Product Bundle"), function() {
-			erpnext.buying.get_items_from_product_bundle(me.frm);
-		}, __("Get Items From"));
-	}
+				if(this.frm.fields_dict["items"].grid.get_field('item_code')) {
+					this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) {
+						return me.set_query_for_item_tax_template(doc, cdt, cdn)
+					});
+				}
+			}
 
-	shipping_address(){
-		var me = this;
-		erpnext.utils.get_address_display(this.frm, "shipping_address",
-			"shipping_address_display", true);
-	}
+			refresh(doc) {
+				frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier'};
 
-	billing_address() {
-		erpnext.utils.get_address_display(this.frm, "billing_address",
-			"billing_address_display", true);
-	}
+				this.frm.toggle_display("supplier_name",
+					(this.frm.doc.supplier_name && this.frm.doc.supplier_name!==this.frm.doc.supplier));
 
-	tc_name() {
-		this.get_terms();
-	}
+				if(this.frm.doc.docstatus==0 &&
+					(this.frm.doctype==="Purchase Order" || this.frm.doctype==="Material Request")) {
+					this.set_from_product_bundle();
+				}
 
-	update_auto_repeat_reference(doc) {
-		if (doc.auto_repeat) {
-			frappe.call({
-				method:"frappe.automation.doctype.auto_repeat.auto_repeat.update_reference",
-				args:{
-					docname: doc.auto_repeat,
-					reference:doc.name
-				},
-				callback: function(r){
-					if (r.message=="success") {
-						frappe.show_alert({message:__("Auto repeat document updated"), indicator:'green'});
-					} else {
-						frappe.show_alert({message:__("An error occurred during the update process"), indicator:'red'});
+				this.toggle_subcontracting_fields();
+				super.refresh();
+			}
+
+			toggle_subcontracting_fields() {
+				if (in_list(['Purchase Receipt', 'Purchase Invoice'], this.frm.doc.doctype)) {
+					this.frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty',
+						'read_only', this.frm.doc.__onload && this.frm.doc.__onload.backflush_based_on === 'BOM');
+
+					this.frm.set_df_property('supplied_items', 'cannot_add_rows', 1);
+					this.frm.set_df_property('supplied_items', 'cannot_delete_rows', 1);
+				}
+			}
+
+			supplier() {
+				var me = this;
+				erpnext.utils.get_party_details(this.frm, null, null, function(){
+					me.apply_price_list();
+				});
+			}
+
+			supplier_address() {
+				erpnext.utils.get_address_display(this.frm);
+				erpnext.utils.set_taxes_from_address(this.frm, "supplier_address", "supplier_address", "supplier_address");
+			}
+
+			buying_price_list() {
+				this.apply_price_list();
+			}
+
+			discount_percentage(doc, cdt, cdn) {
+				var item = frappe.get_doc(cdt, cdn);
+				item.discount_amount = 0.0;
+				this.price_list_rate(doc, cdt, cdn);
+			}
+
+			discount_amount(doc, cdt, cdn) {
+				var item = frappe.get_doc(cdt, cdn);
+				item.discount_percentage = 0.0;
+				this.price_list_rate(doc, cdt, cdn);
+			}
+
+			qty(doc, cdt, cdn) {
+				if ((doc.doctype == "Purchase Receipt") || (doc.doctype == "Purchase Invoice" && (doc.update_stock || doc.is_return))) {
+					this.calculate_received_qty(doc, cdt, cdn)
+				}
+				super.qty(doc, cdt, cdn);
+			}
+
+			rejected_qty(doc, cdt, cdn) {
+				this.calculate_received_qty(doc, cdt, cdn)
+			}
+
+			calculate_received_qty(doc, cdt, cdn){
+				var item = frappe.get_doc(cdt, cdn);
+				frappe.model.round_floats_in(item, ["qty", "rejected_qty"]);
+
+				if(!doc.is_return && this.validate_negative_quantity(cdt, cdn, item, ["qty", "rejected_qty"])){ return }
+
+				let received_qty = flt(item.qty + item.rejected_qty, precision("received_qty", item));
+				let received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(received_qty);
+
+				frappe.model.set_value(cdt, cdn, "received_qty", received_qty);
+				frappe.model.set_value(cdt, cdn, "received_stock_qty", received_stock_qty);
+			}
+
+			batch_no(doc, cdt, cdn) {
+				super.batch_no(doc, cdt, cdn);
+			}
+
+			validate_negative_quantity(cdt, cdn, item, fieldnames){
+				if(!item || !fieldnames) { return }
+
+				var is_negative_qty = false;
+				for(var i = 0; i<fieldnames.length; i++) {
+					if(item[fieldnames[i]] < 0){
+						frappe.msgprint(__("Row #{0}: {1} can not be negative for item {2}", [item.idx,__(frappe.meta.get_label(cdt, fieldnames[i], cdn)), item.item_code]));
+						is_negative_qty = true;
+						break;
 					}
 				}
-			})
-		}
-	}
 
-	manufacturer(doc, cdt, cdn) {
-		const row = locals[cdt][cdn];
+				return is_negative_qty
+			}
 
-		if(row.manufacturer) {
-			frappe.call({
-				method: "erpnext.stock.doctype.item_manufacturer.item_manufacturer.get_item_manufacturer_part_no",
-				args: {
-					'item_code': row.item_code,
-					'manufacturer': row.manufacturer
-				},
-				callback: function(r) {
-					if (r.message) {
-						frappe.model.set_value(cdt, cdn, 'manufacturer_part_no', r.message);
-					}
-				}
-			});
-		}
-	}
-
-	manufacturer_part_no(doc, cdt, cdn) {
-		const row = locals[cdt][cdn];
-
-		if (row.manufacturer_part_no) {
-			frappe.model.get_value('Item Manufacturer',
-				{
-					'item_code': row.item_code,
-					'manufacturer': row.manufacturer,
-					'manufacturer_part_no': row.manufacturer_part_no
-				},
-				'name',
-				function(data) {
-					if (!data) {
-						let msg = {
-							message: __("Manufacturer Part Number <b>{0}</b> is invalid", [row.manufacturer_part_no]),
-							title: __("Invalid Part Number")
+			warehouse(doc, cdt, cdn) {
+				var item = frappe.get_doc(cdt, cdn);
+				if(item.item_code && item.warehouse) {
+					return this.frm.call({
+						method: "erpnext.stock.get_item_details.get_bin_details",
+						child: item,
+						args: {
+							item_code: item.item_code,
+							warehouse: item.warehouse,
+							company: doc.company,
+							include_child_warehouses: true
 						}
-						frappe.throw(msg);
-					}
-				}
-			);
-		}
-	}
-
-	add_serial_batch_bundle(doc, cdt, cdn) {
-		let item = locals[cdt][cdn];
-		let me = this;
-		let path = "assets/erpnext/js/utils/serial_no_batch_selector.js";
-
-		frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
-			.then((r) => {
-				if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) {
-					item.has_serial_no = r.message.has_serial_no;
-					item.has_batch_no = r.message.has_batch_no;
-					item.type_of_transaction = item.qty > 0 ? "Inward" : "Outward";
-					item.is_rejected = false;
-
-					frappe.require(path, function() {
-						new erpnext.SerialBatchPackageSelector(
-							me.frm, item, (r) => {
-								if (r) {
-									let update_values = {
-										"serial_and_batch_bundle": r.name,
-										"qty": Math.abs(r.total_qty)
-									}
-
-									if (r.warehouse) {
-										update_values["warehouse"] = r.warehouse;
-									}
-
-									frappe.model.set_value(item.doctype, item.name, update_values);
-								}
-							}
-						);
 					});
 				}
-			});
-	}
+			}
 
-	add_serial_batch_for_rejected_qty(doc, cdt, cdn) {
-		let item = locals[cdt][cdn];
-		let me = this;
-		let path = "assets/erpnext/js/utils/serial_no_batch_selector.js";
-
-		frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
-			.then((r) => {
-				if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) {
-					item.has_serial_no = r.message.has_serial_no;
-					item.has_batch_no = r.message.has_batch_no;
-					item.type_of_transaction = item.qty > 0 ? "Inward" : "Outward";
-					item.is_rejected = true;
-
-					frappe.require(path, function() {
-						new erpnext.SerialBatchPackageSelector(
-							me.frm, item, (r) => {
-								if (r) {
-									let update_values = {
-										"serial_and_batch_bundle": r.name,
-										"rejected_qty": Math.abs(r.total_qty)
-									}
-
-									if (r.warehouse) {
-										update_values["rejected_warehouse"] = r.warehouse;
-									}
-
-									frappe.model.set_value(item.doctype, item.name, update_values);
-								}
+			project(doc, cdt, cdn) {
+				var item = frappe.get_doc(cdt, cdn);
+				if(item.project) {
+					$.each(this.frm.doc["items"] || [],
+						function(i, other_item) {
+							if(!other_item.project) {
+								other_item.project = item.project;
+								refresh_field("project", other_item.name, other_item.parentfield);
 							}
-						);
+						});
+				}
+			}
+
+			rejected_warehouse(doc, cdt) {
+				// trigger autofill_warehouse only if parent rejected_warehouse field is triggered
+				if (["Purchase Invoice", "Purchase Receipt"].includes(cdt)) {
+					this.autofill_warehouse(doc.items, "rejected_warehouse", doc.rejected_warehouse);
+				}
+			}
+
+			category(doc, cdt, cdn) {
+				// should be the category field of tax table
+				if(cdt != doc.doctype) {
+					this.calculate_taxes_and_totals();
+				}
+			}
+			add_deduct_tax(doc, cdt, cdn) {
+				this.calculate_taxes_and_totals();
+			}
+
+			set_from_product_bundle() {
+				var me = this;
+				this.frm.add_custom_button(__("Product Bundle"), function() {
+					erpnext.buying.get_items_from_product_bundle(me.frm);
+				}, __("Get Items From"));
+			}
+
+			shipping_address(){
+				var me = this;
+				erpnext.utils.get_address_display(this.frm, "shipping_address",
+					"shipping_address_display", true);
+			}
+
+			billing_address() {
+				erpnext.utils.get_address_display(this.frm, "billing_address",
+					"billing_address_display", true);
+			}
+
+			tc_name() {
+				this.get_terms();
+			}
+
+			update_auto_repeat_reference(doc) {
+				if (doc.auto_repeat) {
+					frappe.call({
+						method:"frappe.automation.doctype.auto_repeat.auto_repeat.update_reference",
+						args:{
+							docname: doc.auto_repeat,
+							reference:doc.name
+						},
+						callback: function(r){
+							if (r.message=="success") {
+								frappe.show_alert({message:__("Auto repeat document updated"), indicator:'green'});
+							} else {
+								frappe.show_alert({message:__("An error occurred during the update process"), indicator:'red'});
+							}
+						}
+					})
+				}
+			}
+
+			manufacturer(doc, cdt, cdn) {
+				const row = locals[cdt][cdn];
+
+				if(row.manufacturer) {
+					frappe.call({
+						method: "erpnext.stock.doctype.item_manufacturer.item_manufacturer.get_item_manufacturer_part_no",
+						args: {
+							'item_code': row.item_code,
+							'manufacturer': row.manufacturer
+						},
+						callback: function(r) {
+							if (r.message) {
+								frappe.model.set_value(cdt, cdn, 'manufacturer_part_no', r.message);
+							}
+						}
 					});
 				}
-			});
-	}
-};
+			}
 
-cur_frm.add_fetch('project', 'cost_center', 'cost_center');
+			manufacturer_part_no(doc, cdt, cdn) {
+				const row = locals[cdt][cdn];
+
+				if (row.manufacturer_part_no) {
+					frappe.model.get_value('Item Manufacturer',
+						{
+							'item_code': row.item_code,
+							'manufacturer': row.manufacturer,
+							'manufacturer_part_no': row.manufacturer_part_no
+						},
+						'name',
+						function(data) {
+							if (!data) {
+								let msg = {
+									message: __("Manufacturer Part Number <b>{0}</b> is invalid", [row.manufacturer_part_no]),
+									title: __("Invalid Part Number")
+								}
+								frappe.throw(msg);
+							}
+						}
+					);
+				}
+			}
+
+			add_serial_batch_bundle(doc, cdt, cdn) {
+				let item = locals[cdt][cdn];
+				let me = this;
+				let path = "assets/erpnext/js/utils/serial_no_batch_selector.js";
+
+				frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
+					.then((r) => {
+						if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) {
+							item.has_serial_no = r.message.has_serial_no;
+							item.has_batch_no = r.message.has_batch_no;
+							item.type_of_transaction = item.qty > 0 ? "Inward" : "Outward";
+							item.is_rejected = false;
+
+							frappe.require(path, function() {
+								new erpnext.SerialBatchPackageSelector(
+									me.frm, item, (r) => {
+										if (r) {
+											let update_values = {
+												"serial_and_batch_bundle": r.name,
+												"qty": Math.abs(r.total_qty)
+											}
+
+											if (r.warehouse) {
+												update_values["warehouse"] = r.warehouse;
+											}
+
+											frappe.model.set_value(item.doctype, item.name, update_values);
+										}
+									}
+								);
+							});
+						}
+					});
+			}
+
+			add_serial_batch_for_rejected_qty(doc, cdt, cdn) {
+				let item = locals[cdt][cdn];
+				let me = this;
+				let path = "assets/erpnext/js/utils/serial_no_batch_selector.js";
+
+				frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
+					.then((r) => {
+						if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) {
+							item.has_serial_no = r.message.has_serial_no;
+							item.has_batch_no = r.message.has_batch_no;
+							item.type_of_transaction = item.qty > 0 ? "Inward" : "Outward";
+							item.is_rejected = true;
+
+							frappe.require(path, function() {
+								new erpnext.SerialBatchPackageSelector(
+									me.frm, item, (r) => {
+										if (r) {
+											let update_values = {
+												"serial_and_batch_bundle": r.name,
+												"rejected_qty": Math.abs(r.total_qty)
+											}
+
+											if (r.warehouse) {
+												update_values["rejected_warehouse"] = r.warehouse;
+											}
+
+											frappe.model.set_value(item.doctype, item.name, update_values);
+										}
+									}
+								);
+							});
+						}
+					});
+			}
+		};
+	}
+}
 
 erpnext.buying.link_to_mrs = function(frm) {
 	frappe.call({
diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js
index 4e028e4..d7bea7b 100644
--- a/erpnext/public/js/erpnext.bundle.js
+++ b/erpnext/public/js/erpnext.bundle.js
@@ -10,7 +10,6 @@
 import "./templates/item_selector.html";
 import "./utils/item_selector";
 import "./help_links";
-import "./agriculture/ternary_plot";
 import "./templates/item_quick_entry.html";
 import "./utils/contact_address_quick_entry";
 import "./utils/customer_quick_entry";
@@ -25,5 +24,9 @@
 import "./utils/crm_activities";
 import "./templates/crm_activities.html";
 import "./templates/crm_notes.html";
+import "./controllers/accounts.js"
+import "./utils/landed_taxes_and_charges_common.js";
+import "./utils/sales_common.js";
+import "./controllers/buying.js";
 
 // import { sum } from 'frappe/public/utils/util.js'
diff --git a/erpnext/public/js/leaflet/leaflet.draw.js b/erpnext/public/js/leaflet/leaflet.draw.js
deleted file mode 100755
index 26f1e19..0000000
--- a/erpnext/public/js/leaflet/leaflet.draw.js
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-	Leaflet.draw, a plugin that adds drawing and editing tools to Leaflet powered maps.
-	(c) 2012-2013, Jacob Toye, Smartrak
-
-	https://github.com/Leaflet/Leaflet.draw
-	http://leafletjs.com
-	https://github.com/jacobtoye
-*/
-! function(t, e) {
-    L.drawVersion = "0.2.3", L.drawLocal = { draw: { toolbar: { actions: { title: "Cancel drawing", text: "Cancel" }, undo: { title: "Delete last point drawn", text: "Delete last point" }, buttons: { polyline: "Draw a polyline", polygon: "Draw a polygon", rectangle: "Draw a rectangle", circle: "Draw a circle", marker: "Draw a marker" } }, handlers: { circle: { tooltip: { start: "Click and drag to draw circle." } }, marker: { tooltip: { start: "Click map to place marker." } }, polygon: { tooltip: { start: "Click to start drawing shape.", cont: "Click to continue drawing shape.", end: "Click first point to close this shape." } }, polyline: { error: "<strong>Error:</strong> shape edges cannot cross!", tooltip: { start: "Click to start drawing line.", cont: "Click to continue drawing line.", end: "Click last point to finish line." } }, rectangle: { tooltip: { start: "Click and drag to draw rectangle." } }, simpleshape: { tooltip: { end: "Release mouse to finish drawing." } } } }, edit: { toolbar: { actions: { save: { title: "Save changes.", text: "Save" }, cancel: { title: "Cancel editing, discards all changes.", text: "Cancel" } }, buttons: { edit: "Edit layers.", editDisabled: "No layers to edit.", remove: "Delete layers.", removeDisabled: "No layers to delete." } }, handlers: { edit: { tooltip: { text: "Drag handles, or marker to edit feature.", subtext: "Click cancel to undo changes." } }, remove: { tooltip: { text: "Click on a feature to remove" } } } } }, L.Draw = {}, L.Draw.Feature = L.Handler.extend({ includes: L.Mixin.Events, initialize: function(t, e) { this._map = t, this._container = t._container, this._overlayPane = t._panes.overlayPane, this._popupPane = t._panes.popupPane, e && e.shapeOptions && (e.shapeOptions = L.Util.extend({}, this.options.shapeOptions, e.shapeOptions)), L.setOptions(this, e) }, enable: function() { this._enabled || (this.fire("enabled", { handler: this.type }), this._map.fire("draw:drawstart", { layerType: this.type }), L.Handler.prototype.enable.call(this)) }, disable: function() { this._enabled && (L.Handler.prototype.disable.call(this), this._map.fire("draw:drawstop", { layerType: this.type }), this.fire("disabled", { handler: this.type })) }, addHooks: function() { var t = this._map;
-            t && (L.DomUtil.disableTextSelection(), t.getContainer().focus(), this._tooltip = new L.Tooltip(this._map), L.DomEvent.on(this._container, "keyup", this._cancelDrawing, this)) }, removeHooks: function() { this._map && (L.DomUtil.enableTextSelection(), this._tooltip.dispose(), this._tooltip = null, L.DomEvent.off(this._container, "keyup", this._cancelDrawing, this)) }, setOptions: function(t) { L.setOptions(this, t) }, _fireCreatedEvent: function(t) { this._map.fire("draw:created", { layer: t, layerType: this.type }) }, _cancelDrawing: function(t) { 27 === t.keyCode && this.disable() } }), L.Draw.Polyline = L.Draw.Feature.extend({ statics: { TYPE: "polyline" }, Poly: L.Polyline, options: { allowIntersection: !0, repeatMode: !1, drawError: { color: "#b00b00", timeout: 2500 }, icon: new L.DivIcon({ iconSize: new L.Point(8, 8), className: "leaflet-div-icon leaflet-editing-icon" }), guidelineDistance: 20, maxGuideLineLength: 4e3, shapeOptions: { stroke: !0, color: "#f06eaa", weight: 4, opacity: .5, fill: !1, clickable: !0 }, metric: !0, showLength: !0, zIndexOffset: 2e3 }, initialize: function(t, e) { this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error, e && e.drawError && (e.drawError = L.Util.extend({}, this.options.drawError, e.drawError)), this.type = L.Draw.Polyline.TYPE, L.Draw.Feature.prototype.initialize.call(this, t, e) }, addHooks: function() { L.Draw.Feature.prototype.addHooks.call(this), this._map && (this._markers = [], this._markerGroup = new L.LayerGroup, this._map.addLayer(this._markerGroup), this._poly = new L.Polyline([], this.options.shapeOptions), this._tooltip.updateContent(this._getTooltipText()), this._mouseMarker || (this._mouseMarker = L.marker(this._map.getCenter(), { icon: L.divIcon({ className: "leaflet-mouse-marker", iconAnchor: [20, 20], iconSize: [40, 40] }), opacity: 0, zIndexOffset: this.options.zIndexOffset })), this._mouseMarker.on("mousedown", this._onMouseDown, this).addTo(this._map), this._map.on("mousemove", this._onMouseMove, this).on("mouseup", this._onMouseUp, this).on("zoomend", this._onZoomEnd, this)) }, removeHooks: function() { L.Draw.Feature.prototype.removeHooks.call(this), this._clearHideErrorTimeout(), this._cleanUpShape(), this._map.removeLayer(this._markerGroup), delete this._markerGroup, delete this._markers, this._map.removeLayer(this._poly), delete this._poly, this._mouseMarker.off("mousedown", this._onMouseDown, this).off("mouseup", this._onMouseUp, this), this._map.removeLayer(this._mouseMarker), delete this._mouseMarker, this._clearGuides(), this._map.off("mousemove", this._onMouseMove, this).off("zoomend", this._onZoomEnd, this) }, deleteLastVertex: function() { if (!(this._markers.length <= 1)) { var t = this._markers.pop(),
-                    e = this._poly,
-                    i = this._poly.spliceLatLngs(e.getLatLngs().length - 1, 1)[0];
-                this._markerGroup.removeLayer(t), e.getLatLngs().length < 2 && this._map.removeLayer(e), this._vertexChanged(i, !1) } }, addVertex: function(t) { var e = this._markers.length; return e > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(t) ? void this._showErrorTooltip() : (this._errorShown && this._hideErrorTooltip(), this._markers.push(this._createMarker(t)), this._poly.addLatLng(t), 2 === this._poly.getLatLngs().length && this._map.addLayer(this._poly), void this._vertexChanged(t, !0)) }, _finishShape: function() { var t = this._poly.newLatLngIntersects(this._poly.getLatLngs()[0], !0); return !this.options.allowIntersection && t || !this._shapeIsValid() ? void this._showErrorTooltip() : (this._fireCreatedEvent(), this.disable(), void(this.options.repeatMode && this.enable())) }, _shapeIsValid: function() { return !0 }, _onZoomEnd: function() { this._updateGuide() }, _onMouseMove: function(t) { var e = t.layerPoint,
-                i = t.latlng;
-            this._currentLatLng = i, this._updateTooltip(i), this._updateGuide(e), this._mouseMarker.setLatLng(i), L.DomEvent.preventDefault(t.originalEvent) }, _vertexChanged: function(t, e) { this._updateFinishHandler(), this._updateRunningMeasure(t, e), this._clearGuides(), this._updateTooltip() }, _onMouseDown: function(t) { var e = t.originalEvent;
-            this._mouseDownOrigin = L.point(e.clientX, e.clientY) }, _onMouseUp: function(e) { if (this._mouseDownOrigin) { var i = L.point(e.originalEvent.clientX, e.originalEvent.clientY).distanceTo(this._mouseDownOrigin);
-                Math.abs(i) < 9 * (t.devicePixelRatio || 1) && this.addVertex(e.latlng) }
-            this._mouseDownOrigin = null }, _updateFinishHandler: function() { var t = this._markers.length;
-            t > 1 && this._markers[t - 1].on("click", this._finishShape, this), t > 2 && this._markers[t - 2].off("click", this._finishShape, this) }, _createMarker: function(t) { var e = new L.Marker(t, { icon: this.options.icon, zIndexOffset: 2 * this.options.zIndexOffset }); return this._markerGroup.addLayer(e), e }, _updateGuide: function(t) { var e = this._markers.length;
-            e > 0 && (t = t || this._map.latLngToLayerPoint(this._currentLatLng), this._clearGuides(), this._drawGuide(this._map.latLngToLayerPoint(this._markers[e - 1].getLatLng()), t)) }, _updateTooltip: function(t) { var e = this._getTooltipText();
-            t && this._tooltip.updatePosition(t), this._errorShown || this._tooltip.updateContent(e) }, _drawGuide: function(t, e) { var i, o, a, s = Math.floor(Math.sqrt(Math.pow(e.x - t.x, 2) + Math.pow(e.y - t.y, 2))),
-                r = this.options.guidelineDistance,
-                n = this.options.maxGuideLineLength,
-                l = s > n ? s - n : r; for (this._guidesContainer || (this._guidesContainer = L.DomUtil.create("div", "leaflet-draw-guides", this._overlayPane)); s > l; l += this.options.guidelineDistance) i = l / s, o = { x: Math.floor(t.x * (1 - i) + i * e.x), y: Math.floor(t.y * (1 - i) + i * e.y) }, a = L.DomUtil.create("div", "leaflet-draw-guide-dash", this._guidesContainer), a.style.backgroundColor = this._errorShown ? this.options.drawError.color : this.options.shapeOptions.color, L.DomUtil.setPosition(a, o) }, _updateGuideColor: function(t) { if (this._guidesContainer)
-                for (var e = 0, i = this._guidesContainer.childNodes.length; i > e; e++) this._guidesContainer.childNodes[e].style.backgroundColor = t }, _clearGuides: function() { if (this._guidesContainer)
-                for (; this._guidesContainer.firstChild;) this._guidesContainer.removeChild(this._guidesContainer.firstChild) }, _getTooltipText: function() { var t, e, i = this.options.showLength; return 0 === this._markers.length ? t = { text: L.drawLocal.draw.handlers.polyline.tooltip.start } : (e = i ? this._getMeasurementString() : "", t = 1 === this._markers.length ? { text: L.drawLocal.draw.handlers.polyline.tooltip.cont, subtext: e } : { text: L.drawLocal.draw.handlers.polyline.tooltip.end, subtext: e }), t }, _updateRunningMeasure: function(t, e) { var i, o, a = this._markers.length;
-            1 === this._markers.length ? this._measurementRunningTotal = 0 : (i = a - (e ? 2 : 1), o = t.distanceTo(this._markers[i].getLatLng()), this._measurementRunningTotal += o * (e ? 1 : -1)) }, _getMeasurementString: function() { var t, e = this._currentLatLng,
-                i = this._markers[this._markers.length - 1].getLatLng(); return t = this._measurementRunningTotal + e.distanceTo(i), L.GeometryUtil.readableDistance(t, this.options.metric) }, _showErrorTooltip: function() { this._errorShown = !0, this._tooltip.showAsError().updateContent({ text: this.options.drawError.message }), this._updateGuideColor(this.options.drawError.color), this._poly.setStyle({ color: this.options.drawError.color }), this._clearHideErrorTimeout(), this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout) }, _hideErrorTooltip: function() { this._errorShown = !1, this._clearHideErrorTimeout(), this._tooltip.removeError().updateContent(this._getTooltipText()), this._updateGuideColor(this.options.shapeOptions.color), this._poly.setStyle({ color: this.options.shapeOptions.color }) }, _clearHideErrorTimeout: function() { this._hideErrorTimeout && (clearTimeout(this._hideErrorTimeout), this._hideErrorTimeout = null) }, _cleanUpShape: function() { this._markers.length > 1 && this._markers[this._markers.length - 1].off("click", this._finishShape, this) }, _fireCreatedEvent: function() { var t = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);
-            L.Draw.Feature.prototype._fireCreatedEvent.call(this, t) } }), L.Draw.Polygon = L.Draw.Polyline.extend({ statics: { TYPE: "polygon" }, Poly: L.Polygon, options: { showArea: !1, shapeOptions: { stroke: !0, color: "#f06eaa", weight: 4, opacity: .5, fill: !0, fillColor: null, fillOpacity: .2, clickable: !0 } }, initialize: function(t, e) { L.Draw.Polyline.prototype.initialize.call(this, t, e), this.type = L.Draw.Polygon.TYPE }, _updateFinishHandler: function() { var t = this._markers.length;
-            1 === t && this._markers[0].on("click", this._finishShape, this), t > 2 && (this._markers[t - 1].on("dblclick", this._finishShape, this), t > 3 && this._markers[t - 2].off("dblclick", this._finishShape, this)) }, _getTooltipText: function() { var t, e; return 0 === this._markers.length ? t = L.drawLocal.draw.handlers.polygon.tooltip.start : this._markers.length < 3 ? t = L.drawLocal.draw.handlers.polygon.tooltip.cont : (t = L.drawLocal.draw.handlers.polygon.tooltip.end, e = this._getMeasurementString()), { text: t, subtext: e } }, _getMeasurementString: function() { var t = this._area; return t ? L.GeometryUtil.readableArea(t, this.options.metric) : null }, _shapeIsValid: function() { return this._markers.length >= 3 }, _vertexAdded: function() { if (!this.options.allowIntersection && this.options.showArea) { var t = this._poly.getLatLngs();
-                this._area = L.GeometryUtil.geodesicArea(t) } }, _cleanUpShape: function() { var t = this._markers.length;
-            t > 0 && (this._markers[0].off("click", this._finishShape, this), t > 2 && this._markers[t - 1].off("dblclick", this._finishShape, this)) } }), L.SimpleShape = {}, L.Draw.SimpleShape = L.Draw.Feature.extend({ options: { repeatMode: !1 }, initialize: function(t, e) { this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end, L.Draw.Feature.prototype.initialize.call(this, t, e) }, addHooks: function() { L.Draw.Feature.prototype.addHooks.call(this), this._map && (this._mapDraggable = this._map.dragging.enabled(), this._mapDraggable && this._map.dragging.disable(), this._container.style.cursor = "crosshair", this._tooltip.updateContent({ text: this._initialLabelText }), this._map.on("mousedown", this._onMouseDown, this).on("mousemove", this._onMouseMove, this)) }, removeHooks: function() { L.Draw.Feature.prototype.removeHooks.call(this), this._map && (this._mapDraggable && this._map.dragging.enable(), this._container.style.cursor = "", this._map.off("mousedown", this._onMouseDown, this).off("mousemove", this._onMouseMove, this), L.DomEvent.off(e, "mouseup", this._onMouseUp, this), this._shape && (this._map.removeLayer(this._shape), delete this._shape)), this._isDrawing = !1 }, _onMouseDown: function(t) { this._isDrawing = !0, this._startLatLng = t.latlng, L.DomEvent.on(e, "mouseup", this._onMouseUp, this).preventDefault(t.originalEvent) }, _onMouseMove: function(t) { var e = t.latlng;
-            this._tooltip.updatePosition(e), this._isDrawing && (this._tooltip.updateContent({ text: this._endLabelText }), this._drawShape(e)) }, _onMouseUp: function() { this._shape && this._fireCreatedEvent(), this.disable(), this.options.repeatMode && this.enable() } }), L.Draw.Rectangle = L.Draw.SimpleShape.extend({ statics: { TYPE: "rectangle" }, options: { shapeOptions: { stroke: !0, color: "#f06eaa", weight: 4, opacity: .5, fill: !0, fillColor: null, fillOpacity: .2, clickable: !0 } }, initialize: function(t, e) { this.type = L.Draw.Rectangle.TYPE, this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start, L.Draw.SimpleShape.prototype.initialize.call(this, t, e) }, _drawShape: function(t) { this._shape ? this._shape.setBounds(new L.LatLngBounds(this._startLatLng, t)) : (this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, t), this.options.shapeOptions), this._map.addLayer(this._shape)) }, _fireCreatedEvent: function() { var t = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions);
-            L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, t) } }), L.Draw.Circle = L.Draw.SimpleShape.extend({ statics: { TYPE: "circle" }, options: { shapeOptions: { stroke: !0, color: "#f06eaa", weight: 4, opacity: .5, fill: !0, fillColor: null, fillOpacity: .2, clickable: !0 }, showRadius: !0, metric: !0 }, initialize: function(t, e) { this.type = L.Draw.Circle.TYPE, this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start, L.Draw.SimpleShape.prototype.initialize.call(this, t, e) }, _drawShape: function(t) { this._shape ? this._shape.setRadius(this._startLatLng.distanceTo(t)) : (this._shape = new L.Circle(this._startLatLng, this._startLatLng.distanceTo(t), this.options.shapeOptions), this._map.addLayer(this._shape)) }, _fireCreatedEvent: function() { var t = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions);
-            L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, t) }, _onMouseMove: function(t) { var e, i = t.latlng,
-                o = this.options.showRadius,
-                a = this.options.metric;
-            this._tooltip.updatePosition(i), this._isDrawing && (this._drawShape(i), e = this._shape.getRadius().toFixed(1), this._tooltip.updateContent({ text: this._endLabelText, subtext: o ? "Radius: " + L.GeometryUtil.readableDistance(e, a) : "" })) } }), L.Draw.Marker = L.Draw.Feature.extend({ statics: { TYPE: "marker" }, options: { icon: new L.Icon.Default, repeatMode: !1, zIndexOffset: 2e3 }, initialize: function(t, e) { this.type = L.Draw.Marker.TYPE, L.Draw.Feature.prototype.initialize.call(this, t, e) }, addHooks: function() { L.Draw.Feature.prototype.addHooks.call(this), this._map && (this._tooltip.updateContent({ text: L.drawLocal.draw.handlers.marker.tooltip.start }), this._mouseMarker || (this._mouseMarker = L.marker(this._map.getCenter(), { icon: L.divIcon({ className: "leaflet-mouse-marker", iconAnchor: [20, 20], iconSize: [40, 40] }), opacity: 0, zIndexOffset: this.options.zIndexOffset })), this._mouseMarker.on("click", this._onClick, this).addTo(this._map), this._map.on("mousemove", this._onMouseMove, this)) }, removeHooks: function() { L.Draw.Feature.prototype.removeHooks.call(this), this._map && (this._marker && (this._marker.off("click", this._onClick, this), this._map.off("click", this._onClick, this).removeLayer(this._marker), delete this._marker), this._mouseMarker.off("click", this._onClick, this), this._map.removeLayer(this._mouseMarker), delete this._mouseMarker, this._map.off("mousemove", this._onMouseMove, this)) }, _onMouseMove: function(t) { var e = t.latlng;
-            this._tooltip.updatePosition(e), this._mouseMarker.setLatLng(e), this._marker ? (e = this._mouseMarker.getLatLng(), this._marker.setLatLng(e)) : (this._marker = new L.Marker(e, { icon: this.options.icon, zIndexOffset: this.options.zIndexOffset }), this._marker.on("click", this._onClick, this), this._map.on("click", this._onClick, this).addLayer(this._marker)) }, _onClick: function() { this._fireCreatedEvent(), this.disable(), this.options.repeatMode && this.enable() }, _fireCreatedEvent: function() { var t = new L.Marker(this._marker.getLatLng(), { icon: this.options.icon });
-            L.Draw.Feature.prototype._fireCreatedEvent.call(this, t) } }), L.Edit = L.Edit || {}, L.Edit.Poly = L.Handler.extend({ options: { icon: new L.DivIcon({ iconSize: new L.Point(8, 8), className: "leaflet-div-icon leaflet-editing-icon" }) }, initialize: function(t, e) { this._poly = t, L.setOptions(this, e) }, addHooks: function() { this._poly._map && (this._markerGroup || this._initMarkers(), this._poly._map.addLayer(this._markerGroup)) }, removeHooks: function() { this._poly._map && (this._poly._map.removeLayer(this._markerGroup), delete this._markerGroup, delete this._markers) }, updateMarkers: function() { this._markerGroup.clearLayers(), this._initMarkers() }, _initMarkers: function() { this._markerGroup || (this._markerGroup = new L.LayerGroup), this._markers = []; var t, e, i, o, a = this._poly._latlngs; for (t = 0, i = a.length; i > t; t++) o = this._createMarker(a[t], t), o.on("click", this._onMarkerClick, this), this._markers.push(o); var s, r; for (t = 0, e = i - 1; i > t; e = t++)(0 !== t || L.Polygon && this._poly instanceof L.Polygon) && (s = this._markers[e], r = this._markers[t], this._createMiddleMarker(s, r), this._updatePrevNext(s, r)) }, _createMarker: function(t, e) { var i = new L.Marker(t, { draggable: !0, icon: this.options.icon }); return i._origLatLng = t, i._index = e, i.on("drag", this._onMarkerDrag, this), i.on("dragend", this._fireEdit, this), this._markerGroup.addLayer(i), i }, _removeMarker: function(t) { var e = t._index;
-            this._markerGroup.removeLayer(t), this._markers.splice(e, 1), this._poly.spliceLatLngs(e, 1), this._updateIndexes(e, -1), t.off("drag", this._onMarkerDrag, this).off("dragend", this._fireEdit, this).off("click", this._onMarkerClick, this) }, _fireEdit: function() { this._poly.edited = !0, this._poly.fire("edit") }, _onMarkerDrag: function(t) { var e = t.target;
-            L.extend(e._origLatLng, e._latlng), e._middleLeft && e._middleLeft.setLatLng(this._getMiddleLatLng(e._prev, e)), e._middleRight && e._middleRight.setLatLng(this._getMiddleLatLng(e, e._next)), this._poly.redraw() }, _onMarkerClick: function(t) { var e = L.Polygon && this._poly instanceof L.Polygon ? 4 : 3,
-                i = t.target;
-            this._poly._latlngs.length < e || (this._removeMarker(i), this._updatePrevNext(i._prev, i._next), i._middleLeft && this._markerGroup.removeLayer(i._middleLeft), i._middleRight && this._markerGroup.removeLayer(i._middleRight), i._prev && i._next ? this._createMiddleMarker(i._prev, i._next) : i._prev ? i._next || (i._prev._middleRight = null) : i._next._middleLeft = null, this._fireEdit()) }, _updateIndexes: function(t, e) { this._markerGroup.eachLayer(function(i) { i._index > t && (i._index += e) }) }, _createMiddleMarker: function(t, e) { var i, o, a, s = this._getMiddleLatLng(t, e),
-                r = this._createMarker(s);
-            r.setOpacity(.6), t._middleRight = e._middleLeft = r, o = function() { var o = e._index;
-                r._index = o, r.off("click", i, this).on("click", this._onMarkerClick, this), s.lat = r.getLatLng().lat, s.lng = r.getLatLng().lng, this._poly.spliceLatLngs(o, 0, s), this._markers.splice(o, 0, r), r.setOpacity(1), this._updateIndexes(o, 1), e._index++, this._updatePrevNext(t, r), this._updatePrevNext(r, e), this._poly.fire("editstart") }, a = function() { r.off("dragstart", o, this), r.off("dragend", a, this), this._createMiddleMarker(t, r), this._createMiddleMarker(r, e) }, i = function() { o.call(this), a.call(this), this._fireEdit() }, r.on("click", i, this).on("dragstart", o, this).on("dragend", a, this), this._markerGroup.addLayer(r) }, _updatePrevNext: function(t, e) { t && (t._next = e), e && (e._prev = t) }, _getMiddleLatLng: function(t, e) { var i = this._poly._map,
-                o = i.project(t.getLatLng()),
-                a = i.project(e.getLatLng()); return i.unproject(o._add(a)._divideBy(2)) } }), L.Polyline.addInitHook(function() { this.editing || (L.Edit.Poly && (this.editing = new L.Edit.Poly(this), this.options.editable && this.editing.enable()), this.on("add", function() { this.editing && this.editing.enabled() && this.editing.addHooks() }), this.on("remove", function() { this.editing && this.editing.enabled() && this.editing.removeHooks() })) }), L.Edit = L.Edit || {}, L.Edit.SimpleShape = L.Handler.extend({ options: { moveIcon: new L.DivIcon({ iconSize: new L.Point(8, 8), className: "leaflet-div-icon leaflet-editing-icon leaflet-edit-move" }), resizeIcon: new L.DivIcon({ iconSize: new L.Point(8, 8), className: "leaflet-div-icon leaflet-editing-icon leaflet-edit-resize" }) }, initialize: function(t, e) { this._shape = t, L.Util.setOptions(this, e) }, addHooks: function() { this._shape._map && (this._map = this._shape._map, this._markerGroup || this._initMarkers(), this._map.addLayer(this._markerGroup)) }, removeHooks: function() { if (this._shape._map) { this._unbindMarker(this._moveMarker); for (var t = 0, e = this._resizeMarkers.length; e > t; t++) this._unbindMarker(this._resizeMarkers[t]);
-                this._resizeMarkers = null, this._map.removeLayer(this._markerGroup), delete this._markerGroup }
-            this._map = null }, updateMarkers: function() { this._markerGroup.clearLayers(), this._initMarkers() }, _initMarkers: function() { this._markerGroup || (this._markerGroup = new L.LayerGroup), this._createMoveMarker(), this._createResizeMarker() }, _createMoveMarker: function() {}, _createResizeMarker: function() {}, _createMarker: function(t, e) { var i = new L.Marker(t, { draggable: !0, icon: e, zIndexOffset: 10 }); return this._bindMarker(i), this._markerGroup.addLayer(i), i }, _bindMarker: function(t) { t.on("dragstart", this._onMarkerDragStart, this).on("drag", this._onMarkerDrag, this).on("dragend", this._onMarkerDragEnd, this) }, _unbindMarker: function(t) { t.off("dragstart", this._onMarkerDragStart, this).off("drag", this._onMarkerDrag, this).off("dragend", this._onMarkerDragEnd, this) }, _onMarkerDragStart: function(t) { var e = t.target;
-            e.setOpacity(0), this._shape.fire("editstart") }, _fireEdit: function() { this._shape.edited = !0, this._shape.fire("edit") }, _onMarkerDrag: function(t) { var e = t.target,
-                i = e.getLatLng();
-            e === this._moveMarker ? this._move(i) : this._resize(i), this._shape.redraw() }, _onMarkerDragEnd: function(t) { var e = t.target;
-            e.setOpacity(1), this._fireEdit() }, _move: function() {}, _resize: function() {} }), L.Edit = L.Edit || {}, L.Edit.Rectangle = L.Edit.SimpleShape.extend({ _createMoveMarker: function() { var t = this._shape.getBounds(),
-                e = t.getCenter();
-            this._moveMarker = this._createMarker(e, this.options.moveIcon) }, _createResizeMarker: function() { var t = this._getCorners();
-            this._resizeMarkers = []; for (var e = 0, i = t.length; i > e; e++) this._resizeMarkers.push(this._createMarker(t[e], this.options.resizeIcon)), this._resizeMarkers[e]._cornerIndex = e }, _onMarkerDragStart: function(t) { L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, t); var e = this._getCorners(),
-                i = t.target,
-                o = i._cornerIndex;
-            this._oppositeCorner = e[(o + 2) % 4], this._toggleCornerMarkers(0, o) }, _onMarkerDragEnd: function(t) { var e, i, o = t.target;
-            o === this._moveMarker && (e = this._shape.getBounds(), i = e.getCenter(), o.setLatLng(i)), this._toggleCornerMarkers(1), this._repositionCornerMarkers(), L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, t) }, _move: function(t) { for (var e, i = this._shape.getLatLngs(), o = this._shape.getBounds(), a = o.getCenter(), s = [], r = 0, n = i.length; n > r; r++) e = [i[r].lat - a.lat, i[r].lng - a.lng], s.push([t.lat + e[0], t.lng + e[1]]);
-            this._shape.setLatLngs(s), this._repositionCornerMarkers() }, _resize: function(t) { var e;
-            this._shape.setBounds(L.latLngBounds(t, this._oppositeCorner)), e = this._shape.getBounds(), this._moveMarker.setLatLng(e.getCenter()) }, _getCorners: function() { var t = this._shape.getBounds(),
-                e = t.getNorthWest(),
-                i = t.getNorthEast(),
-                o = t.getSouthEast(),
-                a = t.getSouthWest(); return [e, i, o, a] }, _toggleCornerMarkers: function(t) { for (var e = 0, i = this._resizeMarkers.length; i > e; e++) this._resizeMarkers[e].setOpacity(t) }, _repositionCornerMarkers: function() { for (var t = this._getCorners(), e = 0, i = this._resizeMarkers.length; i > e; e++) this._resizeMarkers[e].setLatLng(t[e]) } }), L.Rectangle.addInitHook(function() { L.Edit.Rectangle && (this.editing = new L.Edit.Rectangle(this), this.options.editable && this.editing.enable()) }), L.Edit = L.Edit || {}, L.Edit.Circle = L.Edit.SimpleShape.extend({ _createMoveMarker: function() { var t = this._shape.getLatLng();
-            this._moveMarker = this._createMarker(t, this.options.moveIcon) }, _createResizeMarker: function() { var t = this._shape.getLatLng(),
-                e = this._getResizeMarkerPoint(t);
-            this._resizeMarkers = [], this._resizeMarkers.push(this._createMarker(e, this.options.resizeIcon)) }, _getResizeMarkerPoint: function(t) { var e = this._shape._radius * Math.cos(Math.PI / 4),
-                i = this._map.project(t); return this._map.unproject([i.x + e, i.y - e]) }, _move: function(t) { var e = this._getResizeMarkerPoint(t);
-            this._resizeMarkers[0].setLatLng(e), this._shape.setLatLng(t) }, _resize: function(t) { var e = this._moveMarker.getLatLng(),
-                i = e.distanceTo(t);
-            this._shape.setRadius(i) } }), L.Circle.addInitHook(function() { L.Edit.Circle && (this.editing = new L.Edit.Circle(this), this.options.editable && this.editing.enable()), this.on("add", function() { this.editing && this.editing.enabled() && this.editing.addHooks() }), this.on("remove", function() { this.editing && this.editing.enabled() && this.editing.removeHooks() }) }), L.LatLngUtil = { cloneLatLngs: function(t) { for (var e = [], i = 0, o = t.length; o > i; i++) e.push(this.cloneLatLng(t[i])); return e }, cloneLatLng: function(t) { return L.latLng(t.lat, t.lng) } }, L.GeometryUtil = L.extend(L.GeometryUtil || {}, { geodesicArea: function(t) { var e, i, o = t.length,
-                a = 0,
-                s = L.LatLng.DEG_TO_RAD; if (o > 2) { for (var r = 0; o > r; r++) e = t[r], i = t[(r + 1) % o], a += (i.lng - e.lng) * s * (2 + Math.sin(e.lat * s) + Math.sin(i.lat * s));
-                a = 6378137 * a * 6378137 / 2 } return Math.abs(a) }, readableArea: function(t, e) { var i; return e ? i = t >= 1e4 ? (1e-4 * t).toFixed(2) + " ha" : t.toFixed(2) + " m&sup2;" : (t *= .836127, i = t >= 3097600 ? (t / 3097600).toFixed(2) + " mi&sup2;" : t >= 4840 ? (t / 4840).toFixed(2) + " acres" : Math.ceil(t) + " yd&sup2;"), i }, readableDistance: function(t, e) { var i; return e ? i = t > 1e3 ? (t / 1e3).toFixed(2) + " km" : Math.ceil(t) + " m" : (t *= 1.09361, i = t > 1760 ? (t / 1760).toFixed(2) + " miles" : Math.ceil(t) + " yd"), i } }), L.Util.extend(L.LineUtil, { segmentsIntersect: function(t, e, i, o) { return this._checkCounterclockwise(t, i, o) !== this._checkCounterclockwise(e, i, o) && this._checkCounterclockwise(t, e, i) !== this._checkCounterclockwise(t, e, o) }, _checkCounterclockwise: function(t, e, i) { return (i.y - t.y) * (e.x - t.x) > (e.y - t.y) * (i.x - t.x) } }), L.Polyline.include({ intersects: function() { var t, e, i, o = this._originalPoints,
-                a = o ? o.length : 0; if (this._tooFewPointsForIntersection()) return !1; for (t = a - 1; t >= 3; t--)
-                if (e = o[t - 1], i = o[t], this._lineSegmentsIntersectsRange(e, i, t - 2)) return !0;
-            return !1 }, newLatLngIntersects: function(t, e) { return this._map ? this.newPointIntersects(this._map.latLngToLayerPoint(t), e) : !1 }, newPointIntersects: function(t, e) { var i = this._originalPoints,
-                o = i ? i.length : 0,
-                a = i ? i[o - 1] : null,
-                s = o - 2; return this._tooFewPointsForIntersection(1) ? !1 : this._lineSegmentsIntersectsRange(a, t, s, e ? 1 : 0) }, _tooFewPointsForIntersection: function(t) { var e = this._originalPoints,
-                i = e ? e.length : 0; return i += t || 0, !this._originalPoints || 3 >= i }, _lineSegmentsIntersectsRange: function(t, e, i, o) { var a, s, r = this._originalPoints;
-            o = o || 0; for (var n = i; n > o; n--)
-                if (a = r[n - 1], s = r[n], L.LineUtil.segmentsIntersect(t, e, a, s)) return !0;
-            return !1 } }), L.Polygon.include({ intersects: function() { var t, e, i, o, a, s = this._originalPoints; return this._tooFewPointsForIntersection() ? !1 : (t = L.Polyline.prototype.intersects.call(this)) ? !0 : (e = s.length, i = s[0], o = s[e - 1], a = e - 2, this._lineSegmentsIntersectsRange(o, i, a, 1)) } }), L.Control.Draw = L.Control.extend({ options: { position: "topleft", draw: {}, edit: !1 }, initialize: function(t) { if (L.version < "0.7") throw new Error("Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/");
-            L.Control.prototype.initialize.call(this, t); var e, i;
-            this._toolbars = {}, L.DrawToolbar && this.options.draw && (i = new L.DrawToolbar(this.options.draw), e = L.stamp(i), this._toolbars[e] = i, this._toolbars[e].on("enable", this._toolbarEnabled, this)), L.EditToolbar && this.options.edit && (i = new L.EditToolbar(this.options.edit), e = L.stamp(i), this._toolbars[e] = i, this._toolbars[e].on("enable", this._toolbarEnabled, this)) }, onAdd: function(t) { var e, i = L.DomUtil.create("div", "leaflet-draw"),
-                o = !1,
-                a = "leaflet-draw-toolbar-top"; for (var s in this._toolbars) this._toolbars.hasOwnProperty(s) && (e = this._toolbars[s].addToolbar(t), e && (o || (L.DomUtil.hasClass(e, a) || L.DomUtil.addClass(e.childNodes[0], a), o = !0), i.appendChild(e))); return i }, onRemove: function() { for (var t in this._toolbars) this._toolbars.hasOwnProperty(t) && this._toolbars[t].removeToolbar() }, setDrawingOptions: function(t) { for (var e in this._toolbars) this._toolbars[e] instanceof L.DrawToolbar && this._toolbars[e].setOptions(t) }, _toolbarEnabled: function(t) { var e = "" + L.stamp(t.target); for (var i in this._toolbars) this._toolbars.hasOwnProperty(i) && i !== e && this._toolbars[i].disable() } }), L.Map.mergeOptions({ drawControlTooltips: !0, drawControl: !1 }), L.Map.addInitHook(function() { this.options.drawControl && (this.drawControl = new L.Control.Draw, this.addControl(this.drawControl)) }), L.Toolbar = L.Class.extend({
-        includes: [L.Mixin.Events],
-        initialize: function(t) { L.setOptions(this, t), this._modes = {}, this._actionButtons = [], this._activeMode = null },
-        enabled: function() { return null !== this._activeMode },
-        disable: function() { this.enabled() && this._activeMode.handler.disable() },
-        addToolbar: function(t) { var e, i = L.DomUtil.create("div", "leaflet-draw-section"),
-                o = 0,
-                a = this._toolbarClass || "",
-                s = this.getModeHandlers(t); for (this._toolbarContainer = L.DomUtil.create("div", "leaflet-draw-toolbar leaflet-bar"), this._map = t, e = 0; e < s.length; e++) s[e].enabled && this._initModeHandler(s[e].handler, this._toolbarContainer, o++, a, s[e].title); return o ? (this._lastButtonIndex = --o, this._actionsContainer = L.DomUtil.create("ul", "leaflet-draw-actions"), i.appendChild(this._toolbarContainer), i.appendChild(this._actionsContainer), i) : void 0 },
-        removeToolbar: function() { for (var t in this._modes) this._modes.hasOwnProperty(t) && (this._disposeButton(this._modes[t].button, this._modes[t].handler.enable, this._modes[t].handler), this._modes[t].handler.disable(), this._modes[t].handler.off("enabled", this._handlerActivated, this).off("disabled", this._handlerDeactivated, this));
-            this._modes = {}; for (var e = 0, i = this._actionButtons.length; i > e; e++) this._disposeButton(this._actionButtons[e].button, this._actionButtons[e].callback, this);
-            this._actionButtons = [], this._actionsContainer = null },
-        _initModeHandler: function(t, e, i, o, a) { var s = t.type;
-            this._modes[s] = {}, this._modes[s].handler = t, this._modes[s].button = this._createButton({ title: a, className: o + "-" + s, container: e, callback: this._modes[s].handler.enable, context: this._modes[s].handler }), this._modes[s].buttonIndex = i, this._modes[s].handler.on("enabled", this._handlerActivated, this).on("disabled", this._handlerDeactivated, this) },
-        _createButton: function(t) { var e = L.DomUtil.create("a", t.className || "", t.container); return e.href = "#", t.text && (e.innerHTML = t.text), t.title && (e.title = t.title), L.DomEvent.on(e, "click", L.DomEvent.stopPropagation).on(e, "mousedown", L.DomEvent.stopPropagation).on(e, "dblclick", L.DomEvent.stopPropagation).on(e, "click", L.DomEvent.preventDefault).on(e, "click", t.callback, t.context), e },
-        _disposeButton: function(t, e) { L.DomEvent.off(t, "click", L.DomEvent.stopPropagation).off(t, "mousedown", L.DomEvent.stopPropagation).off(t, "dblclick", L.DomEvent.stopPropagation).off(t, "click", L.DomEvent.preventDefault).off(t, "click", e) },
-        _handlerActivated: function(t) { this.disable(), this._activeMode = this._modes[t.handler], L.DomUtil.addClass(this._activeMode.button, "leaflet-draw-toolbar-button-enabled"), this._showActionsToolbar(), this.fire("enable") },
-        _handlerDeactivated: function() { this._hideActionsToolbar(), L.DomUtil.removeClass(this._activeMode.button, "leaflet-draw-toolbar-button-enabled"), this._activeMode = null, this.fire("disable") },
-        _createActions: function(t) { var e, i, o, a, s = this._actionsContainer,
-                r = this.getActions(t),
-                n = r.length; for (i = 0, o = this._actionButtons.length; o > i; i++) this._disposeButton(this._actionButtons[i].button, this._actionButtons[i].callback); for (this._actionButtons = []; s.firstChild;) s.removeChild(s.firstChild); for (var l = 0; n > l; l++) "enabled" in r[l] && !r[l].enabled || (e = L.DomUtil.create("li", "", s), a = this._createButton({ title: r[l].title, text: r[l].text, container: e, callback: r[l].callback, context: r[l].context }), this._actionButtons.push({ button: a, callback: r[l].callback })) },
-        _showActionsToolbar: function() {
-            var t = this._activeMode.buttonIndex,
-                e = this._lastButtonIndex,
-                i = this._activeMode.button.offsetTop - 1;
-            this._createActions(this._activeMode.handler), this._actionsContainer.style.top = i + "px", 0 === t && (L.DomUtil.addClass(this._toolbarContainer, "leaflet-draw-toolbar-notop"), L.DomUtil.addClass(this._actionsContainer, "leaflet-draw-actions-top")), t === e && (L.DomUtil.addClass(this._toolbarContainer, "leaflet-draw-toolbar-nobottom"), L.DomUtil.addClass(this._actionsContainer, "leaflet-draw-actions-bottom")), this._actionsContainer.style.display = "block"
-        },
-        _hideActionsToolbar: function() { this._actionsContainer.style.display = "none", L.DomUtil.removeClass(this._toolbarContainer, "leaflet-draw-toolbar-notop"), L.DomUtil.removeClass(this._toolbarContainer, "leaflet-draw-toolbar-nobottom"), L.DomUtil.removeClass(this._actionsContainer, "leaflet-draw-actions-top"), L.DomUtil.removeClass(this._actionsContainer, "leaflet-draw-actions-bottom") }
-    }), L.Tooltip = L.Class.extend({ initialize: function(t) { this._map = t, this._popupPane = t._panes.popupPane, this._container = t.options.drawControlTooltips ? L.DomUtil.create("div", "leaflet-draw-tooltip", this._popupPane) : null, this._singleLineLabel = !1 }, dispose: function() { this._container && (this._popupPane.removeChild(this._container), this._container = null) }, updateContent: function(t) { return this._container ? (t.subtext = t.subtext || "", 0 !== t.subtext.length || this._singleLineLabel ? t.subtext.length > 0 && this._singleLineLabel && (L.DomUtil.removeClass(this._container, "leaflet-draw-tooltip-single"), this._singleLineLabel = !1) : (L.DomUtil.addClass(this._container, "leaflet-draw-tooltip-single"), this._singleLineLabel = !0), this._container.innerHTML = (t.subtext.length > 0 ? '<span class="leaflet-draw-tooltip-subtext">' + t.subtext + "</span><br />" : "") + "<span>" + t.text + "</span>", this) : this }, updatePosition: function(t) { var e = this._map.latLngToLayerPoint(t),
-                i = this._container; return this._container && (i.style.visibility = "inherit", L.DomUtil.setPosition(i, e)), this }, showAsError: function() { return this._container && L.DomUtil.addClass(this._container, "leaflet-error-draw-tooltip"), this }, removeError: function() { return this._container && L.DomUtil.removeClass(this._container, "leaflet-error-draw-tooltip"), this } }), L.DrawToolbar = L.Toolbar.extend({ options: { polyline: {}, polygon: {}, rectangle: {}, circle: {}, marker: {} }, initialize: function(t) { for (var e in this.options) this.options.hasOwnProperty(e) && t[e] && (t[e] = L.extend({}, this.options[e], t[e]));
-            this._toolbarClass = "leaflet-draw-draw", L.Toolbar.prototype.initialize.call(this, t) }, getModeHandlers: function(t) { return [{ enabled: this.options.polyline, handler: new L.Draw.Polyline(t, this.options.polyline), title: L.drawLocal.draw.toolbar.buttons.polyline }, { enabled: this.options.polygon, handler: new L.Draw.Polygon(t, this.options.polygon), title: L.drawLocal.draw.toolbar.buttons.polygon }, { enabled: this.options.rectangle, handler: new L.Draw.Rectangle(t, this.options.rectangle), title: L.drawLocal.draw.toolbar.buttons.rectangle }, { enabled: this.options.circle, handler: new L.Draw.Circle(t, this.options.circle), title: L.drawLocal.draw.toolbar.buttons.circle }, { enabled: this.options.marker, handler: new L.Draw.Marker(t, this.options.marker), title: L.drawLocal.draw.toolbar.buttons.marker }] }, getActions: function(t) { return [{ enabled: t.deleteLastVertex, title: L.drawLocal.draw.toolbar.undo.title, text: L.drawLocal.draw.toolbar.undo.text, callback: t.deleteLastVertex, context: t }, { title: L.drawLocal.draw.toolbar.actions.title, text: L.drawLocal.draw.toolbar.actions.text, callback: this.disable, context: this }] }, setOptions: function(t) { L.setOptions(this, t); for (var e in this._modes) this._modes.hasOwnProperty(e) && t.hasOwnProperty(e) && this._modes[e].handler.setOptions(t[e]) } }), L.EditToolbar = L.Toolbar.extend({ options: { edit: { selectedPathOptions: { color: "#fe57a1", opacity: .6, dashArray: "10, 10", fill: !0, fillColor: "#fe57a1", fillOpacity: .1 } }, remove: {}, featureGroup: null }, initialize: function(t) { t.edit && ("undefined" == typeof t.edit.selectedPathOptions && (t.edit.selectedPathOptions = this.options.edit.selectedPathOptions), t.edit = L.extend({}, this.options.edit, t.edit)), t.remove && (t.remove = L.extend({}, this.options.remove, t.remove)), this._toolbarClass = "leaflet-draw-edit", L.Toolbar.prototype.initialize.call(this, t), this._selectedFeatureCount = 0 }, getModeHandlers: function(t) { var e = this.options.featureGroup; return [{ enabled: this.options.edit, handler: new L.EditToolbar.Edit(t, { featureGroup: e, selectedPathOptions: this.options.edit.selectedPathOptions }), title: L.drawLocal.edit.toolbar.buttons.edit }, { enabled: this.options.remove, handler: new L.EditToolbar.Delete(t, { featureGroup: e }), title: L.drawLocal.edit.toolbar.buttons.remove }] }, getActions: function() { return [{ title: L.drawLocal.edit.toolbar.actions.save.title, text: L.drawLocal.edit.toolbar.actions.save.text, callback: this._save, context: this }, { title: L.drawLocal.edit.toolbar.actions.cancel.title, text: L.drawLocal.edit.toolbar.actions.cancel.text, callback: this.disable, context: this }] }, addToolbar: function(t) { var e = L.Toolbar.prototype.addToolbar.call(this, t); return this._checkDisabled(), this.options.featureGroup.on("layeradd layerremove", this._checkDisabled, this), e }, removeToolbar: function() { this.options.featureGroup.off("layeradd layerremove", this._checkDisabled, this), L.Toolbar.prototype.removeToolbar.call(this) }, disable: function() { this.enabled() && (this._activeMode.handler.revertLayers(), L.Toolbar.prototype.disable.call(this)) }, _save: function() { this._activeMode.handler.save(), this._activeMode.handler.disable() }, _checkDisabled: function() { var t, e = this.options.featureGroup,
-                i = 0 !== e.getLayers().length;
-            this.options.edit && (t = this._modes[L.EditToolbar.Edit.TYPE].button, i ? L.DomUtil.removeClass(t, "leaflet-disabled") : L.DomUtil.addClass(t, "leaflet-disabled"), t.setAttribute("title", i ? L.drawLocal.edit.toolbar.buttons.edit : L.drawLocal.edit.toolbar.buttons.editDisabled)), this.options.remove && (t = this._modes[L.EditToolbar.Delete.TYPE].button, i ? L.DomUtil.removeClass(t, "leaflet-disabled") : L.DomUtil.addClass(t, "leaflet-disabled"), t.setAttribute("title", i ? L.drawLocal.edit.toolbar.buttons.remove : L.drawLocal.edit.toolbar.buttons.removeDisabled)) } }), L.EditToolbar.Edit = L.Handler.extend({ statics: { TYPE: "edit" }, includes: L.Mixin.Events, initialize: function(t, e) { if (L.Handler.prototype.initialize.call(this, t), this._selectedPathOptions = e.selectedPathOptions, this._featureGroup = e.featureGroup, !(this._featureGroup instanceof L.FeatureGroup)) throw new Error("options.featureGroup must be a L.FeatureGroup");
-            this._uneditedLayerProps = {}, this.type = L.EditToolbar.Edit.TYPE }, enable: function() {!this._enabled && this._hasAvailableLayers() && (this.fire("enabled", { handler: this.type }), this._map.fire("draw:editstart", { handler: this.type }), L.Handler.prototype.enable.call(this), this._featureGroup.on("layeradd", this._enableLayerEdit, this).on("layerremove", this._disableLayerEdit, this)) }, disable: function() { this._enabled && (this._featureGroup.off("layeradd", this._enableLayerEdit, this).off("layerremove", this._disableLayerEdit, this), L.Handler.prototype.disable.call(this), this._map.fire("draw:editstop", { handler: this.type }), this.fire("disabled", { handler: this.type })) }, addHooks: function() { var t = this._map;
-            t && (t.getContainer().focus(), this._featureGroup.eachLayer(this._enableLayerEdit, this), this._tooltip = new L.Tooltip(this._map), this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.edit.tooltip.text, subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext }), this._map.on("mousemove", this._onMouseMove, this)) }, removeHooks: function() { this._map && (this._featureGroup.eachLayer(this._disableLayerEdit, this), this._uneditedLayerProps = {}, this._tooltip.dispose(), this._tooltip = null, this._map.off("mousemove", this._onMouseMove, this)) }, revertLayers: function() { this._featureGroup.eachLayer(function(t) { this._revertLayer(t) }, this) }, save: function() { var t = new L.LayerGroup;
-            this._featureGroup.eachLayer(function(e) { e.edited && (t.addLayer(e), e.edited = !1) }), this._map.fire("draw:edited", { layers: t }) }, _backupLayer: function(t) { var e = L.Util.stamp(t);
-            this._uneditedLayerProps[e] || (t instanceof L.Polyline || t instanceof L.Polygon || t instanceof L.Rectangle ? this._uneditedLayerProps[e] = { latlngs: L.LatLngUtil.cloneLatLngs(t.getLatLngs()) } : t instanceof L.Circle ? this._uneditedLayerProps[e] = { latlng: L.LatLngUtil.cloneLatLng(t.getLatLng()), radius: t.getRadius() } : t instanceof L.Marker && (this._uneditedLayerProps[e] = { latlng: L.LatLngUtil.cloneLatLng(t.getLatLng()) })) }, _revertLayer: function(t) { var e = L.Util.stamp(t);
-            t.edited = !1, this._uneditedLayerProps.hasOwnProperty(e) && (t instanceof L.Polyline || t instanceof L.Polygon || t instanceof L.Rectangle ? t.setLatLngs(this._uneditedLayerProps[e].latlngs) : t instanceof L.Circle ? (t.setLatLng(this._uneditedLayerProps[e].latlng), t.setRadius(this._uneditedLayerProps[e].radius)) : t instanceof L.Marker && t.setLatLng(this._uneditedLayerProps[e].latlng)) }, _toggleMarkerHighlight: function(t) { if (t._icon) { var e = t._icon;
-                e.style.display = "none", L.DomUtil.hasClass(e, "leaflet-edit-marker-selected") ? (L.DomUtil.removeClass(e, "leaflet-edit-marker-selected"), this._offsetMarker(e, -4)) : (L.DomUtil.addClass(e, "leaflet-edit-marker-selected"), this._offsetMarker(e, 4)), e.style.display = "" } }, _offsetMarker: function(t, e) { var i = parseInt(t.style.marginTop, 10) - e,
-                o = parseInt(t.style.marginLeft, 10) - e;
-            t.style.marginTop = i + "px", t.style.marginLeft = o + "px" }, _enableLayerEdit: function(t) { var e, i = t.layer || t.target || t,
-                o = i instanceof L.Marker;
-            (!o || i._icon) && (this._backupLayer(i), this._selectedPathOptions && (e = L.Util.extend({}, this._selectedPathOptions), o ? this._toggleMarkerHighlight(i) : (i.options.previousOptions = L.Util.extend({ dashArray: null }, i.options), i instanceof L.Circle || i instanceof L.Polygon || i instanceof L.Rectangle || (e.fill = !1), i.setStyle(e))), o ? (i.dragging.enable(), i.on("dragend", this._onMarkerDragEnd)) : i.editing.enable()) }, _disableLayerEdit: function(t) { var e = t.layer || t.target || t;
-            e.edited = !1, this._selectedPathOptions && (e instanceof L.Marker ? this._toggleMarkerHighlight(e) : (e.setStyle(e.options.previousOptions), delete e.options.previousOptions)), e instanceof L.Marker ? (e.dragging.disable(), e.off("dragend", this._onMarkerDragEnd, this)) : e.editing.disable() }, _onMarkerDragEnd: function(t) { var e = t.target;
-            e.edited = !0 }, _onMouseMove: function(t) { this._tooltip.updatePosition(t.latlng) }, _hasAvailableLayers: function() { return 0 !== this._featureGroup.getLayers().length } }), L.EditToolbar.Delete = L.Handler.extend({ statics: { TYPE: "remove" }, includes: L.Mixin.Events, initialize: function(t, e) { if (L.Handler.prototype.initialize.call(this, t), L.Util.setOptions(this, e), this._deletableLayers = this.options.featureGroup, !(this._deletableLayers instanceof L.FeatureGroup)) throw new Error("options.featureGroup must be a L.FeatureGroup");
-            this.type = L.EditToolbar.Delete.TYPE }, enable: function() {!this._enabled && this._hasAvailableLayers() && (this.fire("enabled", { handler: this.type }), this._map.fire("draw:deletestart", { handler: this.type }), L.Handler.prototype.enable.call(this), this._deletableLayers.on("layeradd", this._enableLayerDelete, this).on("layerremove", this._disableLayerDelete, this)) }, disable: function() { this._enabled && (this._deletableLayers.off("layeradd", this._enableLayerDelete, this).off("layerremove", this._disableLayerDelete, this), L.Handler.prototype.disable.call(this), this._map.fire("draw:deletestop", { handler: this.type }), this.fire("disabled", { handler: this.type })) }, addHooks: function() { var t = this._map;
-            t && (t.getContainer().focus(), this._deletableLayers.eachLayer(this._enableLayerDelete, this), this._deletedLayers = new L.layerGroup, this._tooltip = new L.Tooltip(this._map), this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.remove.tooltip.text }), this._map.on("mousemove", this._onMouseMove, this)) }, removeHooks: function() { this._map && (this._deletableLayers.eachLayer(this._disableLayerDelete, this), this._deletedLayers = null, this._tooltip.dispose(), this._tooltip = null, this._map.off("mousemove", this._onMouseMove, this)) }, revertLayers: function() { this._deletedLayers.eachLayer(function(t) { this._deletableLayers.addLayer(t) }, this) }, save: function() { this._map.fire("draw:deleted", { layers: this._deletedLayers }) }, _enableLayerDelete: function(t) { var e = t.layer || t.target || t;
-            e.on("click", this._removeLayer, this) }, _disableLayerDelete: function(t) { var e = t.layer || t.target || t;
-            e.off("click", this._removeLayer, this), this._deletedLayers.removeLayer(e) }, _removeLayer: function(t) { var e = t.layer || t.target || t;
-            this._deletableLayers.removeLayer(e), this._deletedLayers.addLayer(e) }, _onMouseMove: function(t) { this._tooltip.updatePosition(t.latlng) }, _hasAvailableLayers: function() { return 0 !== this._deletableLayers.getLayers().length } })
-}(window, document);
diff --git a/erpnext/public/js/leaflet/leaflet.js b/erpnext/public/js/leaflet/leaflet.js
deleted file mode 100755
index 91dd3d4..0000000
--- a/erpnext/public/js/leaflet/leaflet.js
+++ /dev/null
@@ -1,771 +0,0 @@
-/*
- Leaflet 1.0.0-beta.2 (dd0faa1), a JS library for interactive maps. http://leafletjs.com
- (c) 2010-2015 Vladimir Agafonkin, (c) 2010-2011 CloudMade
-*/
-! function(t, e, i) {
-    function n() { var e = t.L;
-        o.noConflict = function() { return t.L = e, this }, t.L = o }
-    var o = { version: "1.0.0-beta.2" };
-    "object" == typeof module && "object" == typeof module.exports ? module.exports = o : "function" == typeof define && define.amd && define(o), "undefined" != typeof t && n(), o.Util = { extend: function(t) { var e, i, n, o; for (i = 1, n = arguments.length; n > i; i++) { o = arguments[i]; for (e in o) t[e] = o[e] } return t }, create: Object.create || function() {
-                function t() {} return function(e) { return t.prototype = e, new t } }(), bind: function(t, e) { var i = Array.prototype.slice; if (t.bind) return t.bind.apply(t, i.call(arguments, 1)); var n = i.call(arguments, 2); return function() { return t.apply(e, n.length ? n.concat(i.call(arguments)) : arguments) } }, stamp: function(t) { return t._leaflet_id = t._leaflet_id || ++o.Util.lastId, t._leaflet_id }, lastId: 0, throttle: function(t, e, i) { var n, o, s, r; return r = function() { n = !1, o && (s.apply(i, o), o = !1) }, s = function() { n ? o = arguments : (t.apply(i, arguments), setTimeout(r, e), n = !0) } }, wrapNum: function(t, e, i) { var n = e[1],
-                    o = e[0],
-                    s = n - o; return t === n && i ? t : ((t - o) % s + s) % s + o }, falseFn: function() { return !1 }, formatNum: function(t, e) { var i = Math.pow(10, e || 5); return Math.round(t * i) / i }, trim: function(t) { return t.trim ? t.trim() : t.replace(/^\s+|\s+$/g, "") }, splitWords: function(t) { return o.Util.trim(t).split(/\s+/) }, setOptions: function(t, e) { t.hasOwnProperty("options") || (t.options = t.options ? o.Util.create(t.options) : {}); for (var i in e) t.options[i] = e[i]; return t.options }, getParamString: function(t, e, i) { var n = []; for (var o in t) n.push(encodeURIComponent(i ? o.toUpperCase() : o) + "=" + encodeURIComponent(t[o])); return (e && -1 !== e.indexOf("?") ? "&" : "?") + n.join("&") }, template: function(t, e) { return t.replace(o.Util.templateRe, function(t, n) { var o = e[n]; if (o === i) throw new Error("No value provided for variable " + t); return "function" == typeof o && (o = o(e)), o }) }, templateRe: /\{ *([\w_]+) *\}/g, isArray: Array.isArray || function(t) { return "[object Array]" === Object.prototype.toString.call(t) }, indexOf: function(t, e) { for (var i = 0; i < t.length; i++)
-                    if (t[i] === e) return i;
-                return -1 }, emptyImageUrl: "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" },
-        function() {
-            function e(e) { return t["webkit" + e] || t["moz" + e] || t["ms" + e] }
-
-            function i(e) { var i = +new Date,
-                    o = Math.max(0, 16 - (i - n)); return n = i + o, t.setTimeout(e, o) } var n = 0,
-                s = t.requestAnimationFrame || e("RequestAnimationFrame") || i,
-                r = t.cancelAnimationFrame || e("CancelAnimationFrame") || e("CancelRequestAnimationFrame") || function(e) { t.clearTimeout(e) };
-            o.Util.requestAnimFrame = function(e, n, r) { return r && s === i ? void e.call(n) : s.call(t, o.bind(e, n)) }, o.Util.cancelAnimFrame = function(e) { e && r.call(t, e) } }(), o.extend = o.Util.extend, o.bind = o.Util.bind, o.stamp = o.Util.stamp, o.setOptions = o.Util.setOptions, o.Class = function() {}, o.Class.extend = function(t) { var e = function() { this.initialize && this.initialize.apply(this, arguments), this.callInitHooks() },
-                i = e.__super__ = this.prototype,
-                n = o.Util.create(i);
-            n.constructor = e, e.prototype = n; for (var s in this) this.hasOwnProperty(s) && "prototype" !== s && (e[s] = this[s]); return t.statics && (o.extend(e, t.statics), delete t.statics), t.includes && (o.Util.extend.apply(null, [n].concat(t.includes)), delete t.includes), n.options && (t.options = o.Util.extend(o.Util.create(n.options), t.options)), o.extend(n, t), n._initHooks = [], n.callInitHooks = function() { if (!this._initHooksCalled) { i.callInitHooks && i.callInitHooks.call(this), this._initHooksCalled = !0; for (var t = 0, e = n._initHooks.length; e > t; t++) n._initHooks[t].call(this) } }, e }, o.Class.include = function(t) { o.extend(this.prototype, t) }, o.Class.mergeOptions = function(t) { o.extend(this.prototype.options, t) }, o.Class.addInitHook = function(t) { var e = Array.prototype.slice.call(arguments, 1),
-                i = "function" == typeof t ? t : function() { this[t].apply(this, e) };
-            this.prototype._initHooks = this.prototype._initHooks || [], this.prototype._initHooks.push(i) }, o.Evented = o.Class.extend({ on: function(t, e, i) { if ("object" == typeof t)
-                    for (var n in t) this._on(n, t[n], e);
-                else { t = o.Util.splitWords(t); for (var s = 0, r = t.length; r > s; s++) this._on(t[s], e, i) } return this }, off: function(t, e, i) { if (t)
-                    if ("object" == typeof t)
-                        for (var n in t) this._off(n, t[n], e);
-                    else { t = o.Util.splitWords(t); for (var s = 0, r = t.length; r > s; s++) this._off(t[s], e, i) }
-                else delete this._events; return this }, _on: function(t, e, i) { var n = this._events = this._events || {},
-                    s = i && i !== this && o.stamp(i); if (s) { var r = t + "_idx",
-                        a = t + "_len",
-                        h = n[r] = n[r] || {},
-                        l = o.stamp(e) + "_" + s;
-                    h[l] || (h[l] = { fn: e, ctx: i }, n[a] = (n[a] || 0) + 1) } else n[t] = n[t] || [], n[t].push({ fn: e }) }, _off: function(t, e, i) { var n = this._events,
-                    s = t + "_idx",
-                    r = t + "_len"; if (n) { if (!e) return delete n[t], delete n[s], void delete n[r]; var a, h, l, u, c, d = i && i !== this && o.stamp(i); if (d) c = o.stamp(e) + "_" + d, a = n[s], a && a[c] && (u = a[c], delete a[c], n[r]--);
-                    else if (a = n[t])
-                        for (h = 0, l = a.length; l > h; h++)
-                            if (a[h].fn === e) { u = a[h], a.splice(h, 1); break }
-                    u && (u.fn = o.Util.falseFn) } }, fire: function(t, e, i) { if (!this.listens(t, i)) return this; var n = o.Util.extend({}, e, { type: t, target: this }),
-                    s = this._events; if (s) { var r, a, h, l, u = s[t + "_idx"]; if (s[t])
-                        for (h = s[t].slice(), r = 0, a = h.length; a > r; r++) h[r].fn.call(this, n); for (l in u) u[l].fn.call(u[l].ctx, n) } return i && this._propagateEvent(n), this }, listens: function(t, e) { var i = this._events; if (i && (i[t] || i[t + "_len"])) return !0; if (e)
-                    for (var n in this._eventParents)
-                        if (this._eventParents[n].listens(t, e)) return !0;
-                return !1 }, once: function(t, e, i) { if ("object" == typeof t) { for (var n in t) this.once(n, t[n], e); return this } var s = o.bind(function() { this.off(t, e, i).off(t, s, i) }, this); return this.on(t, e, i).on(t, s, i) }, addEventParent: function(t) { return this._eventParents = this._eventParents || {}, this._eventParents[o.stamp(t)] = t, this }, removeEventParent: function(t) { return this._eventParents && delete this._eventParents[o.stamp(t)], this }, _propagateEvent: function(t) { for (var e in this._eventParents) this._eventParents[e].fire(t.type, o.extend({ layer: t.target }, t), !0) } });
-    var s = o.Evented.prototype;
-    s.addEventListener = s.on, s.removeEventListener = s.clearAllEventListeners = s.off, s.addOneTimeEventListener = s.once, s.fireEvent = s.fire, s.hasEventListeners = s.listens, o.Mixin = { Events: s },
-        function() { var i = navigator.userAgent.toLowerCase(),
-                n = e.documentElement,
-                s = "ActiveXObject" in t,
-                r = -1 !== i.indexOf("webkit"),
-                a = -1 !== i.indexOf("phantom"),
-                h = -1 !== i.search("android [23]"),
-                l = -1 !== i.indexOf("chrome"),
-                u = -1 !== i.indexOf("gecko") && !r && !t.opera && !s,
-                c = "undefined" != typeof orientation || -1 !== i.indexOf("mobile"),
-                d = !t.PointerEvent && t.MSPointerEvent,
-                _ = t.PointerEvent && navigator.pointerEnabled || d,
-                m = s && "transition" in n.style,
-                p = "WebKitCSSMatrix" in t && "m11" in new t.WebKitCSSMatrix && !h,
-                f = "MozPerspective" in n.style,
-                g = "OTransition" in n.style,
-                v = !t.L_NO_TOUCH && !a && (_ || "ontouchstart" in t || t.DocumentTouch && e instanceof t.DocumentTouch);
-            o.Browser = { ie: s, ielt9: s && !e.addEventListener, webkit: r, gecko: u, android: -1 !== i.indexOf("android"), android23: h, chrome: l, safari: !l && -1 !== i.indexOf("safari"), ie3d: m, webkit3d: p, gecko3d: f, opera12: g, any3d: !t.L_DISABLE_3D && (m || p || f) && !g && !a, mobile: c, mobileWebkit: c && r, mobileWebkit3d: c && p, mobileOpera: c && t.opera, mobileGecko: c && u, touch: !!v, msPointer: !!d, pointer: !!_, retina: (t.devicePixelRatio || t.screen.deviceXDPI / t.screen.logicalXDPI) > 1 } }(), o.Point = function(t, e, i) { this.x = i ? Math.round(t) : t, this.y = i ? Math.round(e) : e }, o.Point.prototype = { clone: function() { return new o.Point(this.x, this.y) }, add: function(t) { return this.clone()._add(o.point(t)) }, _add: function(t) { return this.x += t.x, this.y += t.y, this }, subtract: function(t) { return this.clone()._subtract(o.point(t)) }, _subtract: function(t) { return this.x -= t.x, this.y -= t.y, this }, divideBy: function(t) { return this.clone()._divideBy(t) }, _divideBy: function(t) { return this.x /= t, this.y /= t, this }, multiplyBy: function(t) { return this.clone()._multiplyBy(t) }, _multiplyBy: function(t) { return this.x *= t, this.y *= t, this }, scaleBy: function(t) { return new o.Point(this.x * t.x, this.y * t.y) }, unscaleBy: function(t) { return new o.Point(this.x / t.x, this.y / t.y) }, round: function() { return this.clone()._round() }, _round: function() { return this.x = Math.round(this.x), this.y = Math.round(this.y), this }, floor: function() { return this.clone()._floor() }, _floor: function() { return this.x = Math.floor(this.x), this.y = Math.floor(this.y), this }, ceil: function() { return this.clone()._ceil() }, _ceil: function() { return this.x = Math.ceil(this.x), this.y = Math.ceil(this.y), this }, distanceTo: function(t) { t = o.point(t); var e = t.x - this.x,
-                    i = t.y - this.y; return Math.sqrt(e * e + i * i) }, equals: function(t) { return t = o.point(t), t.x === this.x && t.y === this.y }, contains: function(t) { return t = o.point(t), Math.abs(t.x) <= Math.abs(this.x) && Math.abs(t.y) <= Math.abs(this.y) }, toString: function() { return "Point(" + o.Util.formatNum(this.x) + ", " + o.Util.formatNum(this.y) + ")" } }, o.point = function(t, e, n) { return t instanceof o.Point ? t : o.Util.isArray(t) ? new o.Point(t[0], t[1]) : t === i || null === t ? t : new o.Point(t, e, n) }, o.Bounds = function(t, e) { if (t)
-                for (var i = e ? [t, e] : t, n = 0, o = i.length; o > n; n++) this.extend(i[n]) }, o.Bounds.prototype = { extend: function(t) { return t = o.point(t), this.min || this.max ? (this.min.x = Math.min(t.x, this.min.x), this.max.x = Math.max(t.x, this.max.x), this.min.y = Math.min(t.y, this.min.y), this.max.y = Math.max(t.y, this.max.y)) : (this.min = t.clone(), this.max = t.clone()), this }, getCenter: function(t) { return new o.Point((this.min.x + this.max.x) / 2, (this.min.y + this.max.y) / 2, t) }, getBottomLeft: function() { return new o.Point(this.min.x, this.max.y) }, getTopRight: function() { return new o.Point(this.max.x, this.min.y) }, getSize: function() { return this.max.subtract(this.min) }, contains: function(t) { var e, i; return t = "number" == typeof t[0] || t instanceof o.Point ? o.point(t) : o.bounds(t), t instanceof o.Bounds ? (e = t.min, i = t.max) : e = i = t, e.x >= this.min.x && i.x <= this.max.x && e.y >= this.min.y && i.y <= this.max.y }, intersects: function(t) { t = o.bounds(t); var e = this.min,
-                    i = this.max,
-                    n = t.min,
-                    s = t.max,
-                    r = s.x >= e.x && n.x <= i.x,
-                    a = s.y >= e.y && n.y <= i.y; return r && a }, overlaps: function(t) { t = o.bounds(t); var e = this.min,
-                    i = this.max,
-                    n = t.min,
-                    s = t.max,
-                    r = s.x > e.x && n.x < i.x,
-                    a = s.y > e.y && n.y < i.y; return r && a }, isValid: function() { return !(!this.min || !this.max) } }, o.bounds = function(t, e) { return !t || t instanceof o.Bounds ? t : new o.Bounds(t, e) }, o.Transformation = function(t, e, i, n) { this._a = t, this._b = e, this._c = i, this._d = n }, o.Transformation.prototype = { transform: function(t, e) { return this._transform(t.clone(), e) }, _transform: function(t, e) { return e = e || 1, t.x = e * (this._a * t.x + this._b), t.y = e * (this._c * t.y + this._d), t }, untransform: function(t, e) { return e = e || 1, new o.Point((t.x / e - this._b) / this._a, (t.y / e - this._d) / this._c) } }, o.DomUtil = { get: function(t) { return "string" == typeof t ? e.getElementById(t) : t }, getStyle: function(t, i) { var n = t.style[i] || t.currentStyle && t.currentStyle[i]; if ((!n || "auto" === n) && e.defaultView) { var o = e.defaultView.getComputedStyle(t, null);
-                    n = o ? o[i] : null } return "auto" === n ? null : n }, create: function(t, i, n) { var o = e.createElement(t); return o.className = i, n && n.appendChild(o), o }, remove: function(t) { var e = t.parentNode;
-                e && e.removeChild(t) }, empty: function(t) { for (; t.firstChild;) t.removeChild(t.firstChild) }, toFront: function(t) { t.parentNode.appendChild(t) }, toBack: function(t) { var e = t.parentNode;
-                e.insertBefore(t, e.firstChild) }, hasClass: function(t, e) { if (t.classList !== i) return t.classList.contains(e); var n = o.DomUtil.getClass(t); return n.length > 0 && new RegExp("(^|\\s)" + e + "(\\s|$)").test(n) }, addClass: function(t, e) { if (t.classList !== i)
-                    for (var n = o.Util.splitWords(e), s = 0, r = n.length; r > s; s++) t.classList.add(n[s]);
-                else if (!o.DomUtil.hasClass(t, e)) { var a = o.DomUtil.getClass(t);
-                    o.DomUtil.setClass(t, (a ? a + " " : "") + e) } }, removeClass: function(t, e) { t.classList !== i ? t.classList.remove(e) : o.DomUtil.setClass(t, o.Util.trim((" " + o.DomUtil.getClass(t) + " ").replace(" " + e + " ", " "))) }, setClass: function(t, e) { t.className.baseVal === i ? t.className = e : t.className.baseVal = e }, getClass: function(t) { return t.className.baseVal === i ? t.className : t.className.baseVal }, setOpacity: function(t, e) { "opacity" in t.style ? t.style.opacity = e : "filter" in t.style && o.DomUtil._setOpacityIE(t, e) }, _setOpacityIE: function(t, e) { var i = !1,
-                    n = "DXImageTransform.Microsoft.Alpha"; try { i = t.filters.item(n) } catch (o) { if (1 === e) return }
-                e = Math.round(100 * e), i ? (i.Enabled = 100 !== e, i.Opacity = e) : t.style.filter += " progid:" + n + "(opacity=" + e + ")" }, testProp: function(t) { for (var i = e.documentElement.style, n = 0; n < t.length; n++)
-                    if (t[n] in i) return t[n];
-                return !1 }, setTransform: function(t, e, i) { var n = e || new o.Point(0, 0);
-                t.style[o.DomUtil.TRANSFORM] = (o.Browser.ie3d ? "translate(" + n.x + "px," + n.y + "px)" : "translate3d(" + n.x + "px," + n.y + "px,0)") + (i ? " scale(" + i + ")" : "") }, setPosition: function(t, e) { t._leaflet_pos = e, o.Browser.any3d ? o.DomUtil.setTransform(t, e) : (t.style.left = e.x + "px", t.style.top = e.y + "px") }, getPosition: function(t) { return t._leaflet_pos } },
-        function() { o.DomUtil.TRANSFORM = o.DomUtil.testProp(["transform", "WebkitTransform", "OTransform", "MozTransform", "msTransform"]); var i = o.DomUtil.TRANSITION = o.DomUtil.testProp(["webkitTransition", "transition", "OTransition", "MozTransition", "msTransition"]); if (o.DomUtil.TRANSITION_END = "webkitTransition" === i || "OTransition" === i ? i + "End" : "transitionend", "onselectstart" in e) o.DomUtil.disableTextSelection = function() { o.DomEvent.on(t, "selectstart", o.DomEvent.preventDefault) }, o.DomUtil.enableTextSelection = function() { o.DomEvent.off(t, "selectstart", o.DomEvent.preventDefault) };
-            else { var n = o.DomUtil.testProp(["userSelect", "WebkitUserSelect", "OUserSelect", "MozUserSelect", "msUserSelect"]);
-                o.DomUtil.disableTextSelection = function() { if (n) { var t = e.documentElement.style;
-                        this._userSelect = t[n], t[n] = "none" } }, o.DomUtil.enableTextSelection = function() { n && (e.documentElement.style[n] = this._userSelect, delete this._userSelect) } }
-            o.DomUtil.disableImageDrag = function() { o.DomEvent.on(t, "dragstart", o.DomEvent.preventDefault) }, o.DomUtil.enableImageDrag = function() { o.DomEvent.off(t, "dragstart", o.DomEvent.preventDefault) }, o.DomUtil.preventOutline = function(e) { for (; - 1 === e.tabIndex;) e = e.parentNode;
-                e && e.style && (o.DomUtil.restoreOutline(), this._outlineElement = e, this._outlineStyle = e.style.outline, e.style.outline = "none", o.DomEvent.on(t, "keydown", o.DomUtil.restoreOutline, this)) }, o.DomUtil.restoreOutline = function() { this._outlineElement && (this._outlineElement.style.outline = this._outlineStyle, delete this._outlineElement, delete this._outlineStyle, o.DomEvent.off(t, "keydown", o.DomUtil.restoreOutline, this)) } }(), o.LatLng = function(t, e, n) { if (isNaN(t) || isNaN(e)) throw new Error("Invalid LatLng object: (" + t + ", " + e + ")");
-            this.lat = +t, this.lng = +e, n !== i && (this.alt = +n) }, o.LatLng.prototype = { equals: function(t, e) { if (!t) return !1;
-                t = o.latLng(t); var n = Math.max(Math.abs(this.lat - t.lat), Math.abs(this.lng - t.lng)); return (e === i ? 1e-9 : e) >= n }, toString: function(t) { return "LatLng(" + o.Util.formatNum(this.lat, t) + ", " + o.Util.formatNum(this.lng, t) + ")" }, distanceTo: function(t) { return o.CRS.Earth.distance(this, o.latLng(t)) }, wrap: function() { return o.CRS.Earth.wrapLatLng(this) }, toBounds: function(t) { var e = 180 * t / 40075017,
-                    i = e / Math.cos(Math.PI / 180 * this.lat); return o.latLngBounds([this.lat - e, this.lng - i], [this.lat + e, this.lng + i]) }, clone: function() { return new o.LatLng(this.lat, this.lng, this.alt) } }, o.latLng = function(t, e, n) { return t instanceof o.LatLng ? t : o.Util.isArray(t) && "object" != typeof t[0] ? 3 === t.length ? new o.LatLng(t[0], t[1], t[2]) : 2 === t.length ? new o.LatLng(t[0], t[1]) : null : t === i || null === t ? t : "object" == typeof t && "lat" in t ? new o.LatLng(t.lat, "lng" in t ? t.lng : t.lon, t.alt) : e === i ? null : new o.LatLng(t, e, n) }, o.LatLngBounds = function(t, e) { if (t)
-                for (var i = e ? [t, e] : t, n = 0, o = i.length; o > n; n++) this.extend(i[n]) }, o.LatLngBounds.prototype = { extend: function(t) { var e, i, n = this._southWest,
-                    s = this._northEast; if (t instanceof o.LatLng) e = t, i = t;
-                else { if (!(t instanceof o.LatLngBounds)) return t ? this.extend(o.latLng(t) || o.latLngBounds(t)) : this; if (e = t._southWest, i = t._northEast, !e || !i) return this } return n || s ? (n.lat = Math.min(e.lat, n.lat), n.lng = Math.min(e.lng, n.lng), s.lat = Math.max(i.lat, s.lat), s.lng = Math.max(i.lng, s.lng)) : (this._southWest = new o.LatLng(e.lat, e.lng), this._northEast = new o.LatLng(i.lat, i.lng)), this }, pad: function(t) { var e = this._southWest,
-                    i = this._northEast,
-                    n = Math.abs(e.lat - i.lat) * t,
-                    s = Math.abs(e.lng - i.lng) * t; return new o.LatLngBounds(new o.LatLng(e.lat - n, e.lng - s), new o.LatLng(i.lat + n, i.lng + s)) }, getCenter: function() { return new o.LatLng((this._southWest.lat + this._northEast.lat) / 2, (this._southWest.lng + this._northEast.lng) / 2) }, getSouthWest: function() { return this._southWest }, getNorthEast: function() { return this._northEast }, getNorthWest: function() { return new o.LatLng(this.getNorth(), this.getWest()) }, getSouthEast: function() { return new o.LatLng(this.getSouth(), this.getEast()) }, getWest: function() { return this._southWest.lng }, getSouth: function() { return this._southWest.lat }, getEast: function() { return this._northEast.lng }, getNorth: function() { return this._northEast.lat }, contains: function(t) { t = "number" == typeof t[0] || t instanceof o.LatLng ? o.latLng(t) : o.latLngBounds(t); var e, i, n = this._southWest,
-                    s = this._northEast; return t instanceof o.LatLngBounds ? (e = t.getSouthWest(), i = t.getNorthEast()) : e = i = t, e.lat >= n.lat && i.lat <= s.lat && e.lng >= n.lng && i.lng <= s.lng }, intersects: function(t) { t = o.latLngBounds(t); var e = this._southWest,
-                    i = this._northEast,
-                    n = t.getSouthWest(),
-                    s = t.getNorthEast(),
-                    r = s.lat >= e.lat && n.lat <= i.lat,
-                    a = s.lng >= e.lng && n.lng <= i.lng; return r && a }, overlaps: function(t) { t = o.latLngBounds(t); var e = this._southWest,
-                    i = this._northEast,
-                    n = t.getSouthWest(),
-                    s = t.getNorthEast(),
-                    r = s.lat > e.lat && n.lat < i.lat,
-                    a = s.lng > e.lng && n.lng < i.lng; return r && a }, toBBoxString: function() { return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(",") }, equals: function(t) { return t ? (t = o.latLngBounds(t), this._southWest.equals(t.getSouthWest()) && this._northEast.equals(t.getNorthEast())) : !1 }, isValid: function() { return !(!this._southWest || !this._northEast) } }, o.latLngBounds = function(t, e) { return !t || t instanceof o.LatLngBounds ? t : new o.LatLngBounds(t, e) }, o.Projection = {}, o.Projection.LonLat = { project: function(t) { return new o.Point(t.lng, t.lat) }, unproject: function(t) { return new o.LatLng(t.y, t.x) }, bounds: o.bounds([-180, -90], [180, 90]) }, o.Projection.SphericalMercator = { R: 6378137, MAX_LATITUDE: 85.0511287798, project: function(t) { var e = Math.PI / 180,
-                    i = this.MAX_LATITUDE,
-                    n = Math.max(Math.min(i, t.lat), -i),
-                    s = Math.sin(n * e); return new o.Point(this.R * t.lng * e, this.R * Math.log((1 + s) / (1 - s)) / 2) }, unproject: function(t) { var e = 180 / Math.PI; return new o.LatLng((2 * Math.atan(Math.exp(t.y / this.R)) - Math.PI / 2) * e, t.x * e / this.R) }, bounds: function() { var t = 6378137 * Math.PI; return o.bounds([-t, -t], [t, t]) }() }, o.CRS = { latLngToPoint: function(t, e) { var i = this.projection.project(t),
-                    n = this.scale(e); return this.transformation._transform(i, n) }, pointToLatLng: function(t, e) { var i = this.scale(e),
-                    n = this.transformation.untransform(t, i); return this.projection.unproject(n) }, project: function(t) { return this.projection.project(t) }, unproject: function(t) { return this.projection.unproject(t) }, scale: function(t) { return 256 * Math.pow(2, t) }, zoom: function(t) { return Math.log(t / 256) / Math.LN2 }, getProjectedBounds: function(t) { if (this.infinite) return null; var e = this.projection.bounds,
-                    i = this.scale(t),
-                    n = this.transformation.transform(e.min, i),
-                    s = this.transformation.transform(e.max, i); return o.bounds(n, s) }, wrapLatLng: function(t) { var e = this.wrapLng ? o.Util.wrapNum(t.lng, this.wrapLng, !0) : t.lng,
-                    i = this.wrapLat ? o.Util.wrapNum(t.lat, this.wrapLat, !0) : t.lat,
-                    n = t.alt; return o.latLng(i, e, n) } }, o.CRS.Simple = o.extend({}, o.CRS, { projection: o.Projection.LonLat, transformation: new o.Transformation(1, 0, -1, 0), scale: function(t) { return Math.pow(2, t) }, zoom: function(t) { return Math.log(t) / Math.LN2 }, distance: function(t, e) { var i = e.lng - t.lng,
-                    n = e.lat - t.lat; return Math.sqrt(i * i + n * n) }, infinite: !0 }), o.CRS.Earth = o.extend({}, o.CRS, { wrapLng: [-180, 180], R: 6378137, distance: function(t, e) { var i = Math.PI / 180,
-                    n = t.lat * i,
-                    o = e.lat * i,
-                    s = Math.sin(n) * Math.sin(o) + Math.cos(n) * Math.cos(o) * Math.cos((e.lng - t.lng) * i); return this.R * Math.acos(Math.min(s, 1)) } }), o.CRS.EPSG3857 = o.extend({}, o.CRS.Earth, { code: "EPSG:3857", projection: o.Projection.SphericalMercator, transformation: function() { var t = .5 / (Math.PI * o.Projection.SphericalMercator.R); return new o.Transformation(t, .5, -t, .5) }() }), o.CRS.EPSG900913 = o.extend({}, o.CRS.EPSG3857, { code: "EPSG:900913" }), o.CRS.EPSG4326 = o.extend({}, o.CRS.Earth, { code: "EPSG:4326", projection: o.Projection.LonLat, transformation: new o.Transformation(1 / 180, 1, -1 / 180, .5) }), o.Map = o.Evented.extend({
-            options: { crs: o.CRS.EPSG3857, fadeAnimation: !0, trackResize: !0, markerZoomAnimation: !0, maxBoundsViscosity: 0, transform3DLimit: 8388608 },
-            initialize: function(t, e) { e = o.setOptions(this, e), this._initContainer(t), this._initLayout(), this._onResize = o.bind(this._onResize, this), this._initEvents(), e.maxBounds && this.setMaxBounds(e.maxBounds), e.zoom !== i && (this._zoom = this._limitZoom(e.zoom)), e.center && e.zoom !== i && this.setView(o.latLng(e.center), e.zoom, { reset: !0 }), this._handlers = [], this._layers = {}, this._zoomBoundLayers = {}, this._sizeChanged = !0, this.callInitHooks(), this._addLayers(this.options.layers) },
-            setView: function(t, e) { return e = e === i ? this.getZoom() : e, this._resetView(o.latLng(t), e), this },
-            setZoom: function(t, e) { return this._loaded ? this.setView(this.getCenter(), t, { zoom: e }) : (this._zoom = t, this) },
-            zoomIn: function(t, e) { return this.setZoom(this._zoom + (t || 1), e) },
-            zoomOut: function(t, e) { return this.setZoom(this._zoom - (t || 1), e) },
-            setZoomAround: function(t, e, i) { var n = this.getZoomScale(e),
-                    s = this.getSize().divideBy(2),
-                    r = t instanceof o.Point ? t : this.latLngToContainerPoint(t),
-                    a = r.subtract(s).multiplyBy(1 - 1 / n),
-                    h = this.containerPointToLatLng(s.add(a)); return this.setView(h, e, { zoom: i }) },
-            _getBoundsCenterZoom: function(t, e) { e = e || {}, t = t.getBounds ? t.getBounds() : o.latLngBounds(t); var i = o.point(e.paddingTopLeft || e.padding || [0, 0]),
-                    n = o.point(e.paddingBottomRight || e.padding || [0, 0]),
-                    s = this.getBoundsZoom(t, !1, i.add(n));
-                s = e.maxZoom ? Math.min(e.maxZoom, s) : s; var r = n.subtract(i).divideBy(2),
-                    a = this.project(t.getSouthWest(), s),
-                    h = this.project(t.getNorthEast(), s),
-                    l = this.unproject(a.add(h).divideBy(2).add(r), s); return { center: l, zoom: s } },
-            fitBounds: function(t, e) { var i = this._getBoundsCenterZoom(t, e); return this.setView(i.center, i.zoom, e) },
-            fitWorld: function(t) { return this.fitBounds([
-                    [-90, -180],
-                    [90, 180]
-                ], t) },
-            panTo: function(t, e) { return this.setView(t, this._zoom, { pan: e }) },
-            panBy: function(t) { return this.fire("movestart"), this._rawPanBy(o.point(t)), this.fire("move"), this.fire("moveend") },
-            setMaxBounds: function(t) { return (t = o.latLngBounds(t)) ? (this.options.maxBounds && this.off("moveend", this._panInsideMaxBounds), this.options.maxBounds = t, this._loaded && this._panInsideMaxBounds(), this.on("moveend", this._panInsideMaxBounds)) : this.off("moveend", this._panInsideMaxBounds) },
-            setMinZoom: function(t) { return this.options.minZoom = t, this._loaded && this.getZoom() < this.options.minZoom ? this.setZoom(t) : this },
-            setMaxZoom: function(t) { return this.options.maxZoom = t, this._loaded && this.getZoom() > this.options.maxZoom ? this.setZoom(t) : this },
-            panInsideBounds: function(t, e) { this._enforcingBounds = !0; var i = this.getCenter(),
-                    n = this._limitCenter(i, this._zoom, o.latLngBounds(t)); return i.equals(n) ? this : (this.panTo(n, e), this._enforcingBounds = !1, this) },
-            invalidateSize: function(t) { if (!this._loaded) return this;
-                t = o.extend({ animate: !1, pan: !0 }, t === !0 ? { animate: !0 } : t); var e = this.getSize();
-                this._sizeChanged = !0, this._lastCenter = null; var i = this.getSize(),
-                    n = e.divideBy(2).round(),
-                    s = i.divideBy(2).round(),
-                    r = n.subtract(s); return r.x || r.y ? (t.animate && t.pan ? this.panBy(r) : (t.pan && this._rawPanBy(r), this.fire("move"), t.debounceMoveend ? (clearTimeout(this._sizeTimer), this._sizeTimer = setTimeout(o.bind(this.fire, this, "moveend"), 200)) : this.fire("moveend")), this.fire("resize", { oldSize: e, newSize: i })) : this },
-            stop: function() { return o.Util.cancelAnimFrame(this._flyToFrame), this._panAnim && this._panAnim.stop(), this },
-            addHandler: function(t, e) { if (!e) return this; var i = this[t] = new e(this); return this._handlers.push(i), this.options[t] && i.enable(), this },
-            remove: function() { this._initEvents(!0); try { delete this._container._leaflet } catch (t) { this._container._leaflet = i }
-                o.DomUtil.remove(this._mapPane), this._clearControlPos && this._clearControlPos(), this._clearHandlers(), this._loaded && this.fire("unload"); for (var e in this._layers) this._layers[e].remove(); return this },
-            createPane: function(t, e) { var i = "leaflet-pane" + (t ? " leaflet-" + t.replace("Pane", "") + "-pane" : ""),
-                    n = o.DomUtil.create("div", i, e || this._mapPane); return t && (this._panes[t] = n), n },
-            getCenter: function() { return this._checkIfLoaded(), this._lastCenter && !this._moved() ? this._lastCenter : this.layerPointToLatLng(this._getCenterLayerPoint()) },
-            getZoom: function() { return this._zoom },
-            getBounds: function() { var t = this.getPixelBounds(),
-                    e = this.unproject(t.getBottomLeft()),
-                    i = this.unproject(t.getTopRight()); return new o.LatLngBounds(e, i) },
-            getMinZoom: function() { return this.options.minZoom === i ? this._layersMinZoom || 0 : this.options.minZoom },
-            getMaxZoom: function() { return this.options.maxZoom === i ? this._layersMaxZoom === i ? 1 / 0 : this._layersMaxZoom : this.options.maxZoom },
-            getBoundsZoom: function(t, e, i) { t = o.latLngBounds(t); var n, s = this.getMinZoom() - (e ? 1 : 0),
-                    r = this.getMaxZoom(),
-                    a = this.getSize(),
-                    h = t.getNorthWest(),
-                    l = t.getSouthEast(),
-                    u = !0;
-                i = o.point(i || [0, 0]);
-                do s++, n = this.project(l, s).subtract(this.project(h, s)).add(i).floor(), u = e ? n.x < a.x || n.y < a.y : a.contains(n); while (u && r >= s); return u && e ? null : e ? s : s - 1 },
-            getSize: function() { return (!this._size || this._sizeChanged) && (this._size = new o.Point(this._container.clientWidth, this._container.clientHeight), this._sizeChanged = !1), this._size.clone() },
-            getPixelBounds: function(t, e) { var i = this._getTopLeftPoint(t, e); return new o.Bounds(i, i.add(this.getSize())) },
-            getPixelOrigin: function() { return this._checkIfLoaded(), this._pixelOrigin },
-            getPixelWorldBounds: function(t) { return this.options.crs.getProjectedBounds(t === i ? this.getZoom() : t) },
-            getPane: function(t) { return "string" == typeof t ? this._panes[t] : t },
-            getPanes: function() { return this._panes },
-            getContainer: function() { return this._container },
-            getZoomScale: function(t, e) { var n = this.options.crs; return e = e === i ? this._zoom : e, n.scale(t) / n.scale(e) },
-            getScaleZoom: function(t, e) { var n = this.options.crs; return e = e === i ? this._zoom : e, n.zoom(t * n.scale(e)) },
-            project: function(t, e) { return e = e === i ? this._zoom : e, this.options.crs.latLngToPoint(o.latLng(t), e) },
-            unproject: function(t, e) { return e = e === i ? this._zoom : e, this.options.crs.pointToLatLng(o.point(t), e) },
-            layerPointToLatLng: function(t) { var e = o.point(t).add(this.getPixelOrigin()); return this.unproject(e) },
-            latLngToLayerPoint: function(t) { var e = this.project(o.latLng(t))._round(); return e._subtract(this.getPixelOrigin()) },
-            wrapLatLng: function(t) { return this.options.crs.wrapLatLng(o.latLng(t)) },
-            distance: function(t, e) { return this.options.crs.distance(o.latLng(t), o.latLng(e)) },
-            containerPointToLayerPoint: function(t) { return o.point(t).subtract(this._getMapPanePos()) },
-            layerPointToContainerPoint: function(t) { return o.point(t).add(this._getMapPanePos()) },
-            containerPointToLatLng: function(t) { var e = this.containerPointToLayerPoint(o.point(t)); return this.layerPointToLatLng(e) },
-            latLngToContainerPoint: function(t) { return this.layerPointToContainerPoint(this.latLngToLayerPoint(o.latLng(t))) },
-            mouseEventToContainerPoint: function(t) { return o.DomEvent.getMousePosition(t, this._container) },
-            mouseEventToLayerPoint: function(t) { return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t)) },
-            mouseEventToLatLng: function(t) { return this.layerPointToLatLng(this.mouseEventToLayerPoint(t)) },
-            _initContainer: function(t) { var e = this._container = o.DomUtil.get(t); if (!e) throw new Error("Map container not found."); if (e._leaflet) throw new Error("Map container is already initialized.");
-                o.DomEvent.addListener(e, "scroll", this._onScroll, this), e._leaflet = !0 },
-            _initLayout: function() { var t = this._container;
-                this._fadeAnimated = this.options.fadeAnimation && o.Browser.any3d, o.DomUtil.addClass(t, "leaflet-container" + (o.Browser.touch ? " leaflet-touch" : "") + (o.Browser.retina ? " leaflet-retina" : "") + (o.Browser.ielt9 ? " leaflet-oldie" : "") + (o.Browser.safari ? " leaflet-safari" : "") + (this._fadeAnimated ? " leaflet-fade-anim" : "")); var e = o.DomUtil.getStyle(t, "position"); "absolute" !== e && "relative" !== e && "fixed" !== e && (t.style.position = "relative"), this._initPanes(), this._initControlPos && this._initControlPos() },
-            _initPanes: function() { var t = this._panes = {};
-                this._paneRenderers = {}, this._mapPane = this.createPane("mapPane", this._container), o.DomUtil.setPosition(this._mapPane, new o.Point(0, 0)), this.createPane("tilePane"), this.createPane("shadowPane"), this.createPane("overlayPane"), this.createPane("markerPane"), this.createPane("popupPane"), this.options.markerZoomAnimation || (o.DomUtil.addClass(t.markerPane, "leaflet-zoom-hide"), o.DomUtil.addClass(t.shadowPane, "leaflet-zoom-hide")) },
-            _resetView: function(t, e) { o.DomUtil.setPosition(this._mapPane, new o.Point(0, 0)); var i = !this._loaded;
-                this._loaded = !0, e = this._limitZoom(e); var n = this._zoom !== e;
-                this._moveStart(n)._move(t, e)._moveEnd(n), this.fire("viewreset"), i && this.fire("load") },
-            _moveStart: function(t) { return t && this.fire("zoomstart"), this.fire("movestart") },
-            _move: function(t, e, n) { e === i && (e = this._zoom); var o = this._zoom !== e; return this._zoom = e, this._lastCenter = t, this._pixelOrigin = this._getNewPixelOrigin(t), o && this.fire("zoom", n), this.fire("move", n) },
-            _moveEnd: function(t) { return t && this.fire("zoomend"), this.fire("moveend") },
-            _rawPanBy: function(t) { o.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(t)) },
-            _getZoomSpan: function() { return this.getMaxZoom() - this.getMinZoom() },
-            _panInsideMaxBounds: function() { this._enforcingBounds || this.panInsideBounds(this.options.maxBounds) },
-            _checkIfLoaded: function() { if (!this._loaded) throw new Error("Set map center and zoom first.") },
-            _initEvents: function(e) { if (o.DomEvent) { this._targets = {}, this._targets[o.stamp(this._container)] = this; var i = e ? "off" : "on";
-                    o.DomEvent[i](this._container, "click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress", this._handleDOMEvent, this), this.options.trackResize && o.DomEvent[i](t, "resize", this._onResize, this), o.Browser.any3d && this.options.transform3DLimit && this[i]("moveend", this._onMoveEnd) } },
-            _onResize: function() { o.Util.cancelAnimFrame(this._resizeRequest), this._resizeRequest = o.Util.requestAnimFrame(function() { this.invalidateSize({ debounceMoveend: !0 }) }, this) },
-            _onScroll: function() { this._container.scrollTop = 0, this._container.scrollLeft = 0 },
-            _onMoveEnd: function() { var t = this._getMapPanePos();
-                Math.max(Math.abs(t.x), Math.abs(t.y)) >= this.options.transform3DLimit && this._resetView(this.getCenter(), this.getZoom()) },
-            _findEventTargets: function(t, e) { for (var i, n = [], s = "mouseout" === e || "mouseover" === e, r = t.target || t.srcElement; r;) { if (i = this._targets[o.stamp(r)], i && i.listens(e, !0)) { if (s && !o.DomEvent._isExternalTarget(r, t)) break; if (n.push(i), s) break } if (r === this._container) break;
-                    r = r.parentNode } return n.length || s || !o.DomEvent._isExternalTarget(r, t) || (n = [this]), n },
-            _handleDOMEvent: function(t) { if (this._loaded && !o.DomEvent._skipped(t)) { var e = "keypress" === t.type && 13 === t.keyCode ? "click" : t.type; if ("click" === t.type) { var i = o.Util.extend({}, t);
-                        i.type = "preclick", this._handleDOMEvent(i) } "mousedown" === e && o.DomUtil.preventOutline(t.target || t.srcElement), this._fireDOMEvent(t, e) } },
-            _fireDOMEvent: function(t, e, i) { if (!t._stopped && (i = (i || []).concat(this._findEventTargets(t, e)), i.length)) { var n = i[0]; if ("contextmenu" === e && n.listens(e, !0) && o.DomEvent.preventDefault(t), "click" !== t.type && "preclick" !== t.type || t._simulated || !this._draggableMoved(n)) { var s = { originalEvent: t }; if ("keypress" !== t.type) { var r = n instanceof o.Marker;
-                            s.containerPoint = r ? this.latLngToContainerPoint(n.getLatLng()) : this.mouseEventToContainerPoint(t), s.layerPoint = this.containerPointToLayerPoint(s.containerPoint), s.latlng = r ? n.getLatLng() : this.layerPointToLatLng(s.layerPoint) } for (var a = 0; a < i.length; a++)
-                            if (i[a].fire(e, s, !0), s.originalEvent._stopped || i[a].options.nonBubblingEvents && -1 !== o.Util.indexOf(i[a].options.nonBubblingEvents, e)) return } } },
-            _draggableMoved: function(t) { return t = t.options.draggable ? t : this, t.dragging && t.dragging.moved() || this.boxZoom && this.boxZoom.moved() },
-            _clearHandlers: function() { for (var t = 0, e = this._handlers.length; e > t; t++) this._handlers[t].disable() },
-            whenReady: function(t, e) { return this._loaded ? t.call(e || this, { target: this }) : this.on("load", t, e), this },
-            _getMapPanePos: function() { return o.DomUtil.getPosition(this._mapPane) || new o.Point(0, 0) },
-            _moved: function() { var t = this._getMapPanePos(); return t && !t.equals([0, 0]) },
-            _getTopLeftPoint: function(t, e) { var n = t && e !== i ? this._getNewPixelOrigin(t, e) : this.getPixelOrigin(); return n.subtract(this._getMapPanePos()) },
-            _getNewPixelOrigin: function(t, e) { var i = this.getSize()._divideBy(2); return this.project(t, e)._subtract(i)._add(this._getMapPanePos())._round() },
-            _latLngToNewLayerPoint: function(t, e, i) {
-                var n = this._getNewPixelOrigin(i, e);
-                return this.project(t, e)._subtract(n)
-            },
-            _getCenterLayerPoint: function() { return this.containerPointToLayerPoint(this.getSize()._divideBy(2)) },
-            _getCenterOffset: function(t) { return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint()) },
-            _limitCenter: function(t, e, i) { if (!i) return t; var n = this.project(t, e),
-                    s = this.getSize().divideBy(2),
-                    r = new o.Bounds(n.subtract(s), n.add(s)),
-                    a = this._getBoundsOffset(r, i, e); return this.unproject(n.add(a), e) },
-            _limitOffset: function(t, e) { if (!e) return t; var i = this.getPixelBounds(),
-                    n = new o.Bounds(i.min.add(t), i.max.add(t)); return t.add(this._getBoundsOffset(n, e)) },
-            _getBoundsOffset: function(t, e, i) { var n = this.project(e.getNorthWest(), i).subtract(t.min),
-                    s = this.project(e.getSouthEast(), i).subtract(t.max),
-                    r = this._rebound(n.x, -s.x),
-                    a = this._rebound(n.y, -s.y); return new o.Point(r, a) },
-            _rebound: function(t, e) { return t + e > 0 ? Math.round(t - e) / 2 : Math.max(0, Math.ceil(t)) - Math.max(0, Math.floor(e)) },
-            _limitZoom: function(t) { var e = this.getMinZoom(),
-                    i = this.getMaxZoom(); return o.Browser.any3d || (t = Math.round(t)), Math.max(e, Math.min(i, t)) }
-        }), o.map = function(t, e) { return new o.Map(t, e) }, o.Layer = o.Evented.extend({ options: { pane: "overlayPane", nonBubblingEvents: [] }, addTo: function(t) { return t.addLayer(this), this }, remove: function() { return this.removeFrom(this._map || this._mapToAdd) }, removeFrom: function(t) { return t && t.removeLayer(this), this }, getPane: function(t) { return this._map.getPane(t ? this.options[t] || t : this.options.pane) }, addInteractiveTarget: function(t) { return this._map._targets[o.stamp(t)] = this, this }, removeInteractiveTarget: function(t) { return delete this._map._targets[o.stamp(t)], this }, _layerAdd: function(t) { var e = t.target;
-                e.hasLayer(this) && (this._map = e, this._zoomAnimated = e._zoomAnimated, this.getEvents && e.on(this.getEvents(), this), this.onAdd(e), this.getAttribution && this._map.attributionControl && this._map.attributionControl.addAttribution(this.getAttribution()), this.fire("add"), e.fire("layeradd", { layer: this })) } }), o.Map.include({ addLayer: function(t) { var e = o.stamp(t); return this._layers[e] ? t : (this._layers[e] = t, t._mapToAdd = this, t.beforeAdd && t.beforeAdd(this), this.whenReady(t._layerAdd, t), this) }, removeLayer: function(t) { var e = o.stamp(t); return this._layers[e] ? (this._loaded && t.onRemove(this), t.getAttribution && this.attributionControl && this.attributionControl.removeAttribution(t.getAttribution()), t.getEvents && this.off(t.getEvents(), t), delete this._layers[e], this._loaded && (this.fire("layerremove", { layer: t }), t.fire("remove")), t._map = t._mapToAdd = null, this) : this }, hasLayer: function(t) { return !!t && o.stamp(t) in this._layers }, eachLayer: function(t, e) { for (var i in this._layers) t.call(e, this._layers[i]); return this }, _addLayers: function(t) { t = t ? o.Util.isArray(t) ? t : [t] : []; for (var e = 0, i = t.length; i > e; e++) this.addLayer(t[e]) }, _addZoomLimit: function(t) {
-                (isNaN(t.options.maxZoom) || !isNaN(t.options.minZoom)) && (this._zoomBoundLayers[o.stamp(t)] = t, this._updateZoomLevels()) }, _removeZoomLimit: function(t) { var e = o.stamp(t);
-                this._zoomBoundLayers[e] && (delete this._zoomBoundLayers[e], this._updateZoomLevels()) }, _updateZoomLevels: function() { var t = 1 / 0,
-                    e = -(1 / 0),
-                    n = this._getZoomSpan(); for (var o in this._zoomBoundLayers) { var s = this._zoomBoundLayers[o].options;
-                    t = s.minZoom === i ? t : Math.min(t, s.minZoom), e = s.maxZoom === i ? e : Math.max(e, s.maxZoom) }
-                this._layersMaxZoom = e === -(1 / 0) ? i : e, this._layersMinZoom = t === 1 / 0 ? i : t, n !== this._getZoomSpan() && this.fire("zoomlevelschange") } }), o.Projection.Mercator = { R: 6378137, R_MINOR: 6356752.314245179, bounds: o.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), project: function(t) { var e = Math.PI / 180,
-                    i = this.R,
-                    n = t.lat * e,
-                    s = this.R_MINOR / i,
-                    r = Math.sqrt(1 - s * s),
-                    a = r * Math.sin(n),
-                    h = Math.tan(Math.PI / 4 - n / 2) / Math.pow((1 - a) / (1 + a), r / 2); return n = -i * Math.log(Math.max(h, 1e-10)), new o.Point(t.lng * e * i, n) }, unproject: function(t) { for (var e, i = 180 / Math.PI, n = this.R, s = this.R_MINOR / n, r = Math.sqrt(1 - s * s), a = Math.exp(-t.y / n), h = Math.PI / 2 - 2 * Math.atan(a), l = 0, u = .1; 15 > l && Math.abs(u) > 1e-7; l++) e = r * Math.sin(h), e = Math.pow((1 - e) / (1 + e), r / 2), u = Math.PI / 2 - 2 * Math.atan(a * e) - h, h += u; return new o.LatLng(h * i, t.x * i / n) } }, o.CRS.EPSG3395 = o.extend({}, o.CRS.Earth, { code: "EPSG:3395", projection: o.Projection.Mercator, transformation: function() { var t = .5 / (Math.PI * o.Projection.Mercator.R); return new o.Transformation(t, .5, -t, .5) }() }), o.GridLayer = o.Layer.extend({ options: { pane: "tilePane", tileSize: 256, opacity: 1, zIndex: 1, updateWhenIdle: o.Browser.mobile, updateInterval: 200, attribution: null, bounds: null, minZoom: 0 }, initialize: function(t) { t = o.setOptions(this, t) }, onAdd: function() { this._initContainer(), this._levels = {}, this._tiles = {}, this._resetView(), this._update() }, beforeAdd: function(t) { t._addZoomLimit(this) }, onRemove: function(t) { o.DomUtil.remove(this._container), t._removeZoomLimit(this), this._container = null, this._tileZoom = null }, bringToFront: function() { return this._map && (o.DomUtil.toFront(this._container), this._setAutoZIndex(Math.max)), this }, bringToBack: function() { return this._map && (o.DomUtil.toBack(this._container), this._setAutoZIndex(Math.min)), this }, getAttribution: function() { return this.options.attribution }, getContainer: function() { return this._container }, setOpacity: function(t) { return this.options.opacity = t, this._updateOpacity(), this }, setZIndex: function(t) { return this.options.zIndex = t, this._updateZIndex(), this }, isLoading: function() { return this._loading }, redraw: function() { return this._map && (this._removeAllTiles(), this._update()), this }, getEvents: function() { var t = { viewreset: this._resetAll, zoom: this._resetView, moveend: this._onMoveEnd }; return this.options.updateWhenIdle || (this._onMove || (this._onMove = o.Util.throttle(this._onMoveEnd, this.options.updateInterval, this)), t.move = this._onMove), this._zoomAnimated && (t.zoomanim = this._animateZoom), t }, createTile: function() { return e.createElement("div") }, getTileSize: function() { var t = this.options.tileSize; return t instanceof o.Point ? t : new o.Point(t, t) }, _updateZIndex: function() { this._container && this.options.zIndex !== i && null !== this.options.zIndex && (this._container.style.zIndex = this.options.zIndex) }, _setAutoZIndex: function(t) { for (var e, i = this.getPane().children, n = -t(-(1 / 0), 1 / 0), o = 0, s = i.length; s > o; o++) e = i[o].style.zIndex, i[o] !== this._container && e && (n = t(n, +e));
-                isFinite(n) && (this.options.zIndex = n + t(-1, 1), this._updateZIndex()) }, _updateOpacity: function() { if (this._map && !o.Browser.ielt9 && this._map._fadeAnimated) { o.DomUtil.setOpacity(this._container, this.options.opacity); var t = +new Date,
-                        e = !1,
-                        i = !1; for (var n in this._tiles) { var s = this._tiles[n]; if (s.current && s.loaded) { var r = Math.min(1, (t - s.loaded) / 200);
-                            o.DomUtil.setOpacity(s.el, r), 1 > r ? e = !0 : (s.active && (i = !0), s.active = !0) } }
-                    i && !this._noPrune && this._pruneTiles(), e && (o.Util.cancelAnimFrame(this._fadeFrame), this._fadeFrame = o.Util.requestAnimFrame(this._updateOpacity, this)) } }, _initContainer: function() { this._container || (this._container = o.DomUtil.create("div", "leaflet-layer"), this._updateZIndex(), this.options.opacity < 1 && this._updateOpacity(), this.getPane().appendChild(this._container)) }, _updateLevels: function() { var t = this._tileZoom,
-                    e = this.options.maxZoom; for (var i in this._levels) this._levels[i].el.children.length || i === t ? this._levels[i].el.style.zIndex = e - Math.abs(t - i) : (o.DomUtil.remove(this._levels[i].el), delete this._levels[i]); var n = this._levels[t],
-                    s = this._map; return n || (n = this._levels[t] = {}, n.el = o.DomUtil.create("div", "leaflet-tile-container leaflet-zoom-animated", this._container), n.el.style.zIndex = e, n.origin = s.project(s.unproject(s.getPixelOrigin()), t).round(), n.zoom = t, this._setZoomTransform(n, s.getCenter(), s.getZoom()), o.Util.falseFn(n.el.offsetWidth)), this._level = n, n }, _pruneTiles: function() { var t, e, i = this._map.getZoom(); if (i > this.options.maxZoom || i < this.options.minZoom) return this._removeAllTiles(); for (t in this._tiles) e = this._tiles[t], e.retain = e.current; for (t in this._tiles)
-                    if (e = this._tiles[t], e.current && !e.active) { var n = e.coords;
-                        this._retainParent(n.x, n.y, n.z, n.z - 5) || this._retainChildren(n.x, n.y, n.z, n.z + 2) }
-                for (t in this._tiles) this._tiles[t].retain || this._removeTile(t) }, _removeAllTiles: function() { for (var t in this._tiles) this._removeTile(t) }, _resetAll: function() { for (var t in this._levels) o.DomUtil.remove(this._levels[t].el), delete this._levels[t];
-                this._removeAllTiles(), this._tileZoom = null, this._resetView() }, _retainParent: function(t, e, i, n) { var o = Math.floor(t / 2),
-                    s = Math.floor(e / 2),
-                    r = i - 1,
-                    a = o + ":" + s + ":" + r,
-                    h = this._tiles[a]; return h && h.active ? (h.retain = !0, !0) : (h && h.loaded && (h.retain = !0), r > n ? this._retainParent(o, s, r, n) : !1) }, _retainChildren: function(t, e, i, n) { for (var o = 2 * t; 2 * t + 2 > o; o++)
-                    for (var s = 2 * e; 2 * e + 2 > s; s++) { var r = o + ":" + s + ":" + (i + 1),
-                            a = this._tiles[r];
-                        a && a.active ? a.retain = !0 : (a && a.loaded && (a.retain = !0), n > i + 1 && this._retainChildren(o, s, i + 1, n)) } }, _resetView: function(t) { var e = t && (t.pinch || t.flyTo);
-                this._setView(this._map.getCenter(), this._map.getZoom(), e, e) }, _animateZoom: function(t) { this._setView(t.center, t.zoom, !0, t.noUpdate) }, _setView: function(t, e, n, o) { var s = Math.round(e);
-                (this.options.maxZoom !== i && s > this.options.maxZoom || this.options.minZoom !== i && s < this.options.minZoom) && (s = i); var r = s !== this._tileZoom;
-                (!o || r) && (this._tileZoom = s, this._abortLoading && this._abortLoading(), this._updateLevels(), this._resetGrid(), s !== i && this._update(t), n || this._pruneTiles(), this._noPrune = !!n), this._setZoomTransforms(t, e) }, _setZoomTransforms: function(t, e) { for (var i in this._levels) this._setZoomTransform(this._levels[i], t, e) }, _setZoomTransform: function(t, e, i) { var n = this._map.getZoomScale(i, t.zoom),
-                    s = t.origin.multiplyBy(n).subtract(this._map._getNewPixelOrigin(e, i)).round();
-                o.Browser.any3d ? o.DomUtil.setTransform(t.el, s, n) : o.DomUtil.setPosition(t.el, s) }, _resetGrid: function() { var t = this._map,
-                    e = t.options.crs,
-                    i = this._tileSize = this.getTileSize(),
-                    n = this._tileZoom,
-                    o = this._map.getPixelWorldBounds(this._tileZoom);
-                o && (this._globalTileRange = this._pxBoundsToTileRange(o)), this._wrapX = e.wrapLng && !this.options.noWrap && [Math.floor(t.project([0, e.wrapLng[0]], n).x / i.x), Math.ceil(t.project([0, e.wrapLng[1]], n).x / i.y)], this._wrapY = e.wrapLat && !this.options.noWrap && [Math.floor(t.project([e.wrapLat[0], 0], n).y / i.x), Math.ceil(t.project([e.wrapLat[1], 0], n).y / i.y)] }, _onMoveEnd: function() { this._map && !this._map._animatingZoom && this._resetView() }, _getTiledPixelBounds: function(t, e, i) { var n = this._map,
-                    s = n.getZoomScale(e, i),
-                    r = n.project(t, i).floor(),
-                    a = n.getSize().divideBy(2 * s); return new o.Bounds(r.subtract(a), r.add(a)) }, _update: function(t) { var n = this._map; if (n) { var s = n.getZoom(); if (t === i && (t = n.getCenter()), this._tileZoom !== i) { var r = this._getTiledPixelBounds(t, s, this._tileZoom),
-                            a = this._pxBoundsToTileRange(r),
-                            h = a.getCenter(),
-                            l = []; for (var u in this._tiles) this._tiles[u].current = !1; if (Math.abs(s - this._tileZoom) > 1) return void this._setView(t, s); for (var c = a.min.y; c <= a.max.y; c++)
-                            for (var d = a.min.x; d <= a.max.x; d++) { var _ = new o.Point(d, c); if (_.z = this._tileZoom, this._isValidTile(_)) { var m = this._tiles[this._tileCoordsToKey(_)];
-                                    m ? m.current = !0 : l.push(_) } }
-                        if (l.sort(function(t, e) { return t.distanceTo(h) - e.distanceTo(h) }), 0 !== l.length) { this._loading || (this._loading = !0, this.fire("loading")); var p = e.createDocumentFragment(); for (d = 0; d < l.length; d++) this._addTile(l[d], p);
-                            this._level.el.appendChild(p) } } } }, _isValidTile: function(t) { var e = this._map.options.crs; if (!e.infinite) { var i = this._globalTileRange; if (!e.wrapLng && (t.x < i.min.x || t.x > i.max.x) || !e.wrapLat && (t.y < i.min.y || t.y > i.max.y)) return !1 } if (!this.options.bounds) return !0; var n = this._tileCoordsToBounds(t); return o.latLngBounds(this.options.bounds).overlaps(n) }, _keyToBounds: function(t) { return this._tileCoordsToBounds(this._keyToTileCoords(t)) }, _tileCoordsToBounds: function(t) { var e = this._map,
-                    i = this.getTileSize(),
-                    n = t.scaleBy(i),
-                    s = n.add(i),
-                    r = e.wrapLatLng(e.unproject(n, t.z)),
-                    a = e.wrapLatLng(e.unproject(s, t.z)); return new o.LatLngBounds(r, a) }, _tileCoordsToKey: function(t) { return t.x + ":" + t.y + ":" + t.z }, _keyToTileCoords: function(t) { var e = t.split(":"),
-                    i = new o.Point(+e[0], +e[1]); return i.z = +e[2], i }, _removeTile: function(t) { var e = this._tiles[t];
-                e && (o.DomUtil.remove(e.el), delete this._tiles[t], this.fire("tileunload", { tile: e.el, coords: this._keyToTileCoords(t) })) }, _initTile: function(t) { o.DomUtil.addClass(t, "leaflet-tile"); var e = this.getTileSize();
-                t.style.width = e.x + "px", t.style.height = e.y + "px", t.onselectstart = o.Util.falseFn, t.onmousemove = o.Util.falseFn, o.Browser.ielt9 && this.options.opacity < 1 && o.DomUtil.setOpacity(t, this.options.opacity), o.Browser.android && !o.Browser.android23 && (t.style.WebkitBackfaceVisibility = "hidden") }, _addTile: function(t, e) { var i = this._getTilePos(t),
-                    n = this._tileCoordsToKey(t),
-                    s = this.createTile(this._wrapCoords(t), o.bind(this._tileReady, this, t));
-                this._initTile(s), this.createTile.length < 2 && o.Util.requestAnimFrame(o.bind(this._tileReady, this, t, null, s)), o.DomUtil.setPosition(s, i), this._tiles[n] = { el: s, coords: t, current: !0 }, e.appendChild(s), this.fire("tileloadstart", { tile: s, coords: t }) }, _tileReady: function(t, e, i) { if (this._map) { e && this.fire("tileerror", { error: e, tile: i, coords: t }); var n = this._tileCoordsToKey(t);
-                    i = this._tiles[n], i && (i.loaded = +new Date, this._map._fadeAnimated ? (o.DomUtil.setOpacity(i.el, 0), o.Util.cancelAnimFrame(this._fadeFrame), this._fadeFrame = o.Util.requestAnimFrame(this._updateOpacity, this)) : (i.active = !0, this._pruneTiles()), o.DomUtil.addClass(i.el, "leaflet-tile-loaded"), this.fire("tileload", { tile: i.el, coords: t }), this._noTilesToLoad() && (this._loading = !1, this.fire("load"))) } }, _getTilePos: function(t) { return t.scaleBy(this.getTileSize()).subtract(this._level.origin) }, _wrapCoords: function(t) { var e = new o.Point(this._wrapX ? o.Util.wrapNum(t.x, this._wrapX) : t.x, this._wrapY ? o.Util.wrapNum(t.y, this._wrapY) : t.y); return e.z = t.z, e }, _pxBoundsToTileRange: function(t) { var e = this.getTileSize(); return new o.Bounds(t.min.unscaleBy(e).floor(), t.max.unscaleBy(e).ceil().subtract([1, 1])) }, _noTilesToLoad: function() { for (var t in this._tiles)
-                    if (!this._tiles[t].loaded) return !1;
-                return !0 } }), o.gridLayer = function(t) { return new o.GridLayer(t) }, o.TileLayer = o.GridLayer.extend({ options: { maxZoom: 18, subdomains: "abc", errorTileUrl: "", zoomOffset: 0, maxNativeZoom: null, tms: !1, zoomReverse: !1, detectRetina: !1, crossOrigin: !1 }, initialize: function(t, e) { this._url = t, e = o.setOptions(this, e), e.detectRetina && o.Browser.retina && e.maxZoom > 0 && (e.tileSize = Math.floor(e.tileSize / 2), e.zoomOffset++, e.minZoom = Math.max(0, e.minZoom), e.maxZoom--), "string" == typeof e.subdomains && (e.subdomains = e.subdomains.split("")), o.Browser.android || this.on("tileunload", this._onTileRemove) }, setUrl: function(t, e) { return this._url = t, e || this.redraw(), this }, createTile: function(t, i) { var n = e.createElement("img"); return o.DomEvent.on(n, "load", o.bind(this._tileOnLoad, this, i, n)), o.DomEvent.on(n, "error", o.bind(this._tileOnError, this, i, n)), this.options.crossOrigin && (n.crossOrigin = ""), n.alt = "", n.src = this.getTileUrl(t), n }, getTileUrl: function(t) { return o.Util.template(this._url, o.extend({ r: this.options.detectRetina && o.Browser.retina && this.options.maxZoom > 0 ? "@2x" : "", s: this._getSubdomain(t), x: t.x, y: this.options.tms ? this._globalTileRange.max.y - t.y : t.y, z: this._getZoomForUrl() }, this.options)) }, _tileOnLoad: function(t, e) { o.Browser.ielt9 ? setTimeout(o.bind(t, this, null, e), 0) : t(null, e) }, _tileOnError: function(t, e, i) { var n = this.options.errorTileUrl;
-                n && (e.src = n), t(i, e) }, getTileSize: function() { var t = this._map,
-                    e = o.GridLayer.prototype.getTileSize.call(this),
-                    i = this._tileZoom + this.options.zoomOffset,
-                    n = this.options.maxNativeZoom; return null !== n && i > n ? e.divideBy(t.getZoomScale(n, i)).round() : e }, _onTileRemove: function(t) { t.tile.onload = null }, _getZoomForUrl: function() { var t = this.options,
-                    e = this._tileZoom; return t.zoomReverse && (e = t.maxZoom - e), e += t.zoomOffset, null !== t.maxNativeZoom ? Math.min(e, t.maxNativeZoom) : e }, _getSubdomain: function(t) { var e = Math.abs(t.x + t.y) % this.options.subdomains.length; return this.options.subdomains[e] }, _abortLoading: function() { var t, e; for (t in this._tiles) this._tiles[t].coords.z !== this._tileZoom && (e = this._tiles[t].el, e.onload = o.Util.falseFn, e.onerror = o.Util.falseFn, e.complete || (e.src = o.Util.emptyImageUrl, o.DomUtil.remove(e))) } }), o.tileLayer = function(t, e) { return new o.TileLayer(t, e) }, o.TileLayer.WMS = o.TileLayer.extend({ defaultWmsParams: { service: "WMS", request: "GetMap", version: "1.1.1", layers: "", styles: "", format: "image/jpeg", transparent: !1 }, options: { crs: null, uppercase: !1 }, initialize: function(t, e) { this._url = t; var i = o.extend({}, this.defaultWmsParams); for (var n in e) n in this.options || (i[n] = e[n]);
-                e = o.setOptions(this, e), i.width = i.height = e.tileSize * (e.detectRetina && o.Browser.retina ? 2 : 1), this.wmsParams = i }, onAdd: function(t) { this._crs = this.options.crs || t.options.crs, this._wmsVersion = parseFloat(this.wmsParams.version); var e = this._wmsVersion >= 1.3 ? "crs" : "srs";
-                this.wmsParams[e] = this._crs.code, o.TileLayer.prototype.onAdd.call(this, t) }, getTileUrl: function(t) { var e = this._tileCoordsToBounds(t),
-                    i = this._crs.project(e.getNorthWest()),
-                    n = this._crs.project(e.getSouthEast()),
-                    s = (this._wmsVersion >= 1.3 && this._crs === o.CRS.EPSG4326 ? [n.y, i.x, i.y, n.x] : [i.x, n.y, n.x, i.y]).join(","),
-                    r = o.TileLayer.prototype.getTileUrl.call(this, t); return r + o.Util.getParamString(this.wmsParams, r, this.options.uppercase) + (this.options.uppercase ? "&BBOX=" : "&bbox=") + s }, setParams: function(t, e) { return o.extend(this.wmsParams, t), e || this.redraw(), this } }), o.tileLayer.wms = function(t, e) { return new o.TileLayer.WMS(t, e) }, o.ImageOverlay = o.Layer.extend({ options: { opacity: 1, alt: "", interactive: !1 }, initialize: function(t, e, i) { this._url = t, this._bounds = o.latLngBounds(e), o.setOptions(this, i) }, onAdd: function() { this._image || (this._initImage(), this.options.opacity < 1 && this._updateOpacity()), this.options.interactive && (o.DomUtil.addClass(this._image, "leaflet-interactive"), this.addInteractiveTarget(this._image)), this.getPane().appendChild(this._image), this._reset() }, onRemove: function() { o.DomUtil.remove(this._image), this.options.interactive && this.removeInteractiveTarget(this._image) }, setOpacity: function(t) { return this.options.opacity = t, this._image && this._updateOpacity(), this }, setStyle: function(t) { return t.opacity && this.setOpacity(t.opacity), this }, bringToFront: function() { return this._map && o.DomUtil.toFront(this._image), this }, bringToBack: function() { return this._map && o.DomUtil.toBack(this._image), this }, setUrl: function(t) { return this._url = t, this._image && (this._image.src = t), this }, setBounds: function(t) { return this._bounds = t, this._map && this._reset(), this }, getAttribution: function() { return this.options.attribution }, getEvents: function() { var t = { zoom: this._reset, viewreset: this._reset }; return this._zoomAnimated && (t.zoomanim = this._animateZoom), t }, getBounds: function() { return this._bounds }, getElement: function() { return this._image }, _initImage: function() { var t = this._image = o.DomUtil.create("img", "leaflet-image-layer " + (this._zoomAnimated ? "leaflet-zoom-animated" : ""));
-                t.onselectstart = o.Util.falseFn, t.onmousemove = o.Util.falseFn, t.onload = o.bind(this.fire, this, "load"), this.options.crossOrigin && (t.crossOrigin = ""), t.src = this._url, t.alt = this.options.alt }, _animateZoom: function(t) { var e = this._map.getZoomScale(t.zoom),
-                    i = this._map._latLngToNewLayerPoint(this._bounds.getNorthWest(), t.zoom, t.center);
-                o.DomUtil.setTransform(this._image, i, e) }, _reset: function() { var t = this._image,
-                    e = new o.Bounds(this._map.latLngToLayerPoint(this._bounds.getNorthWest()), this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
-                    i = e.getSize();
-                o.DomUtil.setPosition(t, e.min), t.style.width = i.x + "px", t.style.height = i.y + "px" }, _updateOpacity: function() { o.DomUtil.setOpacity(this._image, this.options.opacity) } }), o.imageOverlay = function(t, e, i) { return new o.ImageOverlay(t, e, i) }, o.Icon = o.Class.extend({ initialize: function(t) { o.setOptions(this, t) }, createIcon: function(t) { return this._createIcon("icon", t) }, createShadow: function(t) { return this._createIcon("shadow", t) }, _createIcon: function(t, e) { var i = this._getIconUrl(t); if (!i) { if ("icon" === t) throw new Error("iconUrl not set in Icon options (see the docs)."); return null } var n = this._createImg(i, e && "IMG" === e.tagName ? e : null); return this._setIconStyles(n, t), n }, _setIconStyles: function(t, e) { var i = this.options,
-                    n = o.point(i[e + "Size"]),
-                    s = o.point("shadow" === e && i.shadowAnchor || i.iconAnchor || n && n.divideBy(2, !0));
-                t.className = "leaflet-marker-" + e + " " + (i.className || ""), s && (t.style.marginLeft = -s.x + "px", t.style.marginTop = -s.y + "px"), n && (t.style.width = n.x + "px", t.style.height = n.y + "px") }, _createImg: function(t, i) { return i = i || e.createElement("img"), i.src = t, i }, _getIconUrl: function(t) { return o.Browser.retina && this.options[t + "RetinaUrl"] || this.options[t + "Url"] } }), o.icon = function(t) { return new o.Icon(t) }, o.Icon.Default = o.Icon.extend({ options: { iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41] }, _getIconUrl: function(t) { var e = t + "Url"; if (this.options[e]) return this.options[e]; var i = o.Icon.Default.imagePath; if (!i) throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually."); return i + "/marker-" + t + (o.Browser.retina && "icon" === t ? "-2x" : "") + ".png" } }), o.Icon.Default.imagePath = function() { var t, i, n, o, s = e.getElementsByTagName("script"),
-                r = /[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/; for (t = 0, i = s.length; i > t; t++)
-                if (n = s[t].src || "", n.match(r)) return o = n.split(r)[0], (o ? o + "/" : "") + "images" }(), o.Marker = o.Layer.extend({ options: { pane: "markerPane", nonBubblingEvents: ["click", "dblclick", "mouseover", "mouseout", "contextmenu"], icon: new o.Icon.Default, interactive: !0, keyboard: !0, zIndexOffset: 0, opacity: 1, riseOffset: 250 }, initialize: function(t, e) { o.setOptions(this, e), this._latlng = o.latLng(t) }, onAdd: function(t) { this._zoomAnimated = this._zoomAnimated && t.options.markerZoomAnimation, this._initIcon(), this.update() }, onRemove: function() { this.dragging && this.dragging.enabled() && (this.options.draggable = !0, this.dragging.removeHooks()), this._removeIcon(), this._removeShadow() }, getEvents: function() { var t = { zoom: this.update, viewreset: this.update }; return this._zoomAnimated && (t.zoomanim = this._animateZoom), t }, getLatLng: function() { return this._latlng }, setLatLng: function(t) { var e = this._latlng; return this._latlng = o.latLng(t), this.update(), this.fire("move", { oldLatLng: e, latlng: this._latlng }) }, setZIndexOffset: function(t) { return this.options.zIndexOffset = t, this.update() }, setIcon: function(t) { return this.options.icon = t, this._map && (this._initIcon(), this.update()), this._popup && this.bindPopup(this._popup, this._popup.options), this }, getElement: function() { return this._icon }, update: function() { if (this._icon) { var t = this._map.latLngToLayerPoint(this._latlng).round();
-                    this._setPos(t) } return this }, _initIcon: function() { var t = this.options,
-                    e = "leaflet-zoom-" + (this._zoomAnimated ? "animated" : "hide"),
-                    i = t.icon.createIcon(this._icon),
-                    n = !1;
-                i !== this._icon && (this._icon && this._removeIcon(), n = !0, t.title && (i.title = t.title), t.alt && (i.alt = t.alt)), o.DomUtil.addClass(i, e), t.keyboard && (i.tabIndex = "0"), this._icon = i, t.riseOnHover && this.on({ mouseover: this._bringToFront, mouseout: this._resetZIndex }); var s = t.icon.createShadow(this._shadow),
-                    r = !1;
-                s !== this._shadow && (this._removeShadow(), r = !0), s && o.DomUtil.addClass(s, e), this._shadow = s, t.opacity < 1 && this._updateOpacity(), n && (this.getPane().appendChild(this._icon), this._initInteraction()), s && r && this.getPane("shadowPane").appendChild(this._shadow) }, _removeIcon: function() { this.options.riseOnHover && this.off({ mouseover: this._bringToFront, mouseout: this._resetZIndex }), o.DomUtil.remove(this._icon), this.removeInteractiveTarget(this._icon), this._icon = null }, _removeShadow: function() { this._shadow && o.DomUtil.remove(this._shadow), this._shadow = null }, _setPos: function(t) { o.DomUtil.setPosition(this._icon, t), this._shadow && o.DomUtil.setPosition(this._shadow, t), this._zIndex = t.y + this.options.zIndexOffset, this._resetZIndex() }, _updateZIndex: function(t) { this._icon.style.zIndex = this._zIndex + t }, _animateZoom: function(t) { var e = this._map._latLngToNewLayerPoint(this._latlng, t.zoom, t.center).round();
-                this._setPos(e) }, _initInteraction: function() { if (this.options.interactive && (o.DomUtil.addClass(this._icon, "leaflet-interactive"), this.addInteractiveTarget(this._icon), o.Handler.MarkerDrag)) { var t = this.options.draggable;
-                    this.dragging && (t = this.dragging.enabled(), this.dragging.disable()), this.dragging = new o.Handler.MarkerDrag(this), t && this.dragging.enable() } }, setOpacity: function(t) { return this.options.opacity = t, this._map && this._updateOpacity(), this }, _updateOpacity: function() { var t = this.options.opacity;
-                o.DomUtil.setOpacity(this._icon, t), this._shadow && o.DomUtil.setOpacity(this._shadow, t) }, _bringToFront: function() { this._updateZIndex(this.options.riseOffset) }, _resetZIndex: function() { this._updateZIndex(0) } }), o.marker = function(t, e) { return new o.Marker(t, e) }, o.DivIcon = o.Icon.extend({ options: { iconSize: [12, 12], className: "leaflet-div-icon", html: !1 }, createIcon: function(t) { var i = t && "DIV" === t.tagName ? t : e.createElement("div"),
-                    n = this.options; return i.innerHTML = n.html !== !1 ? n.html : "", n.bgPos && (i.style.backgroundPosition = -n.bgPos.x + "px " + -n.bgPos.y + "px"), this._setIconStyles(i, "icon"), i }, createShadow: function() { return null } }), o.divIcon = function(t) { return new o.DivIcon(t) }, o.Map.mergeOptions({ closePopupOnClick: !0 }), o.Popup = o.Layer.extend({ options: { pane: "popupPane", minWidth: 50, maxWidth: 300, offset: [0, 7], autoPan: !0, autoPanPadding: [5, 5], closeButton: !0, autoClose: !0, zoomAnimation: !0 }, initialize: function(t, e) { o.setOptions(this, t), this._source = e }, onAdd: function(t) { this._zoomAnimated = this._zoomAnimated && this.options.zoomAnimation, this._container || this._initLayout(), t._fadeAnimated && o.DomUtil.setOpacity(this._container, 0), clearTimeout(this._removeTimeout), this.getPane().appendChild(this._container), this.update(), t._fadeAnimated && o.DomUtil.setOpacity(this._container, 1), t.fire("popupopen", { popup: this }), this._source && this._source.fire("popupopen", { popup: this }, !0) }, openOn: function(t) { return t.openPopup(this), this }, onRemove: function(t) { t._fadeAnimated ? (o.DomUtil.setOpacity(this._container, 0), this._removeTimeout = setTimeout(o.bind(o.DomUtil.remove, o.DomUtil, this._container), 200)) : o.DomUtil.remove(this._container), t.fire("popupclose", { popup: this }), this._source && this._source.fire("popupclose", { popup: this }, !0) }, getLatLng: function() { return this._latlng }, setLatLng: function(t) { return this._latlng = o.latLng(t), this._map && (this._updatePosition(), this._adjustPan()), this }, getContent: function() { return this._content }, setContent: function(t) { return this._content = t, this.update(), this }, getElement: function() { return this._container }, update: function() { this._map && (this._container.style.visibility = "hidden", this._updateContent(), this._updateLayout(), this._updatePosition(), this._container.style.visibility = "", this._adjustPan()) }, getEvents: function() { var t = { zoom: this._updatePosition, viewreset: this._updatePosition }; return this._zoomAnimated && (t.zoomanim = this._animateZoom), ("closeOnClick" in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) && (t.preclick = this._close), this.options.keepInView && (t.moveend = this._adjustPan), t }, isOpen: function() { return !!this._map && this._map.hasLayer(this) }, bringToFront: function() { return this._map && o.DomUtil.toFront(this._container), this }, bringToBack: function() { return this._map && o.DomUtil.toBack(this._container), this }, _close: function() { this._map && this._map.closePopup(this) }, _initLayout: function() { var t = "leaflet-popup",
-                    e = this._container = o.DomUtil.create("div", t + " " + (this.options.className || "") + " leaflet-zoom-" + (this._zoomAnimated ? "animated" : "hide")); if (this.options.closeButton) { var i = this._closeButton = o.DomUtil.create("a", t + "-close-button", e);
-                    i.href = "#close", i.innerHTML = "&#215;", o.DomEvent.on(i, "click", this._onCloseButtonClick, this) } var n = this._wrapper = o.DomUtil.create("div", t + "-content-wrapper", e);
-                this._contentNode = o.DomUtil.create("div", t + "-content", n), o.DomEvent.disableClickPropagation(n).disableScrollPropagation(this._contentNode).on(n, "contextmenu", o.DomEvent.stopPropagation), this._tipContainer = o.DomUtil.create("div", t + "-tip-container", e), this._tip = o.DomUtil.create("div", t + "-tip", this._tipContainer) }, _updateContent: function() { if (this._content) { var t = this._contentNode,
-                        e = "function" == typeof this._content ? this._content(this._source || this) : this._content; if ("string" == typeof e) t.innerHTML = e;
-                    else { for (; t.hasChildNodes();) t.removeChild(t.firstChild);
-                        t.appendChild(e) }
-                    this.fire("contentupdate") } }, _updateLayout: function() { var t = this._contentNode,
-                    e = t.style;
-                e.width = "", e.whiteSpace = "nowrap"; var i = t.offsetWidth;
-                i = Math.min(i, this.options.maxWidth), i = Math.max(i, this.options.minWidth), e.width = i + 1 + "px", e.whiteSpace = "", e.height = ""; var n = t.offsetHeight,
-                    s = this.options.maxHeight,
-                    r = "leaflet-popup-scrolled";
-                s && n > s ? (e.height = s + "px", o.DomUtil.addClass(t, r)) : o.DomUtil.removeClass(t, r), this._containerWidth = this._container.offsetWidth }, _updatePosition: function() { if (this._map) { var t = this._map.latLngToLayerPoint(this._latlng),
-                        e = o.point(this.options.offset);
-                    this._zoomAnimated ? o.DomUtil.setPosition(this._container, t) : e = e.add(t); var i = this._containerBottom = -e.y,
-                        n = this._containerLeft = -Math.round(this._containerWidth / 2) + e.x;
-                    this._container.style.bottom = i + "px", this._container.style.left = n + "px" } }, _animateZoom: function(t) { var e = this._map._latLngToNewLayerPoint(this._latlng, t.zoom, t.center);
-                o.DomUtil.setPosition(this._container, e) }, _adjustPan: function() { if (!(!this.options.autoPan || this._map._panAnim && this._map._panAnim._inProgress)) { var t = this._map,
-                        e = this._container.offsetHeight,
-                        i = this._containerWidth,
-                        n = new o.Point(this._containerLeft, -e - this._containerBottom);
-                    this._zoomAnimated && n._add(o.DomUtil.getPosition(this._container)); var s = t.layerPointToContainerPoint(n),
-                        r = o.point(this.options.autoPanPadding),
-                        a = o.point(this.options.autoPanPaddingTopLeft || r),
-                        h = o.point(this.options.autoPanPaddingBottomRight || r),
-                        l = t.getSize(),
-                        u = 0,
-                        c = 0;
-                    s.x + i + h.x > l.x && (u = s.x + i - l.x + h.x), s.x - u - a.x < 0 && (u = s.x - a.x), s.y + e + h.y > l.y && (c = s.y + e - l.y + h.y), s.y - c - a.y < 0 && (c = s.y - a.y), (u || c) && t.fire("autopanstart").panBy([u, c]) } }, _onCloseButtonClick: function(t) { this._close(), o.DomEvent.stop(t) } }), o.popup = function(t, e) { return new o.Popup(t, e) }, o.Map.include({ openPopup: function(t, e, i) { return t instanceof o.Popup || (t = new o.Popup(i).setContent(t)), e && t.setLatLng(e), this.hasLayer(t) ? this : (this._popup && this._popup.options.autoClose && this.closePopup(), this._popup = t, this.addLayer(t)) }, closePopup: function(t) { return t && t !== this._popup || (t = this._popup, this._popup = null), t && this.removeLayer(t), this } }), o.Layer.include({ bindPopup: function(t, e) { return t instanceof o.Popup ? (o.setOptions(t, e), this._popup = t, t._source = this) : ((!this._popup || e) && (this._popup = new o.Popup(e, this)), this._popup.setContent(t)), this._popupHandlersAdded || (this.on({ click: this._openPopup, remove: this.closePopup, move: this._movePopup }), this._popupHandlersAdded = !0), this._originalPopupOffset = this._popup.options.offset, this }, unbindPopup: function() { return this._popup && (this.off({ click: this._openPopup, remove: this.closePopup, move: this._movePopup }), this._popupHandlersAdded = !1, this._popup = null), this }, openPopup: function(t, e) { if (t instanceof o.Layer || (e = t, t = this), t instanceof o.FeatureGroup)
-                    for (var i in this._layers) { t = this._layers[i]; break }
-                return e || (e = t.getCenter ? t.getCenter() : t.getLatLng()), this._popup && this._map && (this._popup.options.offset = this._popupAnchor(t), this._popup._source = t, this._popup.update(), this._map.openPopup(this._popup, e)), this }, closePopup: function() { return this._popup && this._popup._close(), this }, togglePopup: function(t) { return this._popup && (this._popup._map ? this.closePopup() : this.openPopup(t)), this }, isPopupOpen: function() { return this._popup.isOpen() }, setPopupContent: function(t) { return this._popup && this._popup.setContent(t), this }, getPopup: function() { return this._popup }, _openPopup: function(t) { var e = t.layer || t.target; if (this._popup && this._map) return e instanceof o.Path ? void this.openPopup(t.layer || t.target, t.latlng) : void(this._map.hasLayer(this._popup) && this._popup._source === e ? this.closePopup() : this.openPopup(e, t.latlng)) }, _popupAnchor: function(t) { var e = t._getPopupAnchor ? t._getPopupAnchor() : [0, 0],
-                    i = this._originalPopupOffset || o.Popup.prototype.options.offset; return o.point(e).add(i) }, _movePopup: function(t) { this._popup.setLatLng(t.latlng) } }), o.Marker.include({ _getPopupAnchor: function() { return this.options.icon.options.popupAnchor || [0, 0] } }), o.LayerGroup = o.Layer.extend({
-            initialize: function(t) { this._layers = {}; var e, i; if (t)
-                    for (e = 0, i = t.length; i > e; e++) this.addLayer(t[e]) },
-            addLayer: function(t) { var e = this.getLayerId(t); return this._layers[e] = t, this._map && this._map.addLayer(t), this },
-            removeLayer: function(t) {
-                var e = t in this._layers ? t : this.getLayerId(t);
-                return this._map && this._layers[e] && this._map.removeLayer(this._layers[e]),
-                    delete this._layers[e], this
-            },
-            hasLayer: function(t) { return !!t && (t in this._layers || this.getLayerId(t) in this._layers) },
-            clearLayers: function() { for (var t in this._layers) this.removeLayer(this._layers[t]); return this },
-            invoke: function(t) { var e, i, n = Array.prototype.slice.call(arguments, 1); for (e in this._layers) i = this._layers[e], i[t] && i[t].apply(i, n); return this },
-            onAdd: function(t) { for (var e in this._layers) t.addLayer(this._layers[e]) },
-            onRemove: function(t) { for (var e in this._layers) t.removeLayer(this._layers[e]) },
-            eachLayer: function(t, e) { for (var i in this._layers) t.call(e, this._layers[i]); return this },
-            getLayer: function(t) { return this._layers[t] },
-            getLayers: function() { var t = []; for (var e in this._layers) t.push(this._layers[e]); return t },
-            setZIndex: function(t) { return this.invoke("setZIndex", t) },
-            getLayerId: function(t) { return o.stamp(t) }
-        }), o.layerGroup = function(t) { return new o.LayerGroup(t) }, o.FeatureGroup = o.LayerGroup.extend({ addLayer: function(t) { return this.hasLayer(t) ? this : (t.addEventParent(this), o.LayerGroup.prototype.addLayer.call(this, t), this.fire("layeradd", { layer: t })) }, removeLayer: function(t) { return this.hasLayer(t) ? (t in this._layers && (t = this._layers[t]), t.removeEventParent(this), o.LayerGroup.prototype.removeLayer.call(this, t), this.fire("layerremove", { layer: t })) : this }, setStyle: function(t) { return this.invoke("setStyle", t) }, bringToFront: function() { return this.invoke("bringToFront") }, bringToBack: function() { return this.invoke("bringToBack") }, getBounds: function() { var t = new o.LatLngBounds; for (var e in this._layers) { var i = this._layers[e];
-                    t.extend(i.getBounds ? i.getBounds() : i.getLatLng()) } return t } }), o.featureGroup = function(t) { return new o.FeatureGroup(t) }, o.Renderer = o.Layer.extend({ options: { padding: .1 }, initialize: function(t) { o.setOptions(this, t), o.stamp(this) }, onAdd: function() { this._container || (this._initContainer(), this._zoomAnimated && o.DomUtil.addClass(this._container, "leaflet-zoom-animated")), this.getPane().appendChild(this._container), this._update() }, onRemove: function() { o.DomUtil.remove(this._container) }, getEvents: function() { var t = { viewreset: this._reset, zoomstart: this._onZoomStart, zoom: this._onZoom, moveend: this._update }; return this._zoomAnimated && (t.zoomanim = this._onAnimZoom), t }, _onAnimZoom: function(t) { this._updateTransform(t.center, t.zoom) }, _onZoom: function() { this._updateTransform(this._map.getCenter(), this._map.getZoom()) }, _onZoomStart: function() { this._update() }, _updateTransform: function(t, e) { var i = this._map.getZoomScale(e, this._zoom),
-                    n = o.DomUtil.getPosition(this._container),
-                    s = this._map.getSize().multiplyBy(.5 + this.options.padding),
-                    r = this._map.project(this._center, e),
-                    a = this._map.project(t, e),
-                    h = a.subtract(r),
-                    l = s.multiplyBy(-i).add(n).add(s).subtract(h);
-                o.DomUtil.setTransform(this._container, l, i) }, _reset: function() { this._update(), this._updateTransform(this._center, this._zoom) }, _update: function() { var t = this.options.padding,
-                    e = this._map.getSize(),
-                    i = this._map.containerPointToLayerPoint(e.multiplyBy(-t)).round();
-                this._bounds = new o.Bounds(i, i.add(e.multiplyBy(1 + 2 * t)).round()), this._center = this._map.getCenter(), this._zoom = this._map.getZoom() } }), o.Map.include({ getRenderer: function(t) { var e = t.options.renderer || this._getPaneRenderer(t.options.pane) || this.options.renderer || this._renderer; return e || (e = this._renderer = this.options.preferCanvas && o.canvas() || o.svg()), this.hasLayer(e) || this.addLayer(e), e }, _getPaneRenderer: function(t) { if ("overlayPane" === t || t === i) return !1; var e = this._paneRenderers[t]; return e === i && (e = o.SVG && o.svg({ pane: t }) || o.Canvas && o.canvas({ pane: t }), this._paneRenderers[t] = e), e } }), o.Path = o.Layer.extend({ options: { stroke: !0, color: "#3388ff", weight: 3, opacity: 1, lineCap: "round", lineJoin: "round", fillOpacity: .2, fillRule: "evenodd", interactive: !0 }, beforeAdd: function(t) { this._renderer = t.getRenderer(this) }, onAdd: function() { this._renderer._initPath(this), this._reset(), this._renderer._addPath(this) }, onRemove: function() { this._renderer._removePath(this) }, getEvents: function() { return { zoomend: this._project, moveend: this._update, viewreset: this._reset } }, redraw: function() { return this._map && this._renderer._updatePath(this), this }, setStyle: function(t) { return o.setOptions(this, t), this._renderer && this._renderer._updateStyle(this), this }, bringToFront: function() { return this._renderer && this._renderer._bringToFront(this), this }, bringToBack: function() { return this._renderer && this._renderer._bringToBack(this), this }, getElement: function() { return this._path }, _reset: function() { this._project(), this._update() }, _clickTolerance: function() { return (this.options.stroke ? this.options.weight / 2 : 0) + (o.Browser.touch ? 10 : 0) } }), o.LineUtil = { simplify: function(t, e) { if (!e || !t.length) return t.slice(); var i = e * e; return t = this._reducePoints(t, i), t = this._simplifyDP(t, i) }, pointToSegmentDistance: function(t, e, i) { return Math.sqrt(this._sqClosestPointOnSegment(t, e, i, !0)) }, closestPointOnSegment: function(t, e, i) { return this._sqClosestPointOnSegment(t, e, i) }, _simplifyDP: function(t, e) { var n = t.length,
-                    o = typeof Uint8Array != i + "" ? Uint8Array : Array,
-                    s = new o(n);
-                s[0] = s[n - 1] = 1, this._simplifyDPStep(t, s, e, 0, n - 1); var r, a = []; for (r = 0; n > r; r++) s[r] && a.push(t[r]); return a }, _simplifyDPStep: function(t, e, i, n, o) { var s, r, a, h = 0; for (r = n + 1; o - 1 >= r; r++) a = this._sqClosestPointOnSegment(t[r], t[n], t[o], !0), a > h && (s = r, h = a);
-                h > i && (e[s] = 1, this._simplifyDPStep(t, e, i, n, s), this._simplifyDPStep(t, e, i, s, o)) }, _reducePoints: function(t, e) { for (var i = [t[0]], n = 1, o = 0, s = t.length; s > n; n++) this._sqDist(t[n], t[o]) > e && (i.push(t[n]), o = n); return s - 1 > o && i.push(t[s - 1]), i }, clipSegment: function(t, e, i, n, o) { var s, r, a, h = n ? this._lastCode : this._getBitCode(t, i),
-                    l = this._getBitCode(e, i); for (this._lastCode = l;;) { if (!(h | l)) return [t, e]; if (h & l) return !1;
-                    s = h || l, r = this._getEdgeIntersection(t, e, s, i, o), a = this._getBitCode(r, i), s === h ? (t = r, h = a) : (e = r, l = a) } }, _getEdgeIntersection: function(t, e, i, n, s) { var r, a, h = e.x - t.x,
-                    l = e.y - t.y,
-                    u = n.min,
-                    c = n.max; return 8 & i ? (r = t.x + h * (c.y - t.y) / l, a = c.y) : 4 & i ? (r = t.x + h * (u.y - t.y) / l, a = u.y) : 2 & i ? (r = c.x, a = t.y + l * (c.x - t.x) / h) : 1 & i && (r = u.x, a = t.y + l * (u.x - t.x) / h), new o.Point(r, a, s) }, _getBitCode: function(t, e) { var i = 0; return t.x < e.min.x ? i |= 1 : t.x > e.max.x && (i |= 2), t.y < e.min.y ? i |= 4 : t.y > e.max.y && (i |= 8), i }, _sqDist: function(t, e) { var i = e.x - t.x,
-                    n = e.y - t.y; return i * i + n * n }, _sqClosestPointOnSegment: function(t, e, i, n) { var s, r = e.x,
-                    a = e.y,
-                    h = i.x - r,
-                    l = i.y - a,
-                    u = h * h + l * l; return u > 0 && (s = ((t.x - r) * h + (t.y - a) * l) / u, s > 1 ? (r = i.x, a = i.y) : s > 0 && (r += h * s, a += l * s)), h = t.x - r, l = t.y - a, n ? h * h + l * l : new o.Point(r, a) } }, o.Polyline = o.Path.extend({ options: { smoothFactor: 1 }, initialize: function(t, e) { o.setOptions(this, e), this._setLatLngs(t) }, getLatLngs: function() { return this._latlngs }, setLatLngs: function(t) { return this._setLatLngs(t), this.redraw() }, isEmpty: function() { return !this._latlngs.length }, closestLayerPoint: function(t) { for (var e, i, n = 1 / 0, s = null, r = o.LineUtil._sqClosestPointOnSegment, a = 0, h = this._parts.length; h > a; a++)
-                    for (var l = this._parts[a], u = 1, c = l.length; c > u; u++) { e = l[u - 1], i = l[u]; var d = r(t, e, i, !0);
-                        n > d && (n = d, s = r(t, e, i)) }
-                return s && (s.distance = Math.sqrt(n)), s }, getCenter: function() { var t, e, i, n, o, s, r, a = this._rings[0],
-                    h = a.length; if (!h) return null; for (t = 0, e = 0; h - 1 > t; t++) e += a[t].distanceTo(a[t + 1]) / 2; if (0 === e) return this._map.layerPointToLatLng(a[0]); for (t = 0, n = 0; h - 1 > t; t++)
-                    if (o = a[t], s = a[t + 1], i = o.distanceTo(s), n += i, n > e) return r = (n - e) / i, this._map.layerPointToLatLng([s.x - r * (s.x - o.x), s.y - r * (s.y - o.y)]) }, getBounds: function() { return this._bounds }, addLatLng: function(t, e) { return e = e || this._defaultShape(), t = o.latLng(t), e.push(t), this._bounds.extend(t), this.redraw() }, _setLatLngs: function(t) { this._bounds = new o.LatLngBounds, this._latlngs = this._convertLatLngs(t) }, _defaultShape: function() { return o.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0] }, _convertLatLngs: function(t) { for (var e = [], i = o.Polyline._flat(t), n = 0, s = t.length; s > n; n++) i ? (e[n] = o.latLng(t[n]), this._bounds.extend(e[n])) : e[n] = this._convertLatLngs(t[n]); return e }, _project: function() { this._rings = [], this._projectLatlngs(this._latlngs, this._rings); var t = this._clickTolerance(),
-                    e = new o.Point(t, -t);
-                this._bounds.isValid() && (this._pxBounds = new o.Bounds(this._map.latLngToLayerPoint(this._bounds.getSouthWest())._subtract(e), this._map.latLngToLayerPoint(this._bounds.getNorthEast())._add(e))) }, _projectLatlngs: function(t, e) { var i, n, s = t[0] instanceof o.LatLng,
-                    r = t.length; if (s) { for (n = [], i = 0; r > i; i++) n[i] = this._map.latLngToLayerPoint(t[i]);
-                    e.push(n) } else
-                    for (i = 0; r > i; i++) this._projectLatlngs(t[i], e) }, _clipPoints: function() { var t = this._renderer._bounds; if (this._parts = [], this._pxBounds && this._pxBounds.intersects(t)) { if (this.options.noClip) return void(this._parts = this._rings); var e, i, n, s, r, a, h, l = this._parts; for (e = 0, n = 0, s = this._rings.length; s > e; e++)
-                        for (h = this._rings[e], i = 0, r = h.length; r - 1 > i; i++) a = o.LineUtil.clipSegment(h[i], h[i + 1], t, i, !0), a && (l[n] = l[n] || [], l[n].push(a[0]), (a[1] !== h[i + 1] || i === r - 2) && (l[n].push(a[1]), n++)) } }, _simplifyPoints: function() { for (var t = this._parts, e = this.options.smoothFactor, i = 0, n = t.length; n > i; i++) t[i] = o.LineUtil.simplify(t[i], e) }, _update: function() { this._map && (this._clipPoints(), this._simplifyPoints(), this._updatePath()) }, _updatePath: function() { this._renderer._updatePoly(this) } }), o.polyline = function(t, e) { return new o.Polyline(t, e) }, o.Polyline._flat = function(t) { return !o.Util.isArray(t[0]) || "object" != typeof t[0][0] && "undefined" != typeof t[0][0] }, o.PolyUtil = {}, o.PolyUtil.clipPolygon = function(t, e, i) { var n, s, r, a, h, l, u, c, d, _ = [1, 4, 2, 8],
-                m = o.LineUtil; for (s = 0, u = t.length; u > s; s++) t[s]._code = m._getBitCode(t[s], e); for (a = 0; 4 > a; a++) { for (c = _[a], n = [], s = 0, u = t.length, r = u - 1; u > s; r = s++) h = t[s], l = t[r], h._code & c ? l._code & c || (d = m._getEdgeIntersection(l, h, c, e, i), d._code = m._getBitCode(d, e), n.push(d)) : (l._code & c && (d = m._getEdgeIntersection(l, h, c, e, i), d._code = m._getBitCode(d, e), n.push(d)), n.push(h));
-                t = n } return t }, o.Polygon = o.Polyline.extend({ options: { fill: !0 }, isEmpty: function() { return !this._latlngs.length || !this._latlngs[0].length }, getCenter: function() { var t, e, i, n, o, s, r, a, h, l = this._rings[0],
-                    u = l.length; if (!u) return null; for (s = r = a = 0, t = 0, e = u - 1; u > t; e = t++) i = l[t], n = l[e], o = i.y * n.x - n.y * i.x, r += (i.x + n.x) * o, a += (i.y + n.y) * o, s += 3 * o; return h = 0 === s ? l[0] : [r / s, a / s], this._map.layerPointToLatLng(h) }, _convertLatLngs: function(t) { var e = o.Polyline.prototype._convertLatLngs.call(this, t),
-                    i = e.length; return i >= 2 && e[0] instanceof o.LatLng && e[0].equals(e[i - 1]) && e.pop(), e }, _setLatLngs: function(t) { o.Polyline.prototype._setLatLngs.call(this, t), o.Polyline._flat(this._latlngs) && (this._latlngs = [this._latlngs]) }, _defaultShape: function() { return o.Polyline._flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0] }, _clipPoints: function() { var t = this._renderer._bounds,
-                    e = this.options.weight,
-                    i = new o.Point(e, e); if (t = new o.Bounds(t.min.subtract(i), t.max.add(i)), this._parts = [], this._pxBounds && this._pxBounds.intersects(t)) { if (this.options.noClip) return void(this._parts = this._rings); for (var n, s = 0, r = this._rings.length; r > s; s++) n = o.PolyUtil.clipPolygon(this._rings[s], t, !0), n.length && this._parts.push(n) } }, _updatePath: function() { this._renderer._updatePoly(this, !0) } }), o.polygon = function(t, e) { return new o.Polygon(t, e) }, o.Rectangle = o.Polygon.extend({ initialize: function(t, e) { o.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(t), e) }, setBounds: function(t) { return this.setLatLngs(this._boundsToLatLngs(t)) }, _boundsToLatLngs: function(t) { return t = o.latLngBounds(t), [t.getSouthWest(), t.getNorthWest(), t.getNorthEast(), t.getSouthEast()] } }), o.rectangle = function(t, e) { return new o.Rectangle(t, e) }, o.CircleMarker = o.Path.extend({ options: { fill: !0, radius: 10 }, initialize: function(t, e) { o.setOptions(this, e), this._latlng = o.latLng(t), this._radius = this.options.radius }, setLatLng: function(t) { return this._latlng = o.latLng(t), this.redraw(), this.fire("move", { latlng: this._latlng }) }, getLatLng: function() { return this._latlng }, setRadius: function(t) { return this.options.radius = this._radius = t, this.redraw() }, getRadius: function() { return this._radius }, setStyle: function(t) { var e = t && t.radius || this._radius; return o.Path.prototype.setStyle.call(this, t), this.setRadius(e), this }, _project: function() { this._point = this._map.latLngToLayerPoint(this._latlng), this._updateBounds() }, _updateBounds: function() { var t = this._radius,
-                    e = this._radiusY || t,
-                    i = this._clickTolerance(),
-                    n = [t + i, e + i];
-                this._pxBounds = new o.Bounds(this._point.subtract(n), this._point.add(n)) }, _update: function() { this._map && this._updatePath() }, _updatePath: function() { this._renderer._updateCircle(this) }, _empty: function() { return this._radius && !this._renderer._bounds.intersects(this._pxBounds) } }), o.circleMarker = function(t, e) { return new o.CircleMarker(t, e) }, o.Circle = o.CircleMarker.extend({ initialize: function(t, e) { o.setOptions(this, e), this._latlng = o.latLng(t), this._mRadius = this.options.radius }, setRadius: function(t) { return this._mRadius = t, this.redraw() }, getRadius: function() { return this._mRadius }, getBounds: function() { var t = [this._radius, this._radiusY || this._radius]; return new o.LatLngBounds(this._map.layerPointToLatLng(this._point.subtract(t)), this._map.layerPointToLatLng(this._point.add(t))) }, setStyle: o.Path.prototype.setStyle, _project: function() { var t = this._latlng.lng,
-                    e = this._latlng.lat,
-                    i = this._map,
-                    n = i.options.crs; if (n.distance === o.CRS.Earth.distance) { var s = Math.PI / 180,
-                        r = this._mRadius / o.CRS.Earth.R / s,
-                        a = i.project([e + r, t]),
-                        h = i.project([e - r, t]),
-                        l = a.add(h).divideBy(2),
-                        u = i.unproject(l).lat,
-                        c = Math.acos((Math.cos(r * s) - Math.sin(e * s) * Math.sin(u * s)) / (Math.cos(e * s) * Math.cos(u * s))) / s;
-                    this._point = l.subtract(i.getPixelOrigin()), this._radius = isNaN(c) ? 0 : Math.max(Math.round(l.x - i.project([u, t - c]).x), 1), this._radiusY = Math.max(Math.round(l.y - a.y), 1) } else { var d = n.unproject(n.project(this._latlng).subtract([this._mRadius, 0]));
-                    this._point = i.latLngToLayerPoint(this._latlng), this._radius = this._point.x - i.latLngToLayerPoint(d).x }
-                this._updateBounds() } }), o.circle = function(t, e, i) { return "number" == typeof e && (e = o.extend({}, i, { radius: e })), new o.Circle(t, e) }, o.SVG = o.Renderer.extend({ _initContainer: function() { this._container = o.SVG.create("svg"), this._container.setAttribute("pointer-events", "none"), this._rootGroup = o.SVG.create("g"), this._container.appendChild(this._rootGroup) }, _update: function() { if (!this._map._animatingZoom || !this._bounds) { o.Renderer.prototype._update.call(this); var t = this._bounds,
-                        e = t.getSize(),
-                        i = this._container;
-                    this._svgSize && this._svgSize.equals(e) || (this._svgSize = e, i.setAttribute("width", e.x), i.setAttribute("height", e.y)), o.DomUtil.setPosition(i, t.min), i.setAttribute("viewBox", [t.min.x, t.min.y, e.x, e.y].join(" ")) } }, _initPath: function(t) { var e = t._path = o.SVG.create("path");
-                t.options.className && o.DomUtil.addClass(e, t.options.className), t.options.interactive && o.DomUtil.addClass(e, "leaflet-interactive"), this._updateStyle(t) }, _addPath: function(t) { this._rootGroup.appendChild(t._path), t.addInteractiveTarget(t._path) }, _removePath: function(t) { o.DomUtil.remove(t._path), t.removeInteractiveTarget(t._path) }, _updatePath: function(t) { t._project(), t._update() }, _updateStyle: function(t) { var e = t._path,
-                    i = t.options;
-                e && (i.stroke ? (e.setAttribute("stroke", i.color), e.setAttribute("stroke-opacity", i.opacity), e.setAttribute("stroke-width", i.weight), e.setAttribute("stroke-linecap", i.lineCap), e.setAttribute("stroke-linejoin", i.lineJoin), i.dashArray ? e.setAttribute("stroke-dasharray", i.dashArray) : e.removeAttribute("stroke-dasharray"), i.dashOffset ? e.setAttribute("stroke-dashoffset", i.dashOffset) : e.removeAttribute("stroke-dashoffset")) : e.setAttribute("stroke", "none"), i.fill ? (e.setAttribute("fill", i.fillColor || i.color), e.setAttribute("fill-opacity", i.fillOpacity), e.setAttribute("fill-rule", i.fillRule || "evenodd")) : e.setAttribute("fill", "none"), e.setAttribute("pointer-events", i.pointerEvents || (i.interactive ? "visiblePainted" : "none"))) }, _updatePoly: function(t, e) { this._setPath(t, o.SVG.pointsToPath(t._parts, e)) }, _updateCircle: function(t) { var e = t._point,
-                    i = t._radius,
-                    n = t._radiusY || i,
-                    o = "a" + i + "," + n + " 0 1,0 ",
-                    s = t._empty() ? "M0 0" : "M" + (e.x - i) + "," + e.y + o + 2 * i + ",0 " + o + 2 * -i + ",0 ";
-                this._setPath(t, s) }, _setPath: function(t, e) { t._path.setAttribute("d", e) }, _bringToFront: function(t) { o.DomUtil.toFront(t._path) }, _bringToBack: function(t) { o.DomUtil.toBack(t._path) } }), o.extend(o.SVG, { create: function(t) { return e.createElementNS("http://www.w3.org/2000/svg", t) }, pointsToPath: function(t, e) { var i, n, s, r, a, h, l = ""; for (i = 0, s = t.length; s > i; i++) { for (a = t[i], n = 0, r = a.length; r > n; n++) h = a[n], l += (n ? "L" : "M") + h.x + " " + h.y;
-                    l += e ? o.Browser.svg ? "z" : "x" : "" } return l || "M0 0" } }), o.Browser.svg = !(!e.createElementNS || !o.SVG.create("svg").createSVGRect), o.svg = function(t) { return o.Browser.svg || o.Browser.vml ? new o.SVG(t) : null }, o.Browser.vml = !o.Browser.svg && function() { try { var t = e.createElement("div");
-                t.innerHTML = '<v:shape adj="1"/>'; var i = t.firstChild; return i.style.behavior = "url(#default#VML)", i && "object" == typeof i.adj } catch (n) { return !1 } }(), o.SVG.include(o.Browser.vml ? { _initContainer: function() { this._container = o.DomUtil.create("div", "leaflet-vml-container") }, _update: function() { this._map._animatingZoom || o.Renderer.prototype._update.call(this) }, _initPath: function(t) { var e = t._container = o.SVG.create("shape");
-                o.DomUtil.addClass(e, "leaflet-vml-shape " + (this.options.className || "")), e.coordsize = "1 1", t._path = o.SVG.create("path"), e.appendChild(t._path), this._updateStyle(t) }, _addPath: function(t) { var e = t._container;
-                this._container.appendChild(e), t.options.interactive && t.addInteractiveTarget(e) }, _removePath: function(t) { var e = t._container;
-                o.DomUtil.remove(e), t.removeInteractiveTarget(e) }, _updateStyle: function(t) { var e = t._stroke,
-                    i = t._fill,
-                    n = t.options,
-                    s = t._container;
-                s.stroked = !!n.stroke, s.filled = !!n.fill, n.stroke ? (e || (e = t._stroke = o.SVG.create("stroke")), s.appendChild(e), e.weight = n.weight + "px", e.color = n.color, e.opacity = n.opacity, n.dashArray ? e.dashStyle = o.Util.isArray(n.dashArray) ? n.dashArray.join(" ") : n.dashArray.replace(/( *, *)/g, " ") : e.dashStyle = "", e.endcap = n.lineCap.replace("butt", "flat"), e.joinstyle = n.lineJoin) : e && (s.removeChild(e), t._stroke = null), n.fill ? (i || (i = t._fill = o.SVG.create("fill")), s.appendChild(i), i.color = n.fillColor || n.color, i.opacity = n.fillOpacity) : i && (s.removeChild(i), t._fill = null) }, _updateCircle: function(t) { var e = t._point.round(),
-                    i = Math.round(t._radius),
-                    n = Math.round(t._radiusY || i);
-                this._setPath(t, t._empty() ? "M0 0" : "AL " + e.x + "," + e.y + " " + i + "," + n + " 0,23592600") }, _setPath: function(t, e) { t._path.v = e }, _bringToFront: function(t) { o.DomUtil.toFront(t._container) }, _bringToBack: function(t) { o.DomUtil.toBack(t._container) } } : {}), o.Browser.vml && (o.SVG.create = function() { try { return e.namespaces.add("lvml", "urn:schemas-microsoft-com:vml"),
-                    function(t) { return e.createElement("<lvml:" + t + ' class="lvml">') } } catch (t) { return function(t) { return e.createElement("<" + t + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">') } } }()), o.Canvas = o.Renderer.extend({ onAdd: function() { o.Renderer.prototype.onAdd.call(this), this._layers = this._layers || {}, this._draw() }, _initContainer: function() { var t = this._container = e.createElement("canvas");
-                o.DomEvent.on(t, "mousemove", o.Util.throttle(this._onMouseMove, 32, this), this).on(t, "click dblclick mousedown mouseup contextmenu", this._onClick, this).on(t, "mouseout", this._handleMouseOut, this), this._ctx = t.getContext("2d") }, _update: function() { if (!this._map._animatingZoom || !this._bounds) { this._drawnLayers = {}, o.Renderer.prototype._update.call(this); var t = this._bounds,
-                        e = this._container,
-                        i = t.getSize(),
-                        n = o.Browser.retina ? 2 : 1;
-                    o.DomUtil.setPosition(e, t.min), e.width = n * i.x, e.height = n * i.y, e.style.width = i.x + "px", e.style.height = i.y + "px", o.Browser.retina && this._ctx.scale(2, 2), this._ctx.translate(-t.min.x, -t.min.y) } }, _initPath: function(t) { this._layers[o.stamp(t)] = t }, _addPath: o.Util.falseFn, _removePath: function(t) { t._removed = !0, this._requestRedraw(t) }, _updatePath: function(t) { this._redrawBounds = t._pxBounds, this._draw(!0), t._project(), t._update(), this._draw(), this._redrawBounds = null }, _updateStyle: function(t) { this._requestRedraw(t) }, _requestRedraw: function(t) { if (this._map) { var e = (t.options.weight || 0) + 1;
-                    this._redrawBounds = this._redrawBounds || new o.Bounds, this._redrawBounds.extend(t._pxBounds.min.subtract([e, e])), this._redrawBounds.extend(t._pxBounds.max.add([e, e])), this._redrawRequest = this._redrawRequest || o.Util.requestAnimFrame(this._redraw, this) } }, _redraw: function() { this._redrawRequest = null, this._draw(!0), this._draw(), this._redrawBounds = null }, _draw: function(t) { this._clear = t; var e, i = this._redrawBounds;
-                this._ctx.save(), i && (this._ctx.beginPath(), this._ctx.rect(i.min.x, i.min.y, i.max.x - i.min.x, i.max.y - i.min.y), this._ctx.clip()); for (var n in this._layers) e = this._layers[n], (!i || e._pxBounds.intersects(i)) && e._updatePath(), t && e._removed && (delete e._removed, delete this._layers[n]);
-                this._ctx.restore() }, _updatePoly: function(t, e) { var i, n, o, s, r = t._parts,
-                    a = r.length,
-                    h = this._ctx; if (a) { for (this._drawnLayers[t._leaflet_id] = t, h.beginPath(), i = 0; a > i; i++) { for (n = 0, o = r[i].length; o > n; n++) s = r[i][n], h[n ? "lineTo" : "moveTo"](s.x, s.y);
-                        e && h.closePath() }
-                    this._fillStroke(h, t) } }, _updateCircle: function(t) { if (!t._empty()) { var e = t._point,
-                        i = this._ctx,
-                        n = t._radius,
-                        o = (t._radiusY || n) / n;
-                    1 !== o && (i.save(), i.scale(1, o)), i.beginPath(), i.arc(e.x, e.y / o, n, 0, 2 * Math.PI, !1), 1 !== o && i.restore(), this._fillStroke(i, t) } }, _fillStroke: function(t, e) { var i = this._clear,
-                    n = e.options;
-                t.globalCompositeOperation = i ? "destination-out" : "source-over", n.fill && (t.globalAlpha = i ? 1 : n.fillOpacity, t.fillStyle = n.fillColor || n.color, t.fill(n.fillRule || "evenodd")), n.stroke && 0 !== n.weight && (t.globalAlpha = i ? 1 : n.opacity, e._prevWeight = t.lineWidth = i ? e._prevWeight + 1 : n.weight, t.strokeStyle = n.color, t.lineCap = n.lineCap, t.lineJoin = n.lineJoin, t.stroke()) }, _onClick: function(t) { var e = this._map.mouseEventToLayerPoint(t),
-                    i = []; for (var n in this._layers) this._layers[n]._containsPoint(e) && (o.DomEvent._fakeStop(t), i.push(this._layers[n]));
-                i.length && this._fireEvent(i, t) }, _onMouseMove: function(t) { if (this._map && !this._map.dragging._draggable._moving && !this._map._animatingZoom) { var e = this._map.mouseEventToLayerPoint(t);
-                    this._handleMouseOut(t, e), this._handleMouseHover(t, e) } }, _handleMouseOut: function(t, e) { var i = this._hoveredLayer;!i || "mouseout" !== t.type && i._containsPoint(e) || (o.DomUtil.removeClass(this._container, "leaflet-interactive"), this._fireEvent([i], t, "mouseout"), this._hoveredLayer = null) }, _handleMouseHover: function(t, e) { var i, n; if (!this._hoveredLayer)
-                    for (i in this._drawnLayers)
-                        if (n = this._drawnLayers[i], n.options.interactive && n._containsPoint(e)) { o.DomUtil.addClass(this._container, "leaflet-interactive"), this._fireEvent([n], t, "mouseover"), this._hoveredLayer = n; break }
-                this._hoveredLayer && this._fireEvent([this._hoveredLayer], t) }, _fireEvent: function(t, e, i) { this._map._fireDOMEvent(e, i || e.type, t) }, _bringToFront: o.Util.falseFn, _bringToBack: o.Util.falseFn }), o.Browser.canvas = function() { return !!e.createElement("canvas").getContext }(), o.canvas = function(t) { return o.Browser.canvas ? new o.Canvas(t) : null }, o.Polyline.prototype._containsPoint = function(t, e) { var i, n, s, r, a, h, l = this._clickTolerance(); if (!this._pxBounds.contains(t)) return !1; for (i = 0, r = this._parts.length; r > i; i++)
-                for (h = this._parts[i], n = 0, a = h.length, s = a - 1; a > n; s = n++)
-                    if ((e || 0 !== n) && o.LineUtil.pointToSegmentDistance(t, h[s], h[n]) <= l) return !0;
-            return !1 }, o.Polygon.prototype._containsPoint = function(t) { var e, i, n, s, r, a, h, l, u = !1; if (!this._pxBounds.contains(t)) return !1; for (s = 0, h = this._parts.length; h > s; s++)
-                for (e = this._parts[s], r = 0, l = e.length, a = l - 1; l > r; a = r++) i = e[r], n = e[a], i.y > t.y != n.y > t.y && t.x < (n.x - i.x) * (t.y - i.y) / (n.y - i.y) + i.x && (u = !u); return u || o.Polyline.prototype._containsPoint.call(this, t, !0) }, o.CircleMarker.prototype._containsPoint = function(t) { return t.distanceTo(this._point) <= this._radius + this._clickTolerance() }, o.GeoJSON = o.FeatureGroup.extend({ initialize: function(t, e) { o.setOptions(this, e), this._layers = {}, t && this.addData(t) }, addData: function(t) { var e, i, n, s = o.Util.isArray(t) ? t : t.features; if (s) { for (e = 0, i = s.length; i > e; e++) n = s[e], (n.geometries || n.geometry || n.features || n.coordinates) && this.addData(n); return this } var r = this.options; if (r.filter && !r.filter(t)) return this; var a = o.GeoJSON.geometryToLayer(t, r); return a ? (a.feature = o.GeoJSON.asFeature(t), a.defaultOptions = a.options, this.resetStyle(a), r.onEachFeature && r.onEachFeature(t, a), this.addLayer(a)) : this }, resetStyle: function(t) { return t.options = t.defaultOptions, this._setLayerStyle(t, this.options.style), this }, setStyle: function(t) { return this.eachLayer(function(e) { this._setLayerStyle(e, t) }, this) }, _setLayerStyle: function(t, e) { "function" == typeof e && (e = e(t.feature)), t.setStyle && t.setStyle(e) } }), o.extend(o.GeoJSON, { geometryToLayer: function(t, e) { var i, n, s, r, a = "Feature" === t.type ? t.geometry : t,
-                    h = a ? a.coordinates : null,
-                    l = [],
-                    u = e && e.pointToLayer,
-                    c = e && e.coordsToLatLng || this.coordsToLatLng; if (!h && !a) return null; switch (a.type) {
-                    case "Point":
-                        return i = c(h), u ? u(t, i) : new o.Marker(i);
-                    case "MultiPoint":
-                        for (s = 0, r = h.length; r > s; s++) i = c(h[s]), l.push(u ? u(t, i) : new o.Marker(i)); return new o.FeatureGroup(l);
-                    case "LineString":
-                    case "MultiLineString":
-                        return n = this.coordsToLatLngs(h, "LineString" === a.type ? 0 : 1, c), new o.Polyline(n, e);
-                    case "Polygon":
-                    case "MultiPolygon":
-                        return n = this.coordsToLatLngs(h, "Polygon" === a.type ? 1 : 2, c), new o.Polygon(n, e);
-                    case "GeometryCollection":
-                        for (s = 0, r = a.geometries.length; r > s; s++) { var d = this.geometryToLayer({ geometry: a.geometries[s], type: "Feature", properties: t.properties }, e);
-                            d && l.push(d) } return new o.FeatureGroup(l);
-                    default:
-                        throw new Error("Invalid GeoJSON object.") } }, coordsToLatLng: function(t) { return new o.LatLng(t[1], t[0], t[2]) }, coordsToLatLngs: function(t, e, i) { for (var n, o = [], s = 0, r = t.length; r > s; s++) n = e ? this.coordsToLatLngs(t[s], e - 1, i) : (i || this.coordsToLatLng)(t[s]), o.push(n); return o }, latLngToCoords: function(t) { return t.alt !== i ? [t.lng, t.lat, t.alt] : [t.lng, t.lat] }, latLngsToCoords: function(t, e, i) { for (var n = [], s = 0, r = t.length; r > s; s++) n.push(e ? o.GeoJSON.latLngsToCoords(t[s], e - 1, i) : o.GeoJSON.latLngToCoords(t[s])); return !e && i && n.push(n[0]), n }, getFeature: function(t, e) { return t.feature ? o.extend({}, t.feature, { geometry: e }) : o.GeoJSON.asFeature(e) }, asFeature: function(t) { return "Feature" === t.type ? t : { type: "Feature", properties: {}, geometry: t } } });
-    var r = { toGeoJSON: function() { return o.GeoJSON.getFeature(this, { type: "Point", coordinates: o.GeoJSON.latLngToCoords(this.getLatLng()) }) } };
-    o.Marker.include(r), o.Circle.include(r), o.CircleMarker.include(r), o.Polyline.prototype.toGeoJSON = function() { var t = !o.Polyline._flat(this._latlngs),
-            e = o.GeoJSON.latLngsToCoords(this._latlngs, t ? 1 : 0); return o.GeoJSON.getFeature(this, { type: (t ? "Multi" : "") + "LineString", coordinates: e }) }, o.Polygon.prototype.toGeoJSON = function() { var t = !o.Polyline._flat(this._latlngs),
-            e = t && !o.Polyline._flat(this._latlngs[0]),
-            i = o.GeoJSON.latLngsToCoords(this._latlngs, e ? 2 : t ? 1 : 0, !0); return t || (i = [i]), o.GeoJSON.getFeature(this, { type: (e ? "Multi" : "") + "Polygon", coordinates: i }) }, o.LayerGroup.include({ toMultiPoint: function() { var t = []; return this.eachLayer(function(e) { t.push(e.toGeoJSON().geometry.coordinates) }), o.GeoJSON.getFeature(this, { type: "MultiPoint", coordinates: t }) }, toGeoJSON: function() { var t = this.feature && this.feature.geometry && this.feature.geometry.type; if ("MultiPoint" === t) return this.toMultiPoint(); var e = "GeometryCollection" === t,
-                i = []; return this.eachLayer(function(t) { if (t.toGeoJSON) { var n = t.toGeoJSON();
-                    i.push(e ? n.geometry : o.GeoJSON.asFeature(n)) } }), e ? o.GeoJSON.getFeature(this, { geometries: i, type: "GeometryCollection" }) : { type: "FeatureCollection", features: i } } }), o.geoJson = function(t, e) { return new o.GeoJSON(t, e) };
-    var a = "_leaflet_events";
-    o.DomEvent = { on: function(t, e, i, n) { if ("object" == typeof e)
-                for (var s in e) this._on(t, s, e[s], i);
-            else { e = o.Util.splitWords(e); for (var r = 0, a = e.length; a > r; r++) this._on(t, e[r], i, n) } return this }, off: function(t, e, i, n) { if ("object" == typeof e)
-                for (var s in e) this._off(t, s, e[s], i);
-            else { e = o.Util.splitWords(e); for (var r = 0, a = e.length; a > r; r++) this._off(t, e[r], i, n) } return this }, _on: function(e, i, n, s) { var r = i + o.stamp(n) + (s ? "_" + o.stamp(s) : ""); if (e[a] && e[a][r]) return this; var h = function(i) { return n.call(s || e, i || t.event) },
-                l = h; return o.Browser.pointer && 0 === i.indexOf("touch") ? this.addPointerListener(e, i, h, r) : o.Browser.touch && "dblclick" === i && this.addDoubleTapListener ? this.addDoubleTapListener(e, h, r) : "addEventListener" in e ? "mousewheel" === i ? (e.addEventListener("DOMMouseScroll", h, !1), e.addEventListener(i, h, !1)) : "mouseenter" === i || "mouseleave" === i ? (h = function(i) { i = i || t.event, o.DomEvent._isExternalTarget(e, i) && l(i) }, e.addEventListener("mouseenter" === i ? "mouseover" : "mouseout", h, !1)) : ("click" === i && o.Browser.android && (h = function(t) { return o.DomEvent._filterClick(t, l) }), e.addEventListener(i, h, !1)) : "attachEvent" in e && e.attachEvent("on" + i, h), e[a] = e[a] || {}, e[a][r] = h, this }, _off: function(t, e, i, n) { var s = e + o.stamp(i) + (n ? "_" + o.stamp(n) : ""),
-                r = t[a] && t[a][s]; return r ? (o.Browser.pointer && 0 === e.indexOf("touch") ? this.removePointerListener(t, e, s) : o.Browser.touch && "dblclick" === e && this.removeDoubleTapListener ? this.removeDoubleTapListener(t, s) : "removeEventListener" in t ? "mousewheel" === e ? (t.removeEventListener("DOMMouseScroll", r, !1), t.removeEventListener(e, r, !1)) : t.removeEventListener("mouseenter" === e ? "mouseover" : "mouseleave" === e ? "mouseout" : e, r, !1) : "detachEvent" in t && t.detachEvent("on" + e, r), t[a][s] = null, this) : this }, stopPropagation: function(t) { return t.stopPropagation ? t.stopPropagation() : t.originalEvent ? t.originalEvent._stopped = !0 : t.cancelBubble = !0, o.DomEvent._skipped(t), this }, disableScrollPropagation: function(t) { return o.DomEvent.on(t, "mousewheel MozMousePixelScroll", o.DomEvent.stopPropagation) }, disableClickPropagation: function(t) { var e = o.DomEvent.stopPropagation; return o.DomEvent.on(t, o.Draggable.START.join(" "), e), o.DomEvent.on(t, { click: o.DomEvent._fakeStop, dblclick: e }) }, preventDefault: function(t) { return t.preventDefault ? t.preventDefault() : t.returnValue = !1, this }, stop: function(t) { return o.DomEvent.preventDefault(t).stopPropagation(t) }, getMousePosition: function(t, e) { if (!e) return new o.Point(t.clientX, t.clientY); var i = e.getBoundingClientRect(); return new o.Point(t.clientX - i.left - e.clientLeft, t.clientY - i.top - e.clientTop) }, getWheelDelta: function(t) { var e = 0; return t.wheelDelta && (e = t.wheelDelta / 120), t.detail && (e = -t.detail / 3), e }, _skipEvents: {}, _fakeStop: function(t) { o.DomEvent._skipEvents[t.type] = !0 }, _skipped: function(t) { var e = this._skipEvents[t.type]; return this._skipEvents[t.type] = !1, e }, _isExternalTarget: function(t, e) { var i = e.relatedTarget; if (!i) return !0; try { for (; i && i !== t;) i = i.parentNode } catch (n) { return !1 } return i !== t }, _filterClick: function(t, e) { var i = t.timeStamp || t.originalEvent.timeStamp,
-                n = o.DomEvent._lastClick && i - o.DomEvent._lastClick; return n && n > 100 && 500 > n || t.target._simulatedClick && !t._simulated ? void o.DomEvent.stop(t) : (o.DomEvent._lastClick = i, void e(t)) } }, o.DomEvent.addListener = o.DomEvent.on, o.DomEvent.removeListener = o.DomEvent.off, o.Draggable = o.Evented.extend({
-        statics: { START: o.Browser.touch ? ["touchstart", "mousedown"] : ["mousedown"], END: { mousedown: "mouseup", touchstart: "touchend", pointerdown: "touchend", MSPointerDown: "touchend" }, MOVE: { mousedown: "mousemove", touchstart: "touchmove", pointerdown: "touchmove", MSPointerDown: "touchmove" } },
-        initialize: function(t, e, i) { this._element = t, this._dragStartTarget = e || t, this._preventOutline = i },
-        enable: function() { this._enabled || (o.DomEvent.on(this._dragStartTarget, o.Draggable.START.join(" "), this._onDown, this), this._enabled = !0) },
-        disable: function() { this._enabled && (o.DomEvent.off(this._dragStartTarget, o.Draggable.START.join(" "), this._onDown, this), this._enabled = !1, this._moved = !1) },
-        _onDown: function(t) { if (this._moved = !1, !o.DomUtil.hasClass(this._element, "leaflet-zoom-anim") && !(o.Draggable._dragging || t.shiftKey || 1 !== t.which && 1 !== t.button && !t.touches) && this._enabled && (o.Draggable._dragging = !0, this._preventOutline && o.DomUtil.preventOutline(this._element), o.DomUtil.disableImageDrag(), o.DomUtil.disableTextSelection(), !this._moving)) { this.fire("down"); var i = t.touches ? t.touches[0] : t;
-                this._startPoint = new o.Point(i.clientX, i.clientY), this._startPos = this._newPos = o.DomUtil.getPosition(this._element), o.DomEvent.on(e, o.Draggable.MOVE[t.type], this._onMove, this).on(e, o.Draggable.END[t.type], this._onUp, this) } },
-        _onMove: function(t) {
-            if (t.touches && t.touches.length > 1) return void(this._moved = !0);
-            var i = t.touches && 1 === t.touches.length ? t.touches[0] : t,
-                n = new o.Point(i.clientX, i.clientY),
-                s = n.subtract(this._startPoint);
-            (s.x || s.y) && (o.Browser.touch && Math.abs(s.x) + Math.abs(s.y) < 3 || (o.DomEvent.preventDefault(t), this._moved || (this.fire("dragstart"), this._moved = !0, this._startPos = o.DomUtil.getPosition(this._element).subtract(s), o.DomUtil.addClass(e.body, "leaflet-dragging"), this._lastTarget = t.target || t.srcElement, o.DomUtil.addClass(this._lastTarget, "leaflet-drag-target")),
-                this._newPos = this._startPos.add(s), this._moving = !0, o.Util.cancelAnimFrame(this._animRequest), this._lastEvent = t, this._animRequest = o.Util.requestAnimFrame(this._updatePosition, this, !0)))
-        },
-        _updatePosition: function() { var t = { originalEvent: this._lastEvent };
-            this.fire("predrag", t), o.DomUtil.setPosition(this._element, this._newPos), this.fire("drag", t) },
-        _onUp: function() { o.DomUtil.removeClass(e.body, "leaflet-dragging"), this._lastTarget && (o.DomUtil.removeClass(this._lastTarget, "leaflet-drag-target"), this._lastTarget = null); for (var t in o.Draggable.MOVE) o.DomEvent.off(e, o.Draggable.MOVE[t], this._onMove, this).off(e, o.Draggable.END[t], this._onUp, this);
-            o.DomUtil.enableImageDrag(), o.DomUtil.enableTextSelection(), this._moved && this._moving && (o.Util.cancelAnimFrame(this._animRequest), this.fire("dragend", { distance: this._newPos.distanceTo(this._startPos) })), this._moving = !1, o.Draggable._dragging = !1 }
-    }), o.Handler = o.Class.extend({ initialize: function(t) { this._map = t }, enable: function() { this._enabled || (this._enabled = !0, this.addHooks()) }, disable: function() { this._enabled && (this._enabled = !1, this.removeHooks()) }, enabled: function() { return !!this._enabled } }), o.Map.mergeOptions({ dragging: !0, inertia: !o.Browser.android23, inertiaDeceleration: 3400, inertiaMaxSpeed: 1 / 0, easeLinearity: .2, worldCopyJump: !1 }), o.Map.Drag = o.Handler.extend({ addHooks: function() { if (!this._draggable) { var t = this._map;
-                this._draggable = new o.Draggable(t._mapPane, t._container), this._draggable.on({ down: this._onDown, dragstart: this._onDragStart, drag: this._onDrag, dragend: this._onDragEnd }, this), this._draggable.on("predrag", this._onPreDragLimit, this), t.options.worldCopyJump && (this._draggable.on("predrag", this._onPreDragWrap, this), t.on("zoomend", this._onZoomEnd, this), t.whenReady(this._onZoomEnd, this)) }
-            o.DomUtil.addClass(this._map._container, "leaflet-grab"), this._draggable.enable() }, removeHooks: function() { o.DomUtil.removeClass(this._map._container, "leaflet-grab"), this._draggable.disable() }, moved: function() { return this._draggable && this._draggable._moved }, _onDown: function() { this._map.stop() }, _onDragStart: function() { var t = this._map; if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) { var e = o.latLngBounds(this._map.options.maxBounds);
-                this._offsetLimit = o.bounds(this._map.latLngToContainerPoint(e.getNorthWest()).multiplyBy(-1), this._map.latLngToContainerPoint(e.getSouthEast()).multiplyBy(-1).add(this._map.getSize())), this._viscosity = Math.min(1, Math.max(0, this._map.options.maxBoundsViscosity)) } else this._offsetLimit = null;
-            t.fire("movestart").fire("dragstart"), t.options.inertia && (this._positions = [], this._times = []) }, _onDrag: function(t) { if (this._map.options.inertia) { var e = this._lastTime = +new Date,
-                    i = this._lastPos = this._draggable._absPos || this._draggable._newPos;
-                this._positions.push(i), this._times.push(e), e - this._times[0] > 50 && (this._positions.shift(), this._times.shift()) }
-            this._map.fire("move", t).fire("drag", t) }, _onZoomEnd: function() { var t = this._map.getSize().divideBy(2),
-                e = this._map.latLngToLayerPoint([0, 0]);
-            this._initialWorldOffset = e.subtract(t).x, this._worldWidth = this._map.getPixelWorldBounds().getSize().x }, _viscousLimit: function(t, e) { return t - (t - e) * this._viscosity }, _onPreDragLimit: function() { if (this._viscosity && this._offsetLimit) { var t = this._draggable._newPos.subtract(this._draggable._startPos),
-                    e = this._offsetLimit;
-                t.x < e.min.x && (t.x = this._viscousLimit(t.x, e.min.x)), t.y < e.min.y && (t.y = this._viscousLimit(t.y, e.min.y)), t.x > e.max.x && (t.x = this._viscousLimit(t.x, e.max.x)), t.y > e.max.y && (t.y = this._viscousLimit(t.y, e.max.y)), this._draggable._newPos = this._draggable._startPos.add(t) } }, _onPreDragWrap: function() { var t = this._worldWidth,
-                e = Math.round(t / 2),
-                i = this._initialWorldOffset,
-                n = this._draggable._newPos.x,
-                o = (n - e + i) % t + e - i,
-                s = (n + e + i) % t - e - i,
-                r = Math.abs(o + i) < Math.abs(s + i) ? o : s;
-            this._draggable._absPos = this._draggable._newPos.clone(), this._draggable._newPos.x = r }, _onDragEnd: function(t) { var e = this._map,
-                i = e.options,
-                n = !i.inertia || this._times.length < 2; if (e.fire("dragend", t), n) e.fire("moveend");
-            else { var s = this._lastPos.subtract(this._positions[0]),
-                    r = (this._lastTime - this._times[0]) / 1e3,
-                    a = i.easeLinearity,
-                    h = s.multiplyBy(a / r),
-                    l = h.distanceTo([0, 0]),
-                    u = Math.min(i.inertiaMaxSpeed, l),
-                    c = h.multiplyBy(u / l),
-                    d = u / (i.inertiaDeceleration * a),
-                    _ = c.multiplyBy(-d / 2).round();
-                _.x || _.y ? (_ = e._limitOffset(_, e.options.maxBounds), o.Util.requestAnimFrame(function() { e.panBy(_, { duration: d, easeLinearity: a, noMoveStart: !0, animate: !0 }) })) : e.fire("moveend") } } }), o.Map.addInitHook("addHandler", "dragging", o.Map.Drag), o.Map.mergeOptions({ doubleClickZoom: !0 }), o.Map.DoubleClickZoom = o.Handler.extend({ addHooks: function() { this._map.on("dblclick", this._onDoubleClick, this) }, removeHooks: function() { this._map.off("dblclick", this._onDoubleClick, this) }, _onDoubleClick: function(t) { var e = this._map,
-                i = e.getZoom(),
-                n = t.originalEvent.shiftKey ? Math.ceil(i) - 1 : Math.floor(i) + 1; "center" === e.options.doubleClickZoom ? e.setZoom(n) : e.setZoomAround(t.containerPoint, n) } }), o.Map.addInitHook("addHandler", "doubleClickZoom", o.Map.DoubleClickZoom), o.Map.mergeOptions({ scrollWheelZoom: !0, wheelDebounceTime: 40 }), o.Map.ScrollWheelZoom = o.Handler.extend({ addHooks: function() { o.DomEvent.on(this._map._container, { mousewheel: this._onWheelScroll, MozMousePixelScroll: o.DomEvent.preventDefault }, this), this._delta = 0 }, removeHooks: function() { o.DomEvent.off(this._map._container, { mousewheel: this._onWheelScroll, MozMousePixelScroll: o.DomEvent.preventDefault }, this) }, _onWheelScroll: function(t) { var e = o.DomEvent.getWheelDelta(t),
-                i = this._map.options.wheelDebounceTime;
-            this._delta += e, this._lastMousePos = this._map.mouseEventToContainerPoint(t), this._startTime || (this._startTime = +new Date); var n = Math.max(i - (+new Date - this._startTime), 0);
-            clearTimeout(this._timer), this._timer = setTimeout(o.bind(this._performZoom, this), n), o.DomEvent.stop(t) }, _performZoom: function() { var t = this._map,
-                e = this._delta,
-                i = t.getZoom();
-            t.stop(), e = e > 0 ? Math.ceil(e) : Math.floor(e), e = Math.max(Math.min(e, 4), -4), e = t._limitZoom(i + e) - i, this._delta = 0, this._startTime = null, e && ("center" === t.options.scrollWheelZoom ? t.setZoom(i + e) : t.setZoomAround(this._lastMousePos, i + e)) } }), o.Map.addInitHook("addHandler", "scrollWheelZoom", o.Map.ScrollWheelZoom), o.extend(o.DomEvent, { _touchstart: o.Browser.msPointer ? "MSPointerDown" : o.Browser.pointer ? "pointerdown" : "touchstart", _touchend: o.Browser.msPointer ? "MSPointerUp" : o.Browser.pointer ? "pointerup" : "touchend", addDoubleTapListener: function(t, e, i) {
-            function n(t) { var e; if (e = o.Browser.pointer ? o.DomEvent._pointersCount : t.touches.length, !(e > 1)) { var i = Date.now(),
-                        n = i - (r || i);
-                    a = t.touches ? t.touches[0] : t, h = n > 0 && l >= n, r = i } }
-
-            function s() { if (h && !a.cancelBubble) { if (o.Browser.pointer) { var t, i, n = {}; for (i in a) t = a[i], n[i] = t && t.bind ? t.bind(a) : t;
-                        a = n }
-                    a.type = "dblclick", e(a), r = null } } var r, a, h = !1,
-                l = 250,
-                u = "_leaflet_",
-                c = this._touchstart,
-                d = this._touchend; return t[u + c + i] = n, t[u + d + i] = s, t.addEventListener(c, n, !1), t.addEventListener(d, s, !1), this }, removeDoubleTapListener: function(t, e) { var i = "_leaflet_",
-                n = t[i + this._touchend + e]; return t.removeEventListener(this._touchstart, t[i + this._touchstart + e], !1), t.removeEventListener(this._touchend, n, !1), this } }), o.extend(o.DomEvent, { POINTER_DOWN: o.Browser.msPointer ? "MSPointerDown" : "pointerdown", POINTER_MOVE: o.Browser.msPointer ? "MSPointerMove" : "pointermove", POINTER_UP: o.Browser.msPointer ? "MSPointerUp" : "pointerup", POINTER_CANCEL: o.Browser.msPointer ? "MSPointerCancel" : "pointercancel", _pointers: {}, _pointersCount: 0, addPointerListener: function(t, e, i, n) { return "touchstart" === e ? this._addPointerStart(t, i, n) : "touchmove" === e ? this._addPointerMove(t, i, n) : "touchend" === e && this._addPointerEnd(t, i, n), this }, removePointerListener: function(t, e, i) { var n = t["_leaflet_" + e + i]; return "touchstart" === e ? t.removeEventListener(this.POINTER_DOWN, n, !1) : "touchmove" === e ? t.removeEventListener(this.POINTER_MOVE, n, !1) : "touchend" === e && (t.removeEventListener(this.POINTER_UP, n, !1), t.removeEventListener(this.POINTER_CANCEL, n, !1)), this }, _addPointerStart: function(t, i, n) { var s = o.bind(function(t) { "mouse" !== t.pointerType && t.pointerType !== t.MSPOINTER_TYPE_MOUSE && o.DomEvent.preventDefault(t), this._handlePointer(t, i) }, this); if (t["_leaflet_touchstart" + n] = s, t.addEventListener(this.POINTER_DOWN, s, !1), !this._pointerDocListener) { var r = o.bind(this._globalPointerUp, this);
-                e.documentElement.addEventListener(this.POINTER_DOWN, o.bind(this._globalPointerDown, this), !0), e.documentElement.addEventListener(this.POINTER_MOVE, o.bind(this._globalPointerMove, this), !0), e.documentElement.addEventListener(this.POINTER_UP, r, !0), e.documentElement.addEventListener(this.POINTER_CANCEL, r, !0), this._pointerDocListener = !0 } }, _globalPointerDown: function(t) { this._pointers[t.pointerId] = t, this._pointersCount++ }, _globalPointerMove: function(t) { this._pointers[t.pointerId] && (this._pointers[t.pointerId] = t) }, _globalPointerUp: function(t) { delete this._pointers[t.pointerId], this._pointersCount-- }, _handlePointer: function(t, e) { t.touches = []; for (var i in this._pointers) t.touches.push(this._pointers[i]);
-            t.changedTouches = [t], e(t) }, _addPointerMove: function(t, e, i) { var n = o.bind(function(t) {
-                (t.pointerType !== t.MSPOINTER_TYPE_MOUSE && "mouse" !== t.pointerType || 0 !== t.buttons) && this._handlePointer(t, e) }, this);
-            t["_leaflet_touchmove" + i] = n, t.addEventListener(this.POINTER_MOVE, n, !1) }, _addPointerEnd: function(t, e, i) { var n = o.bind(function(t) { this._handlePointer(t, e) }, this);
-            t["_leaflet_touchend" + i] = n, t.addEventListener(this.POINTER_UP, n, !1), t.addEventListener(this.POINTER_CANCEL, n, !1) } }), o.Map.mergeOptions({ touchZoom: o.Browser.touch && !o.Browser.android23, bounceAtZoomLimits: !0 }), o.Map.TouchZoom = o.Handler.extend({ addHooks: function() { o.DomEvent.on(this._map._container, "touchstart", this._onTouchStart, this) }, removeHooks: function() { o.DomEvent.off(this._map._container, "touchstart", this._onTouchStart, this) }, _onTouchStart: function(t) { var i = this._map; if (t.touches && 2 === t.touches.length && !i._animatingZoom && !this._zooming) { var n = i.mouseEventToContainerPoint(t.touches[0]),
-                    s = i.mouseEventToContainerPoint(t.touches[1]);
-                this._centerPoint = i.getSize()._divideBy(2), this._startLatLng = i.containerPointToLatLng(this._centerPoint), "center" !== i.options.touchZoom && (this._pinchStartLatLng = i.containerPointToLatLng(n.add(s)._divideBy(2))), this._startDist = n.distanceTo(s), this._startZoom = i.getZoom(), this._moved = !1, this._zooming = !0, i.stop(), o.DomEvent.on(e, "touchmove", this._onTouchMove, this).on(e, "touchend", this._onTouchEnd, this), o.DomEvent.preventDefault(t) } }, _onTouchMove: function(t) { if (t.touches && 2 === t.touches.length && this._zooming) { var e = this._map,
-                    i = e.mouseEventToContainerPoint(t.touches[0]),
-                    n = e.mouseEventToContainerPoint(t.touches[1]),
-                    s = i.distanceTo(n) / this._startDist; if (this._zoom = e.getScaleZoom(s, this._startZoom), "center" === e.options.touchZoom) { if (this._center = this._startLatLng, 1 === s) return } else { var r = i._add(n)._divideBy(2)._subtract(this._centerPoint); if (1 === s && 0 === r.x && 0 === r.y) return;
-                    this._center = e.unproject(e.project(this._pinchStartLatLng).subtract(r)) } if (e.options.bounceAtZoomLimits || !(this._zoom <= e.getMinZoom() && 1 > s || this._zoom >= e.getMaxZoom() && s > 1)) { this._moved || (e._moveStart(!0), this._moved = !0), o.Util.cancelAnimFrame(this._animRequest); var a = o.bind(e._move, e, this._center, this._zoom, { pinch: !0, round: !1 });
-                    this._animRequest = o.Util.requestAnimFrame(a, this, !0), o.DomEvent.preventDefault(t) } } }, _onTouchEnd: function() { if (!this._moved || !this._zooming) return void(this._zooming = !1);
-            this._zooming = !1, o.Util.cancelAnimFrame(this._animRequest), o.DomEvent.off(e, "touchmove", this._onTouchMove).off(e, "touchend", this._onTouchEnd); var t = this._zoom;
-            t = this._map._limitZoom(t - this._startZoom > 0 ? Math.ceil(t) : Math.floor(t)), this._map._animateZoom(this._center, t, !0, !0) } }), o.Map.addInitHook("addHandler", "touchZoom", o.Map.TouchZoom), o.Map.mergeOptions({ tap: !0, tapTolerance: 15 }), o.Map.Tap = o.Handler.extend({ addHooks: function() { o.DomEvent.on(this._map._container, "touchstart", this._onDown, this) }, removeHooks: function() { o.DomEvent.off(this._map._container, "touchstart", this._onDown, this) }, _onDown: function(t) { if (t.touches) { if (o.DomEvent.preventDefault(t), this._fireClick = !0, t.touches.length > 1) return this._fireClick = !1, void clearTimeout(this._holdTimeout); var i = t.touches[0],
-                    n = i.target;
-                this._startPos = this._newPos = new o.Point(i.clientX, i.clientY), n.tagName && "a" === n.tagName.toLowerCase() && o.DomUtil.addClass(n, "leaflet-active"), this._holdTimeout = setTimeout(o.bind(function() { this._isTapValid() && (this._fireClick = !1, this._onUp(), this._simulateEvent("contextmenu", i)) }, this), 1e3), this._simulateEvent("mousedown", i), o.DomEvent.on(e, { touchmove: this._onMove, touchend: this._onUp }, this) } }, _onUp: function(t) { if (clearTimeout(this._holdTimeout), o.DomEvent.off(e, { touchmove: this._onMove, touchend: this._onUp }, this), this._fireClick && t && t.changedTouches) { var i = t.changedTouches[0],
-                    n = i.target;
-                n && n.tagName && "a" === n.tagName.toLowerCase() && o.DomUtil.removeClass(n, "leaflet-active"), this._simulateEvent("mouseup", i), this._isTapValid() && this._simulateEvent("click", i) } }, _isTapValid: function() { return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance }, _onMove: function(t) { var e = t.touches[0];
-            this._newPos = new o.Point(e.clientX, e.clientY), this._simulateEvent("mousemove", e) }, _simulateEvent: function(i, n) { var o = e.createEvent("MouseEvents");
-            o._simulated = !0, n.target._simulatedClick = !0, o.initMouseEvent(i, !0, !0, t, 1, n.screenX, n.screenY, n.clientX, n.clientY, !1, !1, !1, !1, 0, null), n.target.dispatchEvent(o) } }), o.Browser.touch && !o.Browser.pointer && o.Map.addInitHook("addHandler", "tap", o.Map.Tap), o.Map.mergeOptions({ boxZoom: !0 }), o.Map.BoxZoom = o.Handler.extend({ initialize: function(t) { this._map = t, this._container = t._container, this._pane = t._panes.overlayPane }, addHooks: function() { o.DomEvent.on(this._container, "mousedown", this._onMouseDown, this) }, removeHooks: function() { o.DomEvent.off(this._container, "mousedown", this._onMouseDown, this) }, moved: function() { return this._moved }, _resetState: function() { this._moved = !1 }, _onMouseDown: function(t) { return !t.shiftKey || 1 !== t.which && 1 !== t.button ? !1 : (this._resetState(), o.DomUtil.disableTextSelection(), o.DomUtil.disableImageDrag(), this._startPoint = this._map.mouseEventToContainerPoint(t), void o.DomEvent.on(e, { contextmenu: o.DomEvent.stop, mousemove: this._onMouseMove, mouseup: this._onMouseUp, keydown: this._onKeyDown }, this)) }, _onMouseMove: function(t) { this._moved || (this._moved = !0, this._box = o.DomUtil.create("div", "leaflet-zoom-box", this._container), o.DomUtil.addClass(this._container, "leaflet-crosshair"), this._map.fire("boxzoomstart")), this._point = this._map.mouseEventToContainerPoint(t); var e = new o.Bounds(this._point, this._startPoint),
-                i = e.getSize();
-            o.DomUtil.setPosition(this._box, e.min), this._box.style.width = i.x + "px", this._box.style.height = i.y + "px" }, _finish: function() { this._moved && (o.DomUtil.remove(this._box), o.DomUtil.removeClass(this._container, "leaflet-crosshair")), o.DomUtil.enableTextSelection(), o.DomUtil.enableImageDrag(), o.DomEvent.off(e, { contextmenu: o.DomEvent.stop, mousemove: this._onMouseMove, mouseup: this._onMouseUp, keydown: this._onKeyDown }, this) }, _onMouseUp: function(t) { if ((1 === t.which || 1 === t.button) && (this._finish(), this._moved)) { setTimeout(o.bind(this._resetState, this), 0); var e = new o.LatLngBounds(this._map.containerPointToLatLng(this._startPoint), this._map.containerPointToLatLng(this._point));
-                this._map.fitBounds(e).fire("boxzoomend", { boxZoomBounds: e }) } }, _onKeyDown: function(t) { 27 === t.keyCode && this._finish() } }), o.Map.addInitHook("addHandler", "boxZoom", o.Map.BoxZoom), o.Map.mergeOptions({ keyboard: !0, keyboardPanOffset: 80, keyboardZoomOffset: 1 }), o.Map.Keyboard = o.Handler.extend({ keyCodes: { left: [37], right: [39], down: [40], up: [38], zoomIn: [187, 107, 61, 171], zoomOut: [189, 109, 54, 173] }, initialize: function(t) { this._map = t, this._setPanOffset(t.options.keyboardPanOffset), this._setZoomOffset(t.options.keyboardZoomOffset) }, addHooks: function() { var t = this._map._container;
-            t.tabIndex <= 0 && (t.tabIndex = "0"), o.DomEvent.on(t, { focus: this._onFocus, blur: this._onBlur, mousedown: this._onMouseDown }, this), this._map.on({ focus: this._addHooks, blur: this._removeHooks }, this) }, removeHooks: function() { this._removeHooks(), o.DomEvent.off(this._map._container, { focus: this._onFocus, blur: this._onBlur, mousedown: this._onMouseDown }, this), this._map.off({ focus: this._addHooks, blur: this._removeHooks }, this) }, _onMouseDown: function() { if (!this._focused) { var i = e.body,
-                    n = e.documentElement,
-                    o = i.scrollTop || n.scrollTop,
-                    s = i.scrollLeft || n.scrollLeft;
-                this._map._container.focus(), t.scrollTo(s, o) } }, _onFocus: function() { this._focused = !0, this._map.fire("focus") }, _onBlur: function() { this._focused = !1, this._map.fire("blur") }, _setPanOffset: function(t) { var e, i, n = this._panKeys = {},
-                o = this.keyCodes; for (e = 0, i = o.left.length; i > e; e++) n[o.left[e]] = [-1 * t, 0]; for (e = 0, i = o.right.length; i > e; e++) n[o.right[e]] = [t, 0]; for (e = 0, i = o.down.length; i > e; e++) n[o.down[e]] = [0, t]; for (e = 0, i = o.up.length; i > e; e++) n[o.up[e]] = [0, -1 * t] }, _setZoomOffset: function(t) { var e, i, n = this._zoomKeys = {},
-                o = this.keyCodes; for (e = 0, i = o.zoomIn.length; i > e; e++) n[o.zoomIn[e]] = t; for (e = 0, i = o.zoomOut.length; i > e; e++) n[o.zoomOut[e]] = -t }, _addHooks: function() { o.DomEvent.on(e, "keydown", this._onKeyDown, this) }, _removeHooks: function() { o.DomEvent.off(e, "keydown", this._onKeyDown, this) }, _onKeyDown: function(t) { if (!(t.altKey || t.ctrlKey || t.metaKey)) { var e, i = t.keyCode,
-                    n = this._map; if (i in this._panKeys) { if (n._panAnim && n._panAnim._inProgress) return;
-                    e = this._panKeys[i], t.shiftKey && (e = o.point(e).multiplyBy(3)), n.panBy(e), n.options.maxBounds && n.panInsideBounds(n.options.maxBounds) } else if (i in this._zoomKeys) n.setZoom(n.getZoom() + (t.shiftKey ? 3 : 1) * this._zoomKeys[i]);
-                else { if (27 !== i) return;
-                    n.closePopup() }
-                o.DomEvent.stop(t) } } }), o.Map.addInitHook("addHandler", "keyboard", o.Map.Keyboard), o.Handler.MarkerDrag = o.Handler.extend({ initialize: function(t) { this._marker = t }, addHooks: function() { var t = this._marker._icon;
-            this._draggable || (this._draggable = new o.Draggable(t, t, !0)), this._draggable.on({ dragstart: this._onDragStart, drag: this._onDrag, dragend: this._onDragEnd }, this).enable(), o.DomUtil.addClass(t, "leaflet-marker-draggable") }, removeHooks: function() { this._draggable.off({ dragstart: this._onDragStart, drag: this._onDrag, dragend: this._onDragEnd }, this).disable(), this._marker._icon && o.DomUtil.removeClass(this._marker._icon, "leaflet-marker-draggable") }, moved: function() { return this._draggable && this._draggable._moved }, _onDragStart: function() { this._marker.closePopup().fire("movestart").fire("dragstart") }, _onDrag: function(t) { var e = this._marker,
-                i = e._shadow,
-                n = o.DomUtil.getPosition(e._icon),
-                s = e._map.layerPointToLatLng(n);
-            i && o.DomUtil.setPosition(i, n), e._latlng = s, t.latlng = s, e.fire("move", t).fire("drag", t) }, _onDragEnd: function(t) { this._marker.fire("moveend").fire("dragend", t) } }), o.Control = o.Class.extend({ options: { position: "topright" }, initialize: function(t) { o.setOptions(this, t) }, getPosition: function() { return this.options.position }, setPosition: function(t) { var e = this._map; return e && e.removeControl(this), this.options.position = t, e && e.addControl(this), this }, getContainer: function() { return this._container }, addTo: function(t) { this.remove(), this._map = t; var e = this._container = this.onAdd(t),
-                i = this.getPosition(),
-                n = t._controlCorners[i]; return o.DomUtil.addClass(e, "leaflet-control"), -1 !== i.indexOf("bottom") ? n.insertBefore(e, n.firstChild) : n.appendChild(e), this }, remove: function() { return this._map ? (o.DomUtil.remove(this._container), this.onRemove && this.onRemove(this._map), this._map = null, this) : this }, _refocusOnMap: function(t) { this._map && t && t.screenX > 0 && t.screenY > 0 && this._map.getContainer().focus() } }), o.control = function(t) { return new o.Control(t) }, o.Map.include({ addControl: function(t) { return t.addTo(this), this }, removeControl: function(t) { return t.remove(), this }, _initControlPos: function() {
-            function t(t, s) { var r = i + t + " " + i + s;
-                e[t + s] = o.DomUtil.create("div", r, n) } var e = this._controlCorners = {},
-                i = "leaflet-",
-                n = this._controlContainer = o.DomUtil.create("div", i + "control-container", this._container);
-            t("top", "left"), t("top", "right"), t("bottom", "left"), t("bottom", "right") }, _clearControlPos: function() { o.DomUtil.remove(this._controlContainer) } }), o.Control.Zoom = o.Control.extend({ options: { position: "topleft", zoomInText: "+", zoomInTitle: "Zoom in", zoomOutText: "-", zoomOutTitle: "Zoom out" }, onAdd: function(t) { var e = "leaflet-control-zoom",
-                i = o.DomUtil.create("div", e + " leaflet-bar"),
-                n = this.options; return this._zoomInButton = this._createButton(n.zoomInText, n.zoomInTitle, e + "-in", i, this._zoomIn), this._zoomOutButton = this._createButton(n.zoomOutText, n.zoomOutTitle, e + "-out", i, this._zoomOut), this._updateDisabled(), t.on("zoomend zoomlevelschange", this._updateDisabled, this), i }, onRemove: function(t) { t.off("zoomend zoomlevelschange", this._updateDisabled, this) }, disable: function() { return this._disabled = !0, this._updateDisabled(), this }, enable: function() { return this._disabled = !1, this._updateDisabled(), this }, _zoomIn: function(t) { this._disabled || this._map.zoomIn(t.shiftKey ? 3 : 1) }, _zoomOut: function(t) { this._disabled || this._map.zoomOut(t.shiftKey ? 3 : 1) }, _createButton: function(t, e, i, n, s) { var r = o.DomUtil.create("a", i, n); return r.innerHTML = t, r.href = "#", r.title = e, o.DomEvent.on(r, "mousedown dblclick", o.DomEvent.stopPropagation).on(r, "click", o.DomEvent.stop).on(r, "click", s, this).on(r, "click", this._refocusOnMap, this), r }, _updateDisabled: function() { var t = this._map,
-                e = "leaflet-disabled";
-            o.DomUtil.removeClass(this._zoomInButton, e), o.DomUtil.removeClass(this._zoomOutButton, e), (this._disabled || t._zoom === t.getMinZoom()) && o.DomUtil.addClass(this._zoomOutButton, e), (this._disabled || t._zoom === t.getMaxZoom()) && o.DomUtil.addClass(this._zoomInButton, e) } }), o.Map.mergeOptions({ zoomControl: !0 }), o.Map.addInitHook(function() { this.options.zoomControl && (this.zoomControl = new o.Control.Zoom, this.addControl(this.zoomControl)) }), o.control.zoom = function(t) { return new o.Control.Zoom(t) }, o.Control.Attribution = o.Control.extend({ options: { position: "bottomright", prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>' }, initialize: function(t) { o.setOptions(this, t), this._attributions = {} }, onAdd: function(t) { this._container = o.DomUtil.create("div", "leaflet-control-attribution"), o.DomEvent && o.DomEvent.disableClickPropagation(this._container); for (var e in t._layers) t._layers[e].getAttribution && this.addAttribution(t._layers[e].getAttribution()); return this._update(), this._container }, setPrefix: function(t) { return this.options.prefix = t, this._update(), this }, addAttribution: function(t) { return t ? (this._attributions[t] || (this._attributions[t] = 0), this._attributions[t]++, this._update(), this) : this }, removeAttribution: function(t) { return t ? (this._attributions[t] && (this._attributions[t]--, this._update()), this) : this }, _update: function() { if (this._map) { var t = []; for (var e in this._attributions) this._attributions[e] && t.push(e); var i = [];
-                this.options.prefix && i.push(this.options.prefix), t.length && i.push(t.join(", ")), this._container.innerHTML = i.join(" | ") } } }), o.Map.mergeOptions({ attributionControl: !0 }), o.Map.addInitHook(function() { this.options.attributionControl && (this.attributionControl = (new o.Control.Attribution).addTo(this)) }), o.control.attribution = function(t) { return new o.Control.Attribution(t) }, o.Control.Scale = o.Control.extend({ options: { position: "bottomleft", maxWidth: 100, metric: !0, imperial: !0 }, onAdd: function(t) { var e = "leaflet-control-scale",
-                i = o.DomUtil.create("div", e),
-                n = this.options; return this._addScales(n, e + "-line", i), t.on(n.updateWhenIdle ? "moveend" : "move", this._update, this), t.whenReady(this._update, this), i }, onRemove: function(t) { t.off(this.options.updateWhenIdle ? "moveend" : "move", this._update, this) }, _addScales: function(t, e, i) { t.metric && (this._mScale = o.DomUtil.create("div", e, i)), t.imperial && (this._iScale = o.DomUtil.create("div", e, i)) }, _update: function() { var t = this._map,
-                e = t.getSize().y / 2,
-                i = t.distance(t.containerPointToLatLng([0, e]), t.containerPointToLatLng([this.options.maxWidth, e]));
-            this._updateScales(i) }, _updateScales: function(t) { this.options.metric && t && this._updateMetric(t), this.options.imperial && t && this._updateImperial(t) }, _updateMetric: function(t) { var e = this._getRoundNum(t),
-                i = 1e3 > e ? e + " m" : e / 1e3 + " km";
-            this._updateScale(this._mScale, i, e / t) }, _updateImperial: function(t) { var e, i, n, o = 3.2808399 * t;
-            o > 5280 ? (e = o / 5280, i = this._getRoundNum(e), this._updateScale(this._iScale, i + " mi", i / e)) : (n = this._getRoundNum(o), this._updateScale(this._iScale, n + " ft", n / o)) }, _updateScale: function(t, e, i) { t.style.width = Math.round(this.options.maxWidth * i) + "px", t.innerHTML = e }, _getRoundNum: function(t) { var e = Math.pow(10, (Math.floor(t) + "").length - 1),
-                i = t / e; return i = i >= 10 ? 10 : i >= 5 ? 5 : i >= 3 ? 3 : i >= 2 ? 2 : 1, e * i } }), o.control.scale = function(t) { return new o.Control.Scale(t) }, o.Control.Layers = o.Control.extend({ options: { collapsed: !0, position: "topright", autoZIndex: !0, hideSingleBase: !1 }, initialize: function(t, e, i) { o.setOptions(this, i), this._layers = {}, this._lastZIndex = 0, this._handlingClick = !1; for (var n in t) this._addLayer(t[n], n); for (n in e) this._addLayer(e[n], n, !0) }, onAdd: function(t) { return this._initLayout(), this._update(), this._map = t, t.on("zoomend", this._checkDisabledLayers, this), this._container }, onRemove: function() { this._map.off("zoomend", this._checkDisabledLayers, this) }, addBaseLayer: function(t, e) { return this._addLayer(t, e), this._update() }, addOverlay: function(t, e) { return this._addLayer(t, e, !0), this._update() }, removeLayer: function(t) { return t.off("add remove", this._onLayerChange, this), delete this._layers[o.stamp(t)], this._update() }, _initLayout: function() { var t = "leaflet-control-layers",
-                e = this._container = o.DomUtil.create("div", t);
-            e.setAttribute("aria-haspopup", !0), o.DomEvent.disableClickPropagation(e), o.Browser.touch || o.DomEvent.disableScrollPropagation(e); var i = this._form = o.DomUtil.create("form", t + "-list"); if (this.options.collapsed) { o.Browser.android || o.DomEvent.on(e, { mouseenter: this._expand, mouseleave: this._collapse }, this); var n = this._layersLink = o.DomUtil.create("a", t + "-toggle", e);
-                n.href = "#", n.title = "Layers", o.Browser.touch ? o.DomEvent.on(n, "click", o.DomEvent.stop).on(n, "click", this._expand, this) : o.DomEvent.on(n, "focus", this._expand, this), o.DomEvent.on(i, "click", function() { setTimeout(o.bind(this._onInputClick, this), 0) }, this), this._map.on("click", this._collapse, this) } else this._expand();
-            this._baseLayersList = o.DomUtil.create("div", t + "-base", i), this._separator = o.DomUtil.create("div", t + "-separator", i), this._overlaysList = o.DomUtil.create("div", t + "-overlays", i), e.appendChild(i) }, _addLayer: function(t, e, i) { t.on("add remove", this._onLayerChange, this); var n = o.stamp(t);
-            this._layers[n] = { layer: t, name: e, overlay: i }, this.options.autoZIndex && t.setZIndex && (this._lastZIndex++, t.setZIndex(this._lastZIndex)) }, _update: function() { if (!this._container) return this;
-            o.DomUtil.empty(this._baseLayersList), o.DomUtil.empty(this._overlaysList); var t, e, i, n, s = 0; for (i in this._layers) n = this._layers[i], this._addItem(n), e = e || n.overlay, t = t || !n.overlay, s += n.overlay ? 0 : 1; return this.options.hideSingleBase && (t = t && s > 1, this._baseLayersList.style.display = t ? "" : "none"), this._separator.style.display = e && t ? "" : "none", this }, _onLayerChange: function(t) { this._handlingClick || this._update(); var e = this._layers[o.stamp(t.target)],
-                i = e.overlay ? "add" === t.type ? "overlayadd" : "overlayremove" : "add" === t.type ? "baselayerchange" : null;
-            i && this._map.fire(i, e) }, _createRadioElement: function(t, i) { var n = '<input type="radio" class="leaflet-control-layers-selector" name="' + t + '"' + (i ? ' checked="checked"' : "") + "/>",
-                o = e.createElement("div"); return o.innerHTML = n, o.firstChild }, _addItem: function(t) { var i, n = e.createElement("label"),
-                s = this._map.hasLayer(t.layer);
-            t.overlay ? (i = e.createElement("input"), i.type = "checkbox", i.className = "leaflet-control-layers-selector", i.defaultChecked = s) : i = this._createRadioElement("leaflet-base-layers", s), i.layerId = o.stamp(t.layer), o.DomEvent.on(i, "click", this._onInputClick, this); var r = e.createElement("span");
-            r.innerHTML = " " + t.name; var a = e.createElement("div");
-            n.appendChild(a), a.appendChild(i), a.appendChild(r); var h = t.overlay ? this._overlaysList : this._baseLayersList; return h.appendChild(n), this._checkDisabledLayers(), n }, _onInputClick: function() { var t, e, i, n = this._form.getElementsByTagName("input"),
-                o = [],
-                s = [];
-            this._handlingClick = !0; for (var r = n.length - 1; r >= 0; r--) t = n[r], e = this._layers[t.layerId].layer, i = this._map.hasLayer(e), t.checked && !i ? o.push(e) : !t.checked && i && s.push(e); for (r = 0; r < s.length; r++) this._map.removeLayer(s[r]); for (r = 0; r < o.length; r++) this._map.addLayer(o[r]);
-            this._handlingClick = !1, this._refocusOnMap() }, _expand: function() { o.DomUtil.addClass(this._container, "leaflet-control-layers-expanded"), this._form.style.height = null; var t = this._map._size.y - (this._container.offsetTop + 50);
-            t < this._form.clientHeight ? (o.DomUtil.addClass(this._form, "leaflet-control-layers-scrollbar"), this._form.style.height = t + "px") : o.DomUtil.removeClass(this._form, "leaflet-control-layers-scrollbar"), this._checkDisabledLayers() }, _collapse: function() { o.DomUtil.removeClass(this._container, "leaflet-control-layers-expanded") }, _checkDisabledLayers: function() { for (var t, e, n = this._form.getElementsByTagName("input"), o = this._map.getZoom(), s = n.length - 1; s >= 0; s--) t = n[s], e = this._layers[t.layerId].layer, t.disabled = e.options.minZoom !== i && o < e.options.minZoom || e.options.maxZoom !== i && o > e.options.maxZoom } }), o.control.layers = function(t, e, i) { return new o.Control.Layers(t, e, i) }, o.PosAnimation = o.Evented.extend({ run: function(t, e, i, n) { this.stop(), this._el = t, this._inProgress = !0, this._duration = i || .25, this._easeOutPower = 1 / Math.max(n || .5, .2), this._startPos = o.DomUtil.getPosition(t), this._offset = e.subtract(this._startPos), this._startTime = +new Date, this.fire("start"), this._animate() }, stop: function() { this._inProgress && (this._step(!0), this._complete()) }, _animate: function() { this._animId = o.Util.requestAnimFrame(this._animate, this), this._step() }, _step: function(t) { var e = +new Date - this._startTime,
-                i = 1e3 * this._duration;
-            i > e ? this._runFrame(this._easeOut(e / i), t) : (this._runFrame(1), this._complete()) }, _runFrame: function(t, e) { var i = this._startPos.add(this._offset.multiplyBy(t));
-            e && i._round(), o.DomUtil.setPosition(this._el, i), this.fire("step") }, _complete: function() { o.Util.cancelAnimFrame(this._animId), this._inProgress = !1, this.fire("end") }, _easeOut: function(t) { return 1 - Math.pow(1 - t, this._easeOutPower) } }), o.Map.include({ setView: function(t, e, n) { if (e = e === i ? this._zoom : this._limitZoom(e), t = this._limitCenter(o.latLng(t), e, this.options.maxBounds), n = n || {}, this.stop(), this._loaded && !n.reset && n !== !0) { n.animate !== i && (n.zoom = o.extend({ animate: n.animate }, n.zoom), n.pan = o.extend({ animate: n.animate, duration: n.duration }, n.pan)); var s = this._zoom !== e ? this._tryAnimatedZoom && this._tryAnimatedZoom(t, e, n.zoom) : this._tryAnimatedPan(t, n.pan); if (s) return clearTimeout(this._sizeTimer), this } return this._resetView(t, e), this }, panBy: function(t, e) { if (t = o.point(t).round(), e = e || {}, !t.x && !t.y) return this.fire("moveend"); if (e.animate !== !0 && !this.getSize().contains(t)) return this._resetView(this.unproject(this.project(this.getCenter()).add(t)), this.getZoom()), this; if (this._panAnim || (this._panAnim = new o.PosAnimation, this._panAnim.on({ step: this._onPanTransitionStep, end: this._onPanTransitionEnd }, this)), e.noMoveStart || this.fire("movestart"), e.animate !== !1) { o.DomUtil.addClass(this._mapPane, "leaflet-pan-anim"); var i = this._getMapPanePos().subtract(t);
-                this._panAnim.run(this._mapPane, i, e.duration || .25, e.easeLinearity) } else this._rawPanBy(t), this.fire("move").fire("moveend"); return this }, _onPanTransitionStep: function() { this.fire("move") }, _onPanTransitionEnd: function() { o.DomUtil.removeClass(this._mapPane, "leaflet-pan-anim"), this.fire("moveend") }, _tryAnimatedPan: function(t, e) { var i = this._getCenterOffset(t)._floor(); return (e && e.animate) === !0 || this.getSize().contains(i) ? (this.panBy(i, e), !0) : !1 } }), o.Map.mergeOptions({ zoomAnimation: !0, zoomAnimationThreshold: 4 });
-    var h = o.DomUtil.TRANSITION && o.Browser.any3d && !o.Browser.mobileOpera;
-    h && o.Map.addInitHook(function() { this._zoomAnimated = this.options.zoomAnimation, this._zoomAnimated && (this._createAnimProxy(), o.DomEvent.on(this._proxy, o.DomUtil.TRANSITION_END, this._catchTransitionEnd, this)) }), o.Map.include(h ? {
-        _createAnimProxy: function() {
-            var t = this._proxy = o.DomUtil.create("div", "leaflet-proxy leaflet-zoom-animated");
-            this._panes.mapPane.appendChild(t), this.on("zoomanim", function(e) { var i = o.DomUtil.TRANSFORM,
-                    n = t.style[i];
-                o.DomUtil.setTransform(t, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)), n === t.style[i] && this._animatingZoom && this._onZoomTransitionEnd() }, this), this.on("load moveend", function() {
-                var e = this.getCenter(),
-                    i = this.getZoom();
-                o.DomUtil.setTransform(t, this.project(e, i), this.getZoomScale(i, 1))
-            }, this)
-        },
-        _catchTransitionEnd: function(t) { this._animatingZoom && t.propertyName.indexOf("transform") >= 0 && this._onZoomTransitionEnd() },
-        _nothingToAnimate: function() { return !this._container.getElementsByClassName("leaflet-zoom-animated").length },
-        _tryAnimatedZoom: function(t, e, i) { if (this._animatingZoom) return !0; if (i = i || {}, !this._zoomAnimated || i.animate === !1 || this._nothingToAnimate() || Math.abs(e - this._zoom) > this.options.zoomAnimationThreshold) return !1; var n = this.getZoomScale(e),
-                s = this._getCenterOffset(t)._divideBy(1 - 1 / n); return i.animate === !0 || this.getSize().contains(s) ? (o.Util.requestAnimFrame(function() { this._moveStart(!0)._animateZoom(t, e, !0) }, this), !0) : !1 },
-        _animateZoom: function(t, e, i, n) { i && (this._animatingZoom = !0, this._animateToCenter = t, this._animateToZoom = e, o.DomUtil.addClass(this._mapPane, "leaflet-zoom-anim")), this.fire("zoomanim", { center: t, zoom: e, noUpdate: n }), setTimeout(o.bind(this._onZoomTransitionEnd, this), 250) },
-        _onZoomTransitionEnd: function() { this._animatingZoom && (o.DomUtil.removeClass(this._mapPane, "leaflet-zoom-anim"), o.Util.requestAnimFrame(function() { this._animatingZoom = !1, this._move(this._animateToCenter, this._animateToZoom)._moveEnd(!0) }, this)) }
-    } : {}), o.Map.include({ flyTo: function(t, e, n) {
-            function s(t) { var e = (v * v - g * g + (t ? -1 : 1) * L * L * y * y) / (2 * (t ? v : g) * L * y); return Math.log(Math.sqrt(e * e + 1) - e) }
-
-            function r(t) { return (Math.exp(t) - Math.exp(-t)) / 2 }
-
-            function a(t) { return (Math.exp(t) + Math.exp(-t)) / 2 }
-
-            function h(t) { return r(t) / a(t) }
-
-            function l(t) { return g * (a(x) / a(x + P * t)) }
-
-            function u(t) { return g * (a(x) * h(x + P * t) - r(x)) / L }
-
-            function c(t) { return 1 - Math.pow(1 - t, 1.5) }
-
-            function d() { var i = (Date.now() - b) / D,
-                    n = c(i) * w;
-                1 >= i ? (this._flyToFrame = o.Util.requestAnimFrame(d, this), this._move(this.unproject(_.add(m.subtract(_).multiplyBy(u(n) / y)), f), this.getScaleZoom(g / l(n), f), { flyTo: !0 })) : this._move(t, e)._moveEnd(!0) } if (n = n || {}, n.animate === !1 || !o.Browser.any3d) return this.setView(t, e, n);
-            this.stop(); var _ = this.project(this.getCenter()),
-                m = this.project(t),
-                p = this.getSize(),
-                f = this._zoom;
-            t = o.latLng(t), e = e === i ? f : e; var g = Math.max(p.x, p.y),
-                v = g * this.getZoomScale(f, e),
-                y = m.distanceTo(_) || 1,
-                P = 1.42,
-                L = P * P,
-                x = s(0),
-                b = Date.now(),
-                w = (s(1) - x) / P,
-                D = n.duration ? 1e3 * n.duration : 1e3 * w * .8; return this._moveStart(!0), d.call(this), this }, flyToBounds: function(t, e) { var i = this._getBoundsCenterZoom(t, e); return this.flyTo(i.center, i.zoom, e) } }), o.Map.include({ _defaultLocateOptions: { timeout: 1e4, watch: !1 }, locate: function(t) { if (t = this._locateOptions = o.extend({}, this._defaultLocateOptions, t), !("geolocation" in navigator)) return this._handleGeolocationError({ code: 0, message: "Geolocation not supported." }), this; var e = o.bind(this._handleGeolocationResponse, this),
-                i = o.bind(this._handleGeolocationError, this); return t.watch ? this._locationWatchId = navigator.geolocation.watchPosition(e, i, t) : navigator.geolocation.getCurrentPosition(e, i, t), this }, stopLocate: function() { return navigator.geolocation && navigator.geolocation.clearWatch && navigator.geolocation.clearWatch(this._locationWatchId), this._locateOptions && (this._locateOptions.setView = !1), this }, _handleGeolocationError: function(t) { var e = t.code,
-                i = t.message || (1 === e ? "permission denied" : 2 === e ? "position unavailable" : "timeout");
-            this._locateOptions.setView && !this._loaded && this.fitWorld(), this.fire("locationerror", { code: e, message: "Geolocation error: " + i + "." }) }, _handleGeolocationResponse: function(t) { var e = t.coords.latitude,
-                i = t.coords.longitude,
-                n = new o.LatLng(e, i),
-                s = n.toBounds(t.coords.accuracy),
-                r = this._locateOptions; if (r.setView) { var a = this.getBoundsZoom(s);
-                this.setView(n, r.maxZoom ? Math.min(a, r.maxZoom) : a) } var h = { latlng: n, bounds: s, timestamp: t.timestamp }; for (var l in t.coords) "number" == typeof t.coords[l] && (h[l] = t.coords[l]);
-            this.fire("locationfound", h) } })
-}(window, document);
diff --git a/erpnext/public/js/stock_analytics.js b/erpnext/public/js/stock_analytics.js
index a343c34..98e7f78 100644
--- a/erpnext/public/js/stock_analytics.js
+++ b/erpnext/public/js/stock_analytics.js
@@ -125,6 +125,7 @@
 		this.serialized_buying_rates = this.get_serialized_buying_rates();
 
 		for(var i=0, j=data.length; i<j; i++) {
+			let diff = 0;
 			var sl = data[i];
 			sl.posting_datetime = sl.posting_date + " " + sl.posting_time;
 			var posting_datetime = frappe.datetime.str_to_obj(sl.posting_datetime);
@@ -140,18 +141,18 @@
 					var is_fifo = valuation_method == "FIFO";
 
 					if(sl.voucher_type=="Stock Reconciliation") {
-						var diff = (sl.qty_after_transaction * sl.valuation_rate) - item.closing_qty_value;
+						diff = (sl.qty_after_transaction * sl.valuation_rate) - item.closing_qty_value;
 						wh.fifo_stack = [[sl.qty_after_transaction, sl.valuation_rate, sl.posting_date]];
 						wh.balance_qty = sl.qty_after_transaction;
 						wh.balance_value = sl.valuation_rate * sl.qty_after_transaction;
 					} else {
-						var diff = me.get_value_diff(wh, sl, is_fifo);
+						diff = me.get_value_diff(wh, sl, is_fifo);
 					}
 				} else {
 					if(sl.voucher_type=="Stock Reconciliation") {
-						var diff = sl.qty_after_transaction - item.closing_qty_value;
+						diff = sl.qty_after_transaction - item.closing_qty_value;
 					} else {
-						var diff = sl.qty;
+						diff = sl.qty;
 					}
 				}
 
diff --git a/erpnext/public/js/stock_grid_report.js b/erpnext/public/js/stock_grid_report.js
index 752fafd..c7867d0 100644
--- a/erpnext/public/js/stock_grid_report.js
+++ b/erpnext/public/js/stock_grid_report.js
@@ -12,10 +12,12 @@
 
 	get_value_diff(wh, sl, is_fifo) {
 		// value
+		let value_diff = 0;
+
 		if(sl.qty > 0) {
 			// incoming - rate is given
-			var rate = sl.incoming_rate;
-			var add_qty = sl.qty;
+			let rate = sl.incoming_rate;
+			let add_qty = sl.qty;
 			if(wh.balance_qty < 0) {
 				// negative valuation
 				// only add value of quantity if
@@ -25,10 +27,11 @@
 					add_qty = 0;
 				}
 			}
+
 			if(sl.serial_no) {
-				var value_diff = this.get_serialized_value_diff(sl);
+				value_diff = this.get_serialized_value_diff(sl);
 			} else {
-				var value_diff = (rate * add_qty);
+				value_diff = (rate * add_qty);
 			}
 
 			if(add_qty)
@@ -39,19 +42,19 @@
 
 			// outgoing
 			if(sl.serial_no) {
-				var value_diff = -1 * this.get_serialized_value_diff(sl);
+				value_diff = -1 * this.get_serialized_value_diff(sl);
 			} else if(is_fifo) {
-				var value_diff = fifo_value_diff;
+				value_diff = fifo_value_diff;
 			} else {
 				// average rate for weighted average
-				var rate = (wh.balance_qty.toFixed(2) == 0.00 ? 0 :
+				let rate = (wh.balance_qty.toFixed(2) == 0.00 ? 0 :
 					flt(wh.balance_value) / flt(wh.balance_qty));
 
 				// no change in value if negative qty
 				if((wh.balance_qty + sl.qty).toFixed(2) >= 0.00)
-					var value_diff = (rate * sl.qty);
+					value_diff = (rate * sl.qty);
 				else
-					var value_diff = -wh.balance_value;
+					value_diff = -wh.balance_value;
 			}
 		}
 
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 497f8d2..06e39aa 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -401,6 +401,10 @@
 	},
 
 	get_fiscal_year: function(date) {
+		if(!date) {
+			date = frappe.datetime.get_today();
+		}
+
 		let fiscal_year = '';
 		frappe.call({
 			method: "erpnext.accounts.utils.get_fiscal_year",
@@ -671,7 +675,7 @@
 		})
 	}
 
-	new frappe.ui.Dialog({
+	let dialog = new frappe.ui.Dialog({
 		title: __("Update Items"),
 		size: "extra-large",
 		fields: [
@@ -708,7 +712,9 @@
 			refresh_field("items");
 		},
 		primary_action_label: __('Update')
-	}).show();
+	})
+
+	dialog.show();
 }
 
 erpnext.utils.map_current_doc = function(opts) {
diff --git a/erpnext/public/js/utils/landed_taxes_and_charges_common.js b/erpnext/public/js/utils/landed_taxes_and_charges_common.js
new file mode 100644
index 0000000..c71f77d
--- /dev/null
+++ b/erpnext/public/js/utils/landed_taxes_and_charges_common.js
@@ -0,0 +1,62 @@
+
+erpnext.landed_cost_taxes_and_charges = {
+	setup_triggers: function(doctype) {
+		frappe.ui.form.on(doctype, {
+			refresh: function(frm) {
+				let tax_field = frm.doc.doctype == 'Landed Cost Voucher' ? 'taxes' : 'additional_costs';
+				frm.set_query("expense_account", tax_field, function() {
+					return {
+						filters: {
+							"account_type": ['in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]],
+							"company": frm.doc.company
+						}
+					};
+				});
+			},
+
+			set_account_currency: function(frm, cdt, cdn) {
+				let row = locals[cdt][cdn];
+				if (row.expense_account) {
+					frappe.db.get_value('Account', row.expense_account, 'account_currency', function(value) {
+						frappe.model.set_value(cdt, cdn, "account_currency", value.account_currency);
+						frm.events.set_exchange_rate(frm, cdt, cdn);
+					});
+				}
+			},
+
+			set_exchange_rate: function(frm, cdt, cdn) {
+				let row = locals[cdt][cdn];
+				let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+				if (row.account_currency == company_currency) {
+					row.exchange_rate = 1;
+					frm.set_df_property('taxes', 'hidden', 1, row.name, 'exchange_rate');
+				} else if (!row.exchange_rate || row.exchange_rate == 1) {
+					frm.set_df_property('taxes', 'hidden', 0, row.name, 'exchange_rate');
+					frappe.call({
+						method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
+						args: {
+							posting_date: frm.doc.posting_date,
+							account: row.expense_account,
+							account_currency: row.account_currency,
+							company: frm.doc.company
+						},
+						callback: function(r) {
+							if (r.message) {
+								frappe.model.set_value(cdt, cdn, "exchange_rate", r.message);
+							}
+						}
+					});
+				}
+
+				frm.refresh_field('taxes');
+			},
+
+			set_base_amount: function(frm, cdt, cdn) {
+				let row = locals[cdt][cdn];
+				frappe.model.set_value(cdt, cdn, "base_amount",
+					flt(flt(row.amount)*row.exchange_rate, precision("base_amount", row)));
+			}
+		});
+	}
+}
diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js
new file mode 100644
index 0000000..517d871
--- /dev/null
+++ b/erpnext/public/js/utils/sales_common.js
@@ -0,0 +1,435 @@
+// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+frappe.provide("erpnext.selling");
+
+erpnext.sales_common = {
+	setup_selling_controller:function() {
+		erpnext.selling.SellingController = class SellingController extends erpnext.TransactionController {
+			setup() {
+				super.setup();
+				this.frm.email_field = "contact_email";
+			}
+
+			onload() {
+				super.onload();
+				this.setup_queries();
+				this.frm.set_query('shipping_rule', function() {
+					return {
+						filters: {
+							"shipping_rule_type": "Selling"
+						}
+					};
+				});
+			}
+
+			setup_queries() {
+				var me = this;
+
+				$.each([["customer", "customer"],
+					["lead", "lead"]],
+					function(i, opts) {
+						if(me.frm.fields_dict[opts[0]])
+							me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
+					});
+
+				me.frm.set_query('contact_person', erpnext.queries.contact_query);
+				me.frm.set_query('customer_address', erpnext.queries.address_query);
+				me.frm.set_query('shipping_address_name', erpnext.queries.address_query);
+				me.frm.set_query('dispatch_address_name', erpnext.queries.dispatch_address_query);
+
+				erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype);
+
+				if(this.frm.fields_dict.selling_price_list) {
+					this.frm.set_query("selling_price_list", function() {
+						return { filters: { selling: 1 } };
+					});
+				}
+
+				if(this.frm.fields_dict.tc_name) {
+					this.frm.set_query("tc_name", function() {
+						return { filters: { selling: 1 } };
+					});
+				}
+
+				if(!this.frm.fields_dict["items"]) {
+					return;
+				}
+
+				if(this.frm.fields_dict["items"].grid.get_field('item_code')) {
+					this.frm.set_query("item_code", "items", function() {
+						return {
+							query: "erpnext.controllers.queries.item_query",
+							filters: {'is_sales_item': 1, 'customer': me.frm.doc.customer, 'has_variants': 0}
+						}
+					});
+				}
+
+				if(this.frm.fields_dict["packed_items"] &&
+					this.frm.fields_dict["packed_items"].grid.get_field('batch_no')) {
+					this.frm.set_query("batch_no", "packed_items", function(doc, cdt, cdn) {
+						return me.set_query_for_batch(doc, cdt, cdn)
+					});
+				}
+
+				if(this.frm.fields_dict["items"].grid.get_field('item_code')) {
+					this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) {
+						return me.set_query_for_item_tax_template(doc, cdt, cdn)
+					});
+				}
+
+			}
+
+			refresh() {
+				super.refresh();
+
+				frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
+
+				this.frm.toggle_display("customer_name",
+					(this.frm.doc.customer_name && this.frm.doc.customer_name!==this.frm.doc.customer));
+
+				this.toggle_editable_price_list_rate();
+			}
+
+			customer() {
+				var me = this;
+				erpnext.utils.get_party_details(this.frm, null, null, function() {
+					me.apply_price_list();
+				});
+			}
+
+			customer_address() {
+				erpnext.utils.get_address_display(this.frm, "customer_address");
+				erpnext.utils.set_taxes_from_address(this.frm, "customer_address", "customer_address", "shipping_address_name");
+			}
+
+			shipping_address_name() {
+				erpnext.utils.get_address_display(this.frm, "shipping_address_name", "shipping_address");
+				erpnext.utils.set_taxes_from_address(this.frm, "shipping_address_name", "customer_address", "shipping_address_name");
+			}
+
+			dispatch_address_name() {
+				erpnext.utils.get_address_display(this.frm, "dispatch_address_name", "dispatch_address");
+			}
+
+			sales_partner() {
+				this.apply_pricing_rule();
+			}
+
+			campaign() {
+				this.apply_pricing_rule();
+			}
+
+			selling_price_list() {
+				this.apply_price_list();
+				this.set_dynamic_labels();
+			}
+
+			discount_percentage(doc, cdt, cdn) {
+				var item = frappe.get_doc(cdt, cdn);
+				item.discount_amount = 0.0;
+				this.apply_discount_on_item(doc, cdt, cdn, 'discount_percentage');
+			}
+
+			discount_amount(doc, cdt, cdn) {
+
+				if(doc.name === cdn) {
+					return;
+				}
+
+				var item = frappe.get_doc(cdt, cdn);
+				item.discount_percentage = 0.0;
+				this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount');
+			}
+
+			apply_discount_on_item(doc, cdt, cdn, field) {
+				var item = frappe.get_doc(cdt, cdn);
+				if(!item.price_list_rate) {
+					item[field] = 0.0;
+				} else {
+					this.price_list_rate(doc, cdt, cdn);
+				}
+				this.set_gross_profit(item);
+			}
+
+			commission_rate() {
+				this.calculate_commission();
+			}
+
+			total_commission() {
+				frappe.model.round_floats_in(this.frm.doc, ["amount_eligible_for_commission", "total_commission"]);
+
+				const { amount_eligible_for_commission } = this.frm.doc;
+				if(!amount_eligible_for_commission) return;
+
+				this.frm.set_value(
+					"commission_rate", flt(
+						this.frm.doc.total_commission * 100.0 / amount_eligible_for_commission
+					)
+				);
+			}
+
+			allocated_percentage(doc, cdt, cdn) {
+				var sales_person = frappe.get_doc(cdt, cdn);
+				if(sales_person.allocated_percentage) {
+
+					sales_person.allocated_percentage = flt(sales_person.allocated_percentage,
+						precision("allocated_percentage", sales_person));
+
+					sales_person.allocated_amount = flt(this.frm.doc.amount_eligible_for_commission *
+						sales_person.allocated_percentage / 100.0,
+						precision("allocated_amount", sales_person));
+						refresh_field(["allocated_amount"], sales_person);
+
+					this.calculate_incentive(sales_person);
+					refresh_field(["allocated_percentage", "allocated_amount", "commission_rate","incentives"], sales_person.name,
+						sales_person.parentfield);
+				}
+			}
+
+			sales_person(doc, cdt, cdn) {
+				var row = frappe.get_doc(cdt, cdn);
+				this.calculate_incentive(row);
+				refresh_field("incentives",row.name,row.parentfield);
+			}
+
+			toggle_editable_price_list_rate() {
+				var df = frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "price_list_rate", this.frm.doc.name);
+				var editable_price_list_rate = cint(frappe.defaults.get_default("editable_price_list_rate"));
+
+				if(df && editable_price_list_rate) {
+					const parent_field = frappe.meta.get_parentfield(this.frm.doc.doctype, this.frm.doc.doctype + " Item");
+					if (!this.frm.fields_dict[parent_field]) return;
+
+					this.frm.fields_dict[parent_field].grid.update_docfield_property(
+						'price_list_rate', 'read_only', 0
+					);
+				}
+			}
+
+			calculate_commission() {
+				if(!this.frm.fields_dict.commission_rate || this.frm.doc.docstatus === 1) return;
+
+				if(this.frm.doc.commission_rate > 100) {
+					this.frm.set_value("commission_rate", 100);
+					frappe.throw(`${__(frappe.meta.get_label(
+						this.frm.doc.doctype, "commission_rate", this.frm.doc.name
+					))} ${__("cannot be greater than 100")}`);
+				}
+
+				this.frm.doc.amount_eligible_for_commission = this.frm.doc.items.reduce(
+					(sum, item) => item.grant_commission ? sum + item.base_net_amount : sum, 0
+				)
+
+				this.frm.doc.total_commission = flt(
+					this.frm.doc.amount_eligible_for_commission * this.frm.doc.commission_rate / 100.0,
+					precision("total_commission")
+				);
+
+				refresh_field(["amount_eligible_for_commission", "total_commission"]);
+			}
+
+			calculate_contribution() {
+				var me = this;
+				$.each(this.frm.doc.doctype.sales_team || [], function(i, sales_person) {
+					frappe.model.round_floats_in(sales_person);
+					if (!sales_person.allocated_percentage) return;
+
+					sales_person.allocated_amount = flt(
+						me.frm.doc.amount_eligible_for_commission
+						* sales_person.allocated_percentage
+						/ 100.0,
+						precision("allocated_amount", sales_person)
+					);
+				});
+			}
+
+			calculate_incentive(row) {
+				if(row.allocated_amount)
+				{
+					row.incentives = flt(
+							row.allocated_amount * row.commission_rate / 100.0,
+							precision("incentives", row));
+				}
+			}
+
+			set_dynamic_labels() {
+				super.set_dynamic_labels();
+				this.set_product_bundle_help(this.frm.doc);
+			}
+
+			set_product_bundle_help(doc) {
+				if(!this.frm.fields_dict.packing_list) return;
+				if ((doc.packed_items || []).length) {
+					$(this.frm.fields_dict.packing_list.row.wrapper).toggle(true);
+
+					if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
+						var help_msg = "<div class='alert alert-warning'>" +
+							__("For 'Product Bundle' items, Warehouse, Serial No and Batch No will be considered from the 'Packing List' table. If Warehouse and Batch No are same for all packing items for any 'Product Bundle' item, those values can be entered in the main Item table, values will be copied to 'Packing List' table.")+
+						"</div>";
+						frappe.meta.get_docfield(doc.doctype, 'product_bundle_help', doc.name).options = help_msg;
+					}
+				} else {
+					$(this.frm.fields_dict.packing_list.row.wrapper).toggle(false);
+					if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
+						frappe.meta.get_docfield(doc.doctype, 'product_bundle_help', doc.name).options = '';
+					}
+				}
+				refresh_field('product_bundle_help');
+			}
+
+			company_address() {
+				var me = this;
+				if(this.frm.doc.company_address) {
+					frappe.call({
+						method: "frappe.contacts.doctype.address.address.get_address_display",
+						args: {"address_dict": this.frm.doc.company_address },
+						callback: function(r) {
+							if(r.message) {
+								me.frm.set_value("company_address_display", r.message)
+							}
+						}
+					})
+				} else {
+					this.frm.set_value("company_address_display", "");
+				}
+			}
+
+			conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate) {
+				super.conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate);
+			}
+
+			qty(doc, cdt, cdn) {
+				super.qty(doc, cdt, cdn);
+			}
+
+			pick_serial_and_batch(doc, cdt, cdn) {
+				let item = locals[cdt][cdn];
+				let me = this;
+				let path = "assets/erpnext/js/utils/serial_no_batch_selector.js";
+
+				frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
+					.then((r) => {
+						if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) {
+							item.has_serial_no = r.message.has_serial_no;
+							item.has_batch_no = r.message.has_batch_no;
+							item.type_of_transaction = item.qty > 0 ? "Outward":"Inward";
+
+							item.title = item.has_serial_no ?
+								__("Select Serial No") : __("Select Batch No");
+
+							if (item.has_serial_no && item.has_batch_no) {
+								item.title = __("Select Serial and Batch");
+							}
+
+							frappe.require(path, function() {
+								new erpnext.SerialBatchPackageSelector(
+									me.frm, item, (r) => {
+										if (r) {
+											frappe.model.set_value(item.doctype, item.name, {
+												"serial_and_batch_bundle": r.name,
+												"qty": Math.abs(r.total_qty)
+											});
+										}
+									}
+								);
+							});
+						}
+					});
+			}
+
+			update_auto_repeat_reference(doc) {
+				if (doc.auto_repeat) {
+					frappe.call({
+						method:"frappe.automation.doctype.auto_repeat.auto_repeat.update_reference",
+						args:{
+							docname: doc.auto_repeat,
+							reference:doc.name
+						},
+						callback: function(r){
+							if (r.message=="success") {
+								frappe.show_alert({message:__("Auto repeat document updated"), indicator:'green'});
+							} else {
+								frappe.show_alert({message:__("An error occurred during the update process"), indicator:'red'});
+							}
+						}
+					})
+				}
+			}
+
+			project() {
+				let me = this;
+				if(in_list(["Delivery Note", "Sales Invoice", "Sales Order"], this.frm.doc.doctype)) {
+					if(this.frm.doc.project) {
+						frappe.call({
+							method:'erpnext.projects.doctype.project.project.get_cost_center_name' ,
+							args: {project: this.frm.doc.project},
+							callback: function(r, rt) {
+								if(!r.exc) {
+									$.each(me.frm.doc["items"] || [], function(i, row) {
+										if(r.message) {
+											frappe.model.set_value(row.doctype, row.name, "cost_center", r.message);
+											frappe.msgprint(__("Cost Center For Item with Item Code {0} has been Changed to {1}", [row.item_name, r.message]));
+										}
+									})
+								}
+							}
+						})
+					}
+				}
+			}
+		};
+	}
+}
+
+erpnext.pre_sales = {
+	set_as_lost: function(doctype) {
+		frappe.ui.form.on(doctype, {
+			set_as_lost_dialog: function(frm) {
+				var dialog = new frappe.ui.Dialog({
+					title: __("Set as Lost"),
+					fields: [
+						{
+							"fieldtype": "Table MultiSelect",
+							"label": __("Lost Reasons"),
+							"fieldname": "lost_reason",
+							"options": frm.doctype === 'Opportunity' ? 'Opportunity Lost Reason Detail': 'Quotation Lost Reason Detail',
+							"reqd": 1
+						},
+						{
+							"fieldtype": "Table MultiSelect",
+							"label": __("Competitors"),
+							"fieldname": "competitors",
+							"options": "Competitor Detail"
+						},
+						{
+							"fieldtype": "Small Text",
+							"label": __("Detailed Reason"),
+							"fieldname": "detailed_reason"
+						},
+					],
+					primary_action: function() {
+						let values = dialog.get_values();
+
+						frm.call({
+							doc: frm.doc,
+							method: 'declare_enquiry_lost',
+							args: {
+								'lost_reasons_list': values.lost_reason,
+								'competitors': values.competitors ? values.competitors : [],
+								'detailed_reason': values.detailed_reason
+							},
+							callback: function(r) {
+								dialog.hide();
+								frm.reload_doc();
+							},
+						});
+					},
+					primary_action_label: __('Declare Lost')
+				});
+
+				dialog.show();
+			}
+		});
+	}
+}
\ No newline at end of file
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index 27a7968..9267801 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -382,7 +382,7 @@
 	edit_full_form() {
 		let bundle_id = this.item.serial_and_batch_bundle
 		if (!bundle_id) {
-			_new = frappe.model.get_new_doc(
+			let _new = frappe.model.get_new_doc(
 				"Serial and Batch Bundle", null, null, true
 			);
 
diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss
index 7b7530b..c9d001c 100644
--- a/erpnext/public/scss/point-of-sale.scss
+++ b/erpnext/public/scss/point-of-sale.scss
@@ -34,7 +34,7 @@
 	}
 
 	.abbr {
-		background-color: var(--gray-50);
+		background-color: var(--control-bg);
 		font-size: var(--text-3xl);
 	}
 
@@ -72,7 +72,7 @@
 	.highlighted-numpad-btn {
 		box-shadow: inset 0 0px 4px 0px rgba(0, 0, 0, 0.15) !important;
 		font-weight: 700;
-		background-color: var(--gray-50);
+		background-color: var(--control-bg);
 	}
 
 	> .items-selector {
@@ -152,7 +152,6 @@
 					margin-bottom: 0px;
 					min-height: 8rem;
 					height: 8rem;
-					color: var(--gray-500);
 
 					> img {
 						@extend .image;
@@ -242,7 +241,7 @@
 						width: 3rem;
 						height: 3rem;
 						border-radius: 50%;
-						color: var(--gray-500);
+						color: var(--text-light);
 						margin-right: var(--margin-md);
 
 						> img {
@@ -268,7 +267,6 @@
 						}
 
 						>.customer-desc {
-							color: var(--gray-600);
 							font-weight: 500;
 							font-size: var(--text-sm);
 						}
@@ -363,7 +361,7 @@
 					display: flex;
 					align-items: center;
 					justify-content: center;
-					background-color: var(--gray-50);
+					background-color: var(--control-bg);
 					border-radius: var(--border-radius-md);
 					font-size: var(--text-md);
 					font-weight: 500;
@@ -385,7 +383,7 @@
 						border-radius: var(--border-radius-md);
 
 						&:hover {
-							background-color: var(--gray-50);
+							background-color: var(--control-bg);
 						}
 
 						> .item-image {
@@ -395,7 +393,7 @@
 							width: 2rem;
 							height: 2rem;
 							border-radius: var(--border-radius-md);
-							color: var(--gray-500);
+							color: var(--text-light);
 							margin-right: var(--margin-md);
 
 							> img {
@@ -537,13 +535,13 @@
 					> .edit-cart-btn {
 						@extend .primary-action;
 						display: none;
-						background-color: var(--gray-300);
+						background-color: var(--control-bg);
 						font-weight: 500;
 						transition: all 0.15s ease-in-out;
 
 						&:hover {
-							background-color: var(--gray-600);
-							color: white;
+							background-color: var(--control-bg);
+							color: var(--text-light);
 							font-weight: 700;
 						}
 					}
@@ -832,13 +830,13 @@
 						> .shortcut {
 							@extend .pointer-no-select;
 							border-radius: var(--border-radius-sm);
-							background-color: var(--gray-100);
+							background-color: var(--control-bg);
 							font-weight: 500;
 							padding: var(--padding-xs) var(--padding-sm);
 							transition: all 0.15s ease-in-out;
 
 							&:hover {
-								background-color: var(--gray-300);
+								background-color: var(--control-bg);
 							}
 						}
 					}
@@ -912,7 +910,7 @@
 
 			> .totals {
 				display: flex;
-				background-color: var(--gray-100);
+				background-color: var(--control-bg);
 				justify-content: center;
 				padding: var(--padding-md);
 				border-radius: var(--border-radius-md);
@@ -924,7 +922,6 @@
 					> .total-label {
 						font-size: var(--text-md);
 						font-weight: 500;
-						color: var(--gray-600);
 					}
 
 					> .value {
@@ -1036,7 +1033,6 @@
 						> .customer-email {
 							font-size: var(--text-md);
 							font-weight: 500;
-							color: var(--gray-600);
 						}
 
 						> .cashier {
@@ -1071,7 +1067,7 @@
 					display: flex;
 					flex-direction: column;
 					border-radius: var(--border-radius-md);
-					background-color: var(--gray-50);
+					background-color: var(--control-bg);
 					margin: var(--margin-md) 0px;
 
 					> .summary-row-wrapper {
diff --git a/erpnext/regional/italy/sales_invoice.js b/erpnext/regional/italy/sales_invoice.js
deleted file mode 100644
index b54ac53..0000000
--- a/erpnext/regional/italy/sales_invoice.js
+++ /dev/null
@@ -1,25 +0,0 @@
-erpnext.setup_e_invoice_button = (doctype) => {
-	frappe.ui.form.on(doctype, {
-		refresh: (frm) => {
-			if(frm.doc.docstatus == 1) {
-				frm.add_custom_button('Generate E-Invoice', () => {
-					frm.call({
-						method: "erpnext.regional.italy.utils.generate_single_invoice",
-						args: {
-							docname: frm.doc.name
-						},
-						callback: function(r) {
-							frm.reload_doc();
-							if(r.message) {
-								open_url_post(frappe.request.url, {
-									cmd: 'frappe.core.doctype.file.file.download_file',
-									file_url: r.message
-								});
-							}
-						}
-					});
-				});
-			}
-		}
-	});
-};
diff --git a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js
index d7e3ac9..4fc1be1 100644
--- a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js
+++ b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Electronic Invoice Register"] = {
 	"filters": [
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.js b/erpnext/regional/report/uae_vat_201/uae_vat_201.js
index 5957424..eaefc04 100644
--- a/erpnext/regional/report/uae_vat_201/uae_vat_201.js
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["UAE VAT 201"] = {
 	"filters": [
diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.js b/erpnext/regional/report/vat_audit_report/vat_audit_report.js
index 39ef9b5..41318f3 100644
--- a/erpnext/regional/report/vat_audit_report/vat_audit_report.js
+++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["VAT Audit Report"] = {
 	"filters": [
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/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js
index 67c392c..ea55cb2 100644
--- a/erpnext/selling/doctype/quotation/quotation.js
+++ b/erpnext/selling/doctype/quotation/quotation.js
@@ -1,8 +1,10 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-
-{% include 'erpnext/selling/sales_common.js' %}
+erpnext.accounts.taxes.setup_tax_validations("Sales Taxes and Charges Template");
+erpnext.accounts.taxes.setup_tax_filters("Sales Taxes and Charges");
+erpnext.pre_sales.set_as_lost("Quotation");
+erpnext.sales_common.setup_selling_controller();
 
 frappe.ui.form.on('Quotation', {
 	setup: function(frm) {
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 5d43a07..b57a094 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -1,7 +1,9 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-{% include 'erpnext/selling/sales_common.js' %}
+erpnext.accounts.taxes.setup_tax_filters("Sales Taxes and Charges");
+erpnext.accounts.taxes.setup_tax_validations("Sales Order");
+erpnext.sales_common.setup_selling_controller();
 
 frappe.ui.form.on("Sales Order", {
 	setup: function(frm) {
@@ -814,7 +816,6 @@
 				var method = args.against_default_supplier ? "make_purchase_order_for_default_supplier" : "make_purchase_order"
 				return frappe.call({
 					method: "erpnext.selling.doctype.sales_order.sales_order." + method,
-					freeze: true,
 					freeze_message: __("Creating Purchase Order ..."),
 					args: {
 						"source_name": me.frm.doc.name,
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 016ebf0..8b37e40 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -579,7 +579,7 @@
 			console.log(error);
 		} finally {
 			frappe.dom.unfreeze();
-			return item_row;
+			return item_row; // eslint-disable-line no-unsafe-finally
 		}
 	}
 
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 12cc629..46490c4 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -945,7 +945,7 @@
 					`<div class="no-transactions-placeholder">No recent transactions found</div>`
 				)
 				return;
-			};
+			}
 
 			const elapsed_time = moment(res[0].posting_date+" "+res[0].posting_time).fromNow();
 			this.$customer_section.find('.customer-desc').html(`Last transacted ${elapsed_time}`);
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..f81d1c1 100644
--- a/erpnext/selling/report/address_and_contacts/address_and_contacts.js
+++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Address And Contacts"] = {
 	"filters": [
@@ -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/selling/report/customer_wise_item_price/customer_wise_item_price.js b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.js
index d333c8b..2aac343 100644
--- a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.js
+++ b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Customer-wise Item Price"] = {
 	"filters": [
diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js
index 073be78..f63d02e 100644
--- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js
+++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Item-wise Sales History"] = {
 	"filters": [
diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js
index 990d736..0203a05 100644
--- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js
+++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 function get_filters() {
 	let filters = [
@@ -116,7 +116,7 @@
 	"filters": get_filters(),
 	"formatter": function(value, row, column, data, default_formatter){
 		if(column.fieldname == 'invoices' && value) {
-			invoices = value.split(',');
+			let invoices = value.split(',');
 			const invoice_formatter = (prev_value, curr_value) => {
 				if(prev_value != "") {
 					return prev_value + ", " + default_formatter(curr_value, row, column, data);
@@ -128,7 +128,7 @@
 			return invoices.reduce(invoice_formatter, "")
 		}
 		else if (column.fieldname == 'paid_amount' && value){
-			formatted_value = default_formatter(value, row, column, data);
+			let formatted_value = default_formatter(value, row, column, data);
 			if(value > 0) {
 				formatted_value = "<span style='color:green;'>" + formatted_value + "</span>"
 			}
diff --git a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.js b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.js
index 37634ef..2042059 100644
--- a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.js
+++ b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Pending SO Items For Purchase Request"] = {
 }
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js
index 87dd02f..ace16f4 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.js
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Sales Analytics"] = {
 	"filters": [
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
index f3f931e..ac3d3db 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Sales Order Analysis"] = {
 	"filters": [
diff --git a/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.js b/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.js
index 63d930c..f08780a 100644
--- a/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.js
+++ b/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Sales Partner Commission Summary"] = {
 	"filters": [
diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js
index adae47b..83934fb 100644
--- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js
+++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Sales Partner Target Variance based on Item Group"] = {
 	"filters": [
diff --git a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js
index e404233..e443ab3 100644
--- a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js
+++ b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Sales Partner Transaction Summary"] = {
 	"filters": [
diff --git a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js
index ba6ee78..306ef6f 100644
--- a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js
+++ b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Sales Person Commission Summary"] = {
 	"filters": [
diff --git a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js
index 2b84436..9414ad6 100644
--- a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js
+++ b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Sales Person Target Variance Based On Item Group"] = {
 	"filters": [
diff --git a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js
index 9f3d255..12bb49f 100644
--- a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js
+++ b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Territory Target Variance Based On Item Group"] = {
 	"filters": [
diff --git a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.js b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.js
index bef800f..c755a75 100644
--- a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.js
+++ b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 
 frappe.query_reports["Territory-wise Sales"] = {
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
deleted file mode 100644
index 87c0fae..0000000
--- a/erpnext/selling/sales_common.js
+++ /dev/null
@@ -1,431 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// License: GNU General Public License v3. See license.txt
-
-
-cur_frm.cscript.tax_table = "Sales Taxes and Charges";
-{% include 'erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js' %}
-
-
-cur_frm.email_field = "contact_email";
-
-frappe.provide("erpnext.selling");
-erpnext.selling.SellingController = class SellingController extends erpnext.TransactionController {
-	setup() {
-		super.setup();
-	}
-
-	onload() {
-		super.onload();
-		this.setup_queries();
-		this.frm.set_query('shipping_rule', function() {
-			return {
-				filters: {
-					"shipping_rule_type": "Selling"
-				}
-			};
-		});
-	}
-
-	setup_queries() {
-		var me = this;
-
-		$.each([["customer", "customer"],
-			["lead", "lead"]],
-			function(i, opts) {
-				if(me.frm.fields_dict[opts[0]])
-					me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
-			});
-
-		me.frm.set_query('contact_person', erpnext.queries.contact_query);
-		me.frm.set_query('customer_address', erpnext.queries.address_query);
-		me.frm.set_query('shipping_address_name', erpnext.queries.address_query);
-		me.frm.set_query('dispatch_address_name', erpnext.queries.dispatch_address_query);
-
-		erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype);
-
-		if(this.frm.fields_dict.selling_price_list) {
-			this.frm.set_query("selling_price_list", function() {
-				return { filters: { selling: 1 } };
-			});
-		}
-
-		if(this.frm.fields_dict.tc_name) {
-			this.frm.set_query("tc_name", function() {
-				return { filters: { selling: 1 } };
-			});
-		}
-
-		if(!this.frm.fields_dict["items"]) {
-			return;
-		}
-
-		if(this.frm.fields_dict["items"].grid.get_field('item_code')) {
-			this.frm.set_query("item_code", "items", function() {
-				return {
-					query: "erpnext.controllers.queries.item_query",
-					filters: {'is_sales_item': 1, 'customer': cur_frm.doc.customer, 'has_variants': 0}
-				}
-			});
-		}
-
-		if(this.frm.fields_dict["packed_items"] &&
-			this.frm.fields_dict["packed_items"].grid.get_field('batch_no')) {
-			this.frm.set_query("batch_no", "packed_items", function(doc, cdt, cdn) {
-				return me.set_query_for_batch(doc, cdt, cdn)
-			});
-		}
-
-		if(this.frm.fields_dict["items"].grid.get_field('item_code')) {
-			this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) {
-				return me.set_query_for_item_tax_template(doc, cdt, cdn)
-			});
-		}
-
-	}
-
-	refresh() {
-		super.refresh();
-
-		frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
-
-		this.frm.toggle_display("customer_name",
-			(this.frm.doc.customer_name && this.frm.doc.customer_name!==this.frm.doc.customer));
-
-		this.toggle_editable_price_list_rate();
-	}
-
-	customer() {
-		var me = this;
-		erpnext.utils.get_party_details(this.frm, null, null, function() {
-			me.apply_price_list();
-		});
-	}
-
-	customer_address() {
-		erpnext.utils.get_address_display(this.frm, "customer_address");
-		erpnext.utils.set_taxes_from_address(this.frm, "customer_address", "customer_address", "shipping_address_name");
-	}
-
-	shipping_address_name() {
-		erpnext.utils.get_address_display(this.frm, "shipping_address_name", "shipping_address");
-		erpnext.utils.set_taxes_from_address(this.frm, "shipping_address_name", "customer_address", "shipping_address_name");
-	}
-
-	dispatch_address_name() {
-		erpnext.utils.get_address_display(this.frm, "dispatch_address_name", "dispatch_address");
-	}
-
-	sales_partner() {
-		this.apply_pricing_rule();
-	}
-
-	campaign() {
-		this.apply_pricing_rule();
-	}
-
-	selling_price_list() {
-		this.apply_price_list();
-		this.set_dynamic_labels();
-	}
-
-	discount_percentage(doc, cdt, cdn) {
-		var item = frappe.get_doc(cdt, cdn);
-		item.discount_amount = 0.0;
-		this.apply_discount_on_item(doc, cdt, cdn, 'discount_percentage');
-	}
-
-	discount_amount(doc, cdt, cdn) {
-
-		if(doc.name === cdn) {
-			return;
-		}
-
-		var item = frappe.get_doc(cdt, cdn);
-		item.discount_percentage = 0.0;
-		this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount');
-	}
-
-	apply_discount_on_item(doc, cdt, cdn, field) {
-		var item = frappe.get_doc(cdt, cdn);
-		if(!item.price_list_rate) {
-			item[field] = 0.0;
-		} else {
-			this.price_list_rate(doc, cdt, cdn);
-		}
-		this.set_gross_profit(item);
-	}
-
-	commission_rate() {
-		this.calculate_commission();
-	}
-
-	total_commission() {
-		frappe.model.round_floats_in(this.frm.doc, ["amount_eligible_for_commission", "total_commission"]);
-
-		const { amount_eligible_for_commission } = this.frm.doc;
-		if(!amount_eligible_for_commission) return;
-
-		this.frm.set_value(
-			"commission_rate", flt(
-				this.frm.doc.total_commission * 100.0 / amount_eligible_for_commission
-			)
-		);
-	}
-
-	allocated_percentage(doc, cdt, cdn) {
-		var sales_person = frappe.get_doc(cdt, cdn);
-		if(sales_person.allocated_percentage) {
-
-			sales_person.allocated_percentage = flt(sales_person.allocated_percentage,
-				precision("allocated_percentage", sales_person));
-
-			sales_person.allocated_amount = flt(this.frm.doc.amount_eligible_for_commission *
-				sales_person.allocated_percentage / 100.0,
-				precision("allocated_amount", sales_person));
-				refresh_field(["allocated_amount"], sales_person);
-
-			this.calculate_incentive(sales_person);
-			refresh_field(["allocated_percentage", "allocated_amount", "commission_rate","incentives"], sales_person.name,
-				sales_person.parentfield);
-		}
-	}
-
-	sales_person(doc, cdt, cdn) {
-		var row = frappe.get_doc(cdt, cdn);
-		this.calculate_incentive(row);
-		refresh_field("incentives",row.name,row.parentfield);
-	}
-
-	toggle_editable_price_list_rate() {
-		var df = frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "price_list_rate", this.frm.doc.name);
-		var editable_price_list_rate = cint(frappe.defaults.get_default("editable_price_list_rate"));
-
-		if(df && editable_price_list_rate) {
-			const parent_field = frappe.meta.get_parentfield(this.frm.doc.doctype, this.frm.doc.doctype + " Item");
-			if (!this.frm.fields_dict[parent_field]) return;
-
-			this.frm.fields_dict[parent_field].grid.update_docfield_property(
-				'price_list_rate', 'read_only', 0
-			);
-		}
-	}
-
-	calculate_commission() {
-		if(!this.frm.fields_dict.commission_rate || this.frm.doc.docstatus === 1) return;
-
-		if(this.frm.doc.commission_rate > 100) {
-			this.frm.set_value("commission_rate", 100);
-			frappe.throw(`${__(frappe.meta.get_label(
-				this.frm.doc.doctype, "commission_rate", this.frm.doc.name
-			))} ${__("cannot be greater than 100")}`);
-		}
-
-		this.frm.doc.amount_eligible_for_commission = this.frm.doc.items.reduce(
-			(sum, item) => item.grant_commission ? sum + item.base_net_amount : sum, 0
-		)
-
-		this.frm.doc.total_commission = flt(
-			this.frm.doc.amount_eligible_for_commission * this.frm.doc.commission_rate / 100.0,
-			precision("total_commission")
-		);
-
-		refresh_field(["amount_eligible_for_commission", "total_commission"]);
-	}
-
-	calculate_contribution() {
-		var me = this;
-		$.each(this.frm.doc.doctype.sales_team || [], function(i, sales_person) {
-			frappe.model.round_floats_in(sales_person);
-			if (!sales_person.allocated_percentage) return;
-
-			sales_person.allocated_amount = flt(
-				me.frm.doc.amount_eligible_for_commission
-				* sales_person.allocated_percentage
-				/ 100.0,
-				precision("allocated_amount", sales_person)
-			);
-		});
-	}
-
-	calculate_incentive(row) {
-		if(row.allocated_amount)
-		{
-			row.incentives = flt(
-					row.allocated_amount * row.commission_rate / 100.0,
-					precision("incentives", row));
-		}
-	}
-
-	set_dynamic_labels() {
-		super.set_dynamic_labels();
-		this.set_product_bundle_help(this.frm.doc);
-	}
-
-	set_product_bundle_help(doc) {
-		if(!cur_frm.fields_dict.packing_list) return;
-		if ((doc.packed_items || []).length) {
-			$(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true);
-
-			if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
-				var help_msg = "<div class='alert alert-warning'>" +
-					__("For 'Product Bundle' items, Warehouse, Serial No and Batch No will be considered from the 'Packing List' table. If Warehouse and Batch No are same for all packing items for any 'Product Bundle' item, those values can be entered in the main Item table, values will be copied to 'Packing List' table.")+
-				"</div>";
-				frappe.meta.get_docfield(doc.doctype, 'product_bundle_help', doc.name).options = help_msg;
-			}
-		} else {
-			$(cur_frm.fields_dict.packing_list.row.wrapper).toggle(false);
-			if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
-				frappe.meta.get_docfield(doc.doctype, 'product_bundle_help', doc.name).options = '';
-			}
-		}
-		refresh_field('product_bundle_help');
-	}
-
-	company_address() {
-		var me = this;
-		if(this.frm.doc.company_address) {
-			frappe.call({
-				method: "frappe.contacts.doctype.address.address.get_address_display",
-				args: {"address_dict": this.frm.doc.company_address },
-				callback: function(r) {
-					if(r.message) {
-						me.frm.set_value("company_address_display", r.message)
-					}
-				}
-			})
-		} else {
-			this.frm.set_value("company_address_display", "");
-		}
-	}
-
-	conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate) {
-	    super.conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate);
-	}
-
-	qty(doc, cdt, cdn) {
-		super.qty(doc, cdt, cdn);
-	}
-
-	pick_serial_and_batch(doc, cdt, cdn) {
-		let item = locals[cdt][cdn];
-		let me = this;
-		let path = "assets/erpnext/js/utils/serial_no_batch_selector.js";
-
-		frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
-			.then((r) => {
-				if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) {
-					item.has_serial_no = r.message.has_serial_no;
-					item.has_batch_no = r.message.has_batch_no;
-					item.type_of_transaction = item.qty > 0 ? "Outward":"Inward";
-
-					item.title = item.has_serial_no ?
-						__("Select Serial No") : __("Select Batch No");
-
-					if (item.has_serial_no && item.has_batch_no) {
-						item.title = __("Select Serial and Batch");
-					}
-
-					frappe.require(path, function() {
-						new erpnext.SerialBatchPackageSelector(
-							me.frm, item, (r) => {
-								if (r) {
-									frappe.model.set_value(item.doctype, item.name, {
-										"serial_and_batch_bundle": r.name,
-										"qty": Math.abs(r.total_qty)
-									});
-								}
-							}
-						);
-					});
-				}
-			});
-	}
-
-	update_auto_repeat_reference(doc) {
-		if (doc.auto_repeat) {
-			frappe.call({
-				method:"frappe.automation.doctype.auto_repeat.auto_repeat.update_reference",
-				args:{
-					docname: doc.auto_repeat,
-					reference:doc.name
-				},
-				callback: function(r){
-					if (r.message=="success") {
-						frappe.show_alert({message:__("Auto repeat document updated"), indicator:'green'});
-					} else {
-						frappe.show_alert({message:__("An error occurred during the update process"), indicator:'red'});
-					}
-				}
-			})
-		}
-	}
-};
-
-frappe.ui.form.on(cur_frm.doctype,"project", function(frm) {
-	if(in_list(["Delivery Note", "Sales Invoice"], frm.doc.doctype)) {
-		if(frm.doc.project) {
-			frappe.call({
-				method:'erpnext.projects.doctype.project.project.get_cost_center_name' ,
-				args: {	project: frm.doc.project	},
-				callback: function(r, rt) {
-					if(!r.exc) {
-						$.each(frm.doc["items"] || [], function(i, row) {
-							if(r.message) {
-								frappe.model.set_value(row.doctype, row.name, "cost_center", r.message);
-								frappe.msgprint(__("Cost Center For Item with Item Code {0} has been Changed to {1}", [row.item_name, r.message]));
-							}
-						})
-					}
-				}
-			})
-		}
-	}
-})
-
-frappe.ui.form.on(cur_frm.doctype, {
-	set_as_lost_dialog: function(frm) {
-		var dialog = new frappe.ui.Dialog({
-			title: __("Set as Lost"),
-			fields: [
-				{
-					"fieldtype": "Table MultiSelect",
-					"label": __("Lost Reasons"),
-					"fieldname": "lost_reason",
-					"options": frm.doctype === 'Opportunity' ? 'Opportunity Lost Reason Detail': 'Quotation Lost Reason Detail',
-					"reqd": 1
-				},
-				{
-					"fieldtype": "Table MultiSelect",
-					"label": __("Competitors"),
-					"fieldname": "competitors",
-					"options": "Competitor Detail"
-				},
-				{
-					"fieldtype": "Small Text",
-					"label": __("Detailed Reason"),
-					"fieldname": "detailed_reason"
-				},
-			],
-			primary_action: function() {
-				let values = dialog.get_values();
-
-				frm.call({
-					doc: frm.doc,
-					method: 'declare_enquiry_lost',
-					args: {
-						'lost_reasons_list': values.lost_reason,
-						'competitors': values.competitors ? values.competitors : [],
-						'detailed_reason': values.detailed_reason
-					},
-					callback: function(r) {
-						dialog.hide();
-						frm.reload_doc();
-					},
-				});
-			},
-			primary_action_label: __('Declare Lost')
-		});
-
-		dialog.show();
-	}
-})
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index b09b715..e02abb4 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -129,8 +129,6 @@
 			context = this.get_item_dashboard_data(data, this.max_count, true);
 		}
 
-		this.max_count = this.max_count;
-
 		// show more button
 		if (data && data.length === (this.page_length + 1)) {
 			this.content.find('.more').removeClass('hidden');
diff --git a/erpnext/stock/doctype/batch/batch_list.js b/erpnext/stock/doctype/batch/batch_list.js
index 0de9fd0..f1a0643 100644
--- a/erpnext/stock/doctype/batch/batch_list.js
+++ b/erpnext/stock/doctype/batch/batch_list.js
@@ -9,6 +9,6 @@
 			return [__("Expired"), "red", "expiry_date,not in,|expiry_date,<=,Today|batch_qty,>,0|disabled,=,0"]
 		} else {
 			return [__("Active"), "green", "batch_qty,>,0|disabled,=,0"];
-		};
+		}
 	}
 };
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index a648195..eee7972 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -1,14 +1,17 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-{% include 'erpnext/selling/sales_common.js' %};
-
 cur_frm.add_fetch('customer', 'tax_id', 'tax_id');
 
 frappe.provide("erpnext.stock");
 frappe.provide("erpnext.stock.delivery_note");
 frappe.provide("erpnext.accounts.dimensions");
 
+erpnext.accounts.taxes.setup_tax_filters("Sales Taxes and Charges");
+erpnext.accounts.taxes.setup_tax_validations("Delivery Note");
+erpnext.sales_common.setup_selling_controller();
+
+
 frappe.ui.form.on("Delivery Note", {
 	setup: function(frm) {
 		frm.custom_make_buttons = {
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js
index 6ff3ed3..51a899b 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js
@@ -24,7 +24,7 @@
 					if (!doc.docstatus) {
 						frappe.throw(__("Cannot create a Delivery Trip from Draft documents."));
 					}
-				};
+				}
 
 				frappe.new_doc("Delivery Trip")
 					.then(() => {
@@ -51,7 +51,7 @@
 							}
 						});
 					})
-			};
+			}
 		};
 
 		// doclist.page.add_actions_menu_item(__('Create Delivery Trip'), action, false);
@@ -66,4 +66,4 @@
 			erpnext.bulk_transaction_processing.create(doclist, "Delivery Note", "Packing Slip");
 		});
 	}
-};
+}
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
index a6fbb66..de503dc 100755
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
@@ -92,7 +92,7 @@
 					frm.set_value("driver_email", data.message.email);
 				}
 			});
-		};
+		}
 	},
 
 	optimize_route: function (frm) {
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
index cec5e21..c3edba3 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
@@ -24,7 +24,6 @@
 			"description",
 			"variant_of",
 			"valuation_rate",
-			"description",
 			"barcodes",
 			"has_variants",
 			"attributes",
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
index 9c1a809..8215efc 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
@@ -1,10 +1,9 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
-
 frappe.provide("erpnext.stock");
 
+erpnext.landed_cost_taxes_and_charges.setup_triggers("Landed Cost Voucher");
 erpnext.stock.LandedCostVoucher = class LandedCostVoucher extends erpnext.stock.StockController {
 	setup() {
 		var me = this;
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index c1f1b0d..989bfd0 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -3,7 +3,7 @@
 
 // eslint-disable-next-line
 frappe.provide("erpnext.accounts.dimensions");
-{% include 'erpnext/public/js/controllers/buying.js' %};
+erpnext.buying.setup_buying_controller();
 
 frappe.ui.form.on('Material Request', {
 	setup: function(frm) {
@@ -472,13 +472,13 @@
 		set_schedule_date(this.frm);
 	}
 
-	onload(doc, cdt, cdn) {
-		this.frm.set_query("item_code", "items", function() {
+	onload() {
+		this.frm.set_query("item_code", "items", function(doc, cdt, cdn) {
 			if (doc.material_request_type == "Customer Provided") {
 				return{
 					query: "erpnext.controllers.queries.item_query",
 					filters:{
-						'customer': me.frm.doc.customer,
+						'customer': doc.customer,
 						'is_stock_item':1
 					}
 				}
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 35aad78..136553a 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -1,10 +1,12 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-{% include 'erpnext/public/js/controllers/buying.js' %};
-
 frappe.provide("erpnext.stock");
 
+erpnext.accounts.taxes.setup_tax_filters("Purchase Taxes and Charges");
+erpnext.accounts.taxes.setup_tax_validations("Purchase Receipt");
+erpnext.buying.setup_buying_controller();
+
 frappe.ui.form.on("Purchase Receipt", {
 	setup: (frm) => {
 		frm.make_methods = {
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 27066b8..f128c8e 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -271,7 +271,11 @@
 			message += "<br>" + "Traceback: <br>" + traceback
 		frappe.db.set_value(doc.doctype, doc.name, "error_log", message)
 
-		if not isinstance(e, RecoverableErrors):
+		outgoing_email_account = frappe.get_cached_value(
+			"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
+		)
+
+		if outgoing_email_account and not isinstance(e, RecoverableErrors):
 			notify_error_to_stock_managers(doc, message)
 			doc.set_status("Failed")
 	finally:
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 3e83faf..4fb8a10 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -3,7 +3,7 @@
 frappe.provide("erpnext.stock");
 frappe.provide("erpnext.accounts.dimensions");
 
-{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
+erpnext.landed_cost_taxes_and_charges.setup_triggers("Stock Entry");
 
 frappe.ui.form.on('Stock Entry', {
 	setup: function(frm) {
@@ -56,7 +56,7 @@
 
 		frappe.db.get_value('Stock Settings', {name: 'Stock Settings'}, 'sample_retention_warehouse', (r) => {
 			if (r.sample_retention_warehouse) {
-				var filters = [
+				let filters = [
 							["Warehouse", 'company', '=', frm.doc.company],
 							["Warehouse", "is_group", "=",0],
 							['Warehouse', 'name', '!=', r.sample_retention_warehouse]
@@ -75,17 +75,19 @@
 		});
 
 		frm.set_query('batch_no', 'items', function(doc, cdt, cdn) {
-			var item = locals[cdt][cdn];
+			let item = locals[cdt][cdn];
+			let filters = {};
+
 			if(!item.item_code) {
 				frappe.throw(__("Please enter Item Code to get Batch Number"));
 			} else {
 				if (in_list(["Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor"], doc.purpose)) {
-					var filters = {
+					filters = {
 						'item_code': item.item_code,
 						'posting_date': frm.doc.posting_date || frappe.datetime.nowdate()
 					}
 				} else {
-					var filters = {
+					filters = {
 						'item_code': item.item_code
 					}
 				}
@@ -686,7 +688,6 @@
 	},
 
 	process_loss_percentage(frm) {
-		debugger
 		if (frm.doc.process_loss_percentage) {
 			frm.doc.process_loss_qty = flt((frm.doc.fg_completed_qty * frm.doc.process_loss_percentage) / 100 , precision("process_loss_qty", frm.doc));
 			refresh_field("process_loss_qty");
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 cb2adf1..5452692 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -67,6 +67,7 @@
 	},
 
 	company: function(frm) {
+		frm.trigger("toggle_display_account_head");
 		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
@@ -221,9 +222,6 @@
 			frappe.model.set_value(cdt, cdn, "amount_difference", flt(d.amount) - flt(d.current_amount));
 		}
 	},
-	company: function(frm) {
-		frm.trigger("toggle_display_account_head");
-	},
 	toggle_display_account_head: function(frm) {
 		frm.toggle_display(['expense_account', 'cost_center'],
 			erpnext.is_perpetual_inventory_enabled(frm.doc.company));
diff --git a/erpnext/stock/landed_taxes_and_charges_common.js b/erpnext/stock/landed_taxes_and_charges_common.js
deleted file mode 100644
index 1d76a3d..0000000
--- a/erpnext/stock/landed_taxes_and_charges_common.js
+++ /dev/null
@@ -1,61 +0,0 @@
-let document_list = ['Landed Cost Voucher', 'Stock Entry', 'Subcontracting Order', 'Subcontracting Receipt'];
-
-document_list.forEach((doctype) => {
-	frappe.ui.form.on(doctype, {
-		refresh: function(frm) {
-			let tax_field = frm.doc.doctype == 'Landed Cost Voucher' ? 'taxes' : 'additional_costs';
-			frm.set_query("expense_account", tax_field, function() {
-				return {
-					filters: {
-						"account_type": ['in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]],
-						"company": frm.doc.company
-					}
-				};
-			});
-		},
-
-		set_account_currency: function(frm, cdt, cdn) {
-			let row = locals[cdt][cdn];
-			if (row.expense_account) {
-				frappe.db.get_value('Account', row.expense_account, 'account_currency', function(value) {
-					frappe.model.set_value(cdt, cdn, "account_currency", value.account_currency);
-					frm.events.set_exchange_rate(frm, cdt, cdn);
-				});
-			}
-		},
-
-		set_exchange_rate: function(frm, cdt, cdn) {
-			let row = locals[cdt][cdn];
-			let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
-
-			if (row.account_currency == company_currency) {
-				row.exchange_rate = 1;
-				frm.set_df_property('taxes', 'hidden', 1, row.name, 'exchange_rate');
-			} else if (!row.exchange_rate || row.exchange_rate == 1) {
-				frm.set_df_property('taxes', 'hidden', 0, row.name, 'exchange_rate');
-				frappe.call({
-					method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
-					args: {
-						posting_date: frm.doc.posting_date,
-						account: row.expense_account,
-						account_currency: row.account_currency,
-						company: frm.doc.company
-					},
-					callback: function(r) {
-						if (r.message) {
-							frappe.model.set_value(cdt, cdn, "exchange_rate", r.message);
-						}
-					}
-				});
-			}
-
-			frm.refresh_field('taxes');
-		},
-
-		set_base_amount: function(frm, cdt, cdn) {
-			let row = locals[cdt][cdn];
-			frappe.model.set_value(cdt, cdn, "base_amount",
-				flt(flt(row.amount)*row.exchange_rate, precision("base_amount", row)));
-		}
-	});
-});
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/stock/report/cogs_by_item_group/cogs_by_item_group.js b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js
index d7c50a6..a032285 100644
--- a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js
+++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 
 frappe.query_reports["COGS By Item Group"] = {
diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.js b/erpnext/stock/report/delayed_item_report/delayed_item_report.js
index 40e6abe..cf6e12f 100644
--- a/erpnext/stock/report/delayed_item_report/delayed_item_report.js
+++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Delayed Item Report"] = {
 	"filters": [
diff --git a/erpnext/stock/report/delayed_order_report/delayed_order_report.js b/erpnext/stock/report/delayed_order_report/delayed_order_report.js
index aab0f3d..cf489c9 100644
--- a/erpnext/stock/report/delayed_order_report/delayed_order_report.js
+++ b/erpnext/stock/report/delayed_order_report/delayed_order_report.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Delayed Order Report"] = {
 	"filters": [
diff --git a/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js
index 0b8f496..bc86979 100644
--- a/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js
+++ b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 const DIFFERNCE_FIELD_NAMES = [
 	"fifo_qty_diff",
diff --git a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js
index bf11277..0f9120b 100644
--- a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js
+++ b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Incorrect Balance Qty After Transaction"] = {
 	"filters": [
diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js
index c62d480..6325cc8 100644
--- a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js
+++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Incorrect Serial No Valuation"] = {
 	"filters": [
diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js
index ff42480..174d033 100644
--- a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js
+++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Incorrect Stock Value Report"] = {
 	"filters": [
diff --git a/erpnext/stock/report/item_price_stock/item_price_stock.js b/erpnext/stock/report/item_price_stock/item_price_stock.js
index 7af1dab..c4684da 100644
--- a/erpnext/stock/report/item_price_stock/item_price_stock.js
+++ b/erpnext/stock/report/item_price_stock/item_price_stock.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Item Price Stock"] = {
 	"filters": [
diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.js b/erpnext/stock/report/item_shortage_report/item_shortage_report.js
index ca42a33..5642038 100644
--- a/erpnext/stock/report/item_shortage_report/item_shortage_report.js
+++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Item Shortage Report"] = {
 	"filters": [
diff --git a/erpnext/stock/report/item_variant_details/item_variant_details.js b/erpnext/stock/report/item_variant_details/item_variant_details.js
index 78eab40..b902294 100644
--- a/erpnext/stock/report/item_variant_details/item_variant_details.js
+++ b/erpnext/stock/report/item_variant_details/item_variant_details.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Item Variant Details"] = {
 	"filters": [
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
index 976e515..fe977c6 100644
--- a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Serial No Ledger"] = {
 	"filters": [
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.js b/erpnext/stock/report/stock_analytics/stock_analytics.js
index 78afe6d..ea7bf56 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.js
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Stock Analytics"] = {
 	"filters": [
@@ -93,11 +93,11 @@
 			checkboxColumn: true,
 			events: {
 				onCheckRow: function(data) {
-					row_name = data[2].content;
-					row_values = data.slice(7).map(function (column) {
+					let row_name = data[2].content;
+					let row_values = data.slice(7).map(function (column) {
 						return column.content;
 					})
-					entry  = {
+					let entry  = {
 						'name':row_name,
 						'values':row_values
 					}
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js
index 254f527..ffef11a 100644
--- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js
+++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Stock and Account Value Comparison"] = {
 	"filters": [
diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js
index 31f389f..3447e0a 100644
--- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js
+++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 const DIFFERNCE_FIELD_NAMES = [
 	"difference_in_qty",
diff --git a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js
index 2a0fd40..7a48798 100644
--- a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js
+++ b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Stock Qty vs Serial No Count"] = {
 	"filters": [
diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.js b/erpnext/stock/report/total_stock_summary/total_stock_summary.js
index 88054aa..3d247f6 100644
--- a/erpnext/stock/report/total_stock_summary/total_stock_summary.js
+++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Total Stock Summary"] = {
 	"filters": [
diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.js b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.js
index 39cfd72..8d6b283 100644
--- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.js
+++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Warehouse wise Item Balance Age and Value"] = {
         "filters": [
diff --git a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js b/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js
index 752e464..4a77052 100644
--- a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js
+++ b/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Warehouse Wise Stock Balance"] = {
 	"filters": [
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
index 15a2ac9..f2b395a 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
@@ -3,7 +3,7 @@
 
 frappe.provide('erpnext.buying');
 
-{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
+erpnext.landed_cost_taxes_and_charges.setup_triggers("Subcontracting Order");
 
 frappe.ui.form.on('Subcontracting Order', {
 	setup: (frm) => {
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
index 5ee1f7b..94a2589 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
@@ -3,7 +3,7 @@
 
 frappe.provide('erpnext.buying');
 
-{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
+erpnext.landed_cost_taxes_and_charges.setup_triggers("Subcontracting Receipt");
 
 frappe.ui.form.on('Subcontracting Receipt', {
 	setup: (frm) => {
diff --git a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js
index 18691fe..a133770 100644
--- a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js
+++ b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["First Response Time for Issues"] = {
 	"filters": [
diff --git a/erpnext/support/report/issue_analytics/issue_analytics.js b/erpnext/support/report/issue_analytics/issue_analytics.js
index 746eee0..be45b9b 100644
--- a/erpnext/support/report/issue_analytics/issue_analytics.js
+++ b/erpnext/support/report/issue_analytics/issue_analytics.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Issue Analytics"] = {
 	"filters": [
@@ -93,11 +93,11 @@
 			events: {
 				onCheckRow: function(data) {
 					if (data && data.length) {
-						row_name = data[2].content;
-						row_values = data.slice(3).map(function(column) {
+						let row_name = data[2].content;
+						let row_values = data.slice(3).map(function(column) {
 							return column.content;
 						})
-						entry  = {
+						let entry  = {
 							'name': row_name,
 							'values': row_values
 						}
diff --git a/erpnext/support/report/issue_summary/issue_summary.js b/erpnext/support/report/issue_summary/issue_summary.js
index a5122d0..aee6f53 100644
--- a/erpnext/support/report/issue_summary/issue_summary.js
+++ b/erpnext/support/report/issue_summary/issue_summary.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Issue Summary"] = {
 	"filters": [
diff --git a/erpnext/support/report/support_hour_distribution/support_hour_distribution.js b/erpnext/support/report/support_hour_distribution/support_hour_distribution.js
index ae30b6a..82ccc73 100644
--- a/erpnext/support/report/support_hour_distribution/support_hour_distribution.js
+++ b/erpnext/support/report/support_hour_distribution/support_hour_distribution.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["Support Hour Distribution"] = {
 	"filters": [
diff --git a/erpnext/templates/includes/itemised_tax_breakup.html b/erpnext/templates/includes/itemised_tax_breakup.html
index fbc80de..89d4373 100644
--- a/erpnext/templates/includes/itemised_tax_breakup.html
+++ b/erpnext/templates/includes/itemised_tax_breakup.html
@@ -12,14 +12,14 @@
 			</tr>
 		</thead>
 		<tbody>
-			{% for item, taxes in itemised_tax.items() %}
+			{% for taxes in itemised_tax_data %}
 				<tr>
-					<td>{{ item }}</td>
+					<td>{{ taxes.item }}</td>
 					<td class="text-right">
 						{% if doc.get('is_return') %}
-							{{ frappe.utils.fmt_money((itemised_taxable_amount.get(item, 0))|abs, None, doc.currency) }}
+							{{ frappe.utils.fmt_money(taxes.taxable_amount |abs, None, doc.currency) }}
 						{% else %}
-							{{ frappe.utils.fmt_money(itemised_taxable_amount.get(item, 0), None, doc.currency) }}
+							{{ frappe.utils.fmt_money(taxes.taxable_amount, None, doc.currency) }}
 						{% endif %}
 					</td>
 					{% for tax_account in tax_accounts %}
diff --git a/erpnext/templates/includes/product_list.js b/erpnext/templates/includes/product_list.js
index 2f9d978..acee36c 100644
--- a/erpnext/templates/includes/product_list.js
+++ b/erpnext/templates/includes/product_list.js
@@ -27,10 +27,10 @@
 }
 
 window.render_product_list = function(data) {
-	var table = $("#search-list .table");
+	let table = $("#search-list .table");
 	if(data.length) {
 		if(!table.length)
-			var table = $("<table class='table'>").appendTo("#search-list");
+			table = $("<table class='table'>").appendTo("#search-list");
 
 		$.each(data, function(i, d) {
 			$(d).appendTo(table);
@@ -38,11 +38,13 @@
 	}
 	if(data.length < 10) {
 		if(!table) {
+			let message = __("No products found.");
 			$(".more-btn")
-				.replaceWith("<div class='alert alert-warning'>{{ _("No products found.") }}</div>");
+				.replaceWith(`<div class='alert alert-warning'>{{ ${message} }}</div>`);
 		} else {
+			let message = __("Nothing more to show.");
 			$(".more-btn")
-				.replaceWith("<div class='text-muted'>{{ _("Nothing more to show.") }}</div>");
+				.replaceWith(`<div class='text-muted'>{{ ${message} }}</div>`);
 		}
 	} else {
 		$(".more-btn").toggle(true)
diff --git a/erpnext/templates/pages/projects.js b/erpnext/templates/pages/projects.js
index bd6bcea..7149cee 100644
--- a/erpnext/templates/pages/projects.js
+++ b/erpnext/templates/pages/projects.js
@@ -72,7 +72,7 @@
 
 	var more_items = function(item, item_status){
 		if(item_status) {
-			var item_status = $('.project-'+ item +'-section .btn-group .bold').hasClass('btn-completed-'+ item)
+			item_status = $('.project-'+ item +'-section .btn-group .bold').hasClass('btn-completed-'+ item)
 				? 'completed' : 'open';
 		}
 		$.ajax({
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index e30a5d0..8efa94d 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -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,
@@ -8555,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,
@@ -8741,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/erpnext/utilities/regional.py b/erpnext/utilities/regional.py
new file mode 100644
index 0000000..858976f
--- /dev/null
+++ b/erpnext/utilities/regional.py
@@ -0,0 +1,13 @@
+from contextlib import contextmanager
+
+import frappe
+
+
+@contextmanager
+def temporary_flag(flag_name, value):
+	flags = frappe.local.flags
+	flags[flag_name] = value
+	try:
+		yield
+	finally:
+		flags.pop(flag_name, None)
diff --git a/erpnext/utilities/report/youtube_interactions/youtube_interactions.js b/erpnext/utilities/report/youtube_interactions/youtube_interactions.js
index 6e3e4e6..adf2cf6 100644
--- a/erpnext/utilities/report/youtube_interactions/youtube_interactions.js
+++ b/erpnext/utilities/report/youtube_interactions/youtube_interactions.js
@@ -1,6 +1,6 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
-/* eslint-disable */
+
 
 frappe.query_reports["YouTube Interactions"] = {
 	"filters": [
diff --git a/erpnext/www/book_appointment/index.js b/erpnext/www/book_appointment/index.js
index d02cdad..71a34d4 100644
--- a/erpnext/www/book_appointment/index.js
+++ b/erpnext/www/book_appointment/index.js
@@ -243,7 +243,7 @@
 }
 
 function get_form_data() {
-    contact = {};
+    let contact = {};
     let inputs = ['name', 'skype', 'number', 'notes', 'email'];
     inputs.forEach((id) => contact[id] = document.getElementById(`customer_${id}`).value)
     return contact