Merge branch 'develop' of github.com:frappe/erpnext into refactor-website-routing
diff --git a/.eslintrc b/.eslintrc
index 3b6ab74..e40502a 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -151,6 +151,7 @@
 		"context": true,
 		"before": true,
 		"beforeEach": true,
-		"onScan": true
+		"onScan": true,
+		"extend_cscript": true
 	}
 }
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000..c820d23
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,12 @@
+# Since version 2.23 (released in August 2019), git-blame has a feature
+# to ignore or bypass certain commits.
+#
+# This file contains a list of commits that are not likely what you
+# are looking for in a blame, such as mass reformatting or renaming.
+# You can set this file as a default ignore file for blame by running
+# the following command.
+#
+# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
+
+# Replace use of Class.extend with native JS class
+1fe891b287a1b3f225d29ee3d07e7b1824aba9e7
diff --git a/.gitignore b/.gitignore
index 652fbdc..63c51c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
 .wnf-lang-status
 *.egg-info
 dist/
+erpnext/public/dist
 erpnext/docs/current
 *.swp
 *.swo
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 6775398..8d17674 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
 from erpnext.hooks import regional_overrides
 from frappe.utils import getdate
 
-__version__ = '13.2.1'
+__version__ = '13.3.1'
 
 def get_default_company(user=None):
 	'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index e1276e7..781f94e 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -7,26 +7,30 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "auto_accounting_for_stock",
-  "acc_frozen_upto",
-  "frozen_accounts_modifier",
-  "determine_address_tax_category_from",
+  "accounts_transactions_settings_section",
   "over_billing_allowance",
   "role_allowed_to_over_bill",
-  "column_break_4",
-  "credit_controller",
-  "check_supplier_invoice_uniqueness",
   "make_payment_via_journal_entry",
+  "column_break_11",
+  "check_supplier_invoice_uniqueness",
   "unlink_payment_on_cancellation_of_invoice",
-  "unlink_advance_payment_on_cancelation_of_order",
-  "book_asset_depreciation_entry_automatically",
-  "add_taxes_from_item_tax_template",
   "automatically_fetch_payment_terms",
   "delete_linked_ledger_entries",
+  "book_asset_depreciation_entry_automatically",
+  "unlink_advance_payment_on_cancelation_of_order",
+  "tax_settings_section",
+  "determine_address_tax_category_from",
+  "column_break_19",
+  "add_taxes_from_item_tax_template",
+  "period_closing_settings_section",
+  "acc_frozen_upto",
+  "frozen_accounts_modifier",
+  "column_break_4",
+  "credit_controller",
   "deferred_accounting_settings_section",
-  "automatically_process_deferred_accounting_entry",
   "book_deferred_entries_based_on",
   "column_break_18",
+  "automatically_process_deferred_accounting_entry",
   "book_deferred_entries_via_journal_entry",
   "submit_journal_entries",
   "print_settings",
@@ -41,15 +45,6 @@
  ],
  "fields": [
   {
-   "default": "1",
-   "description": "If enabled, the system will post accounting entries for inventory automatically",
-   "fieldname": "auto_accounting_for_stock",
-   "fieldtype": "Check",
-   "hidden": 1,
-   "in_list_view": 1,
-   "label": "Make Accounting Entry For Every Stock Movement"
-  },
-  {
    "description": "Accounting entries are frozen up to this date. Nobody can create or modify entries except users with the role specified below",
    "fieldname": "acc_frozen_upto",
    "fieldtype": "Date",
@@ -94,6 +89,7 @@
    "default": "0",
    "fieldname": "make_payment_via_journal_entry",
    "fieldtype": "Check",
+   "hidden": 1,
    "label": "Make Payment via Journal Entry"
   },
   {
@@ -234,6 +230,29 @@
    "fieldtype": "Link",
    "label": "Role Allowed to Over Bill ",
    "options": "Role"
+  },
+  {
+   "fieldname": "period_closing_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Period Closing Settings"
+  },
+  {
+   "fieldname": "accounts_transactions_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Transactions Settings"
+  },
+  {
+   "fieldname": "column_break_11",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "tax_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Tax Settings"
+  },
+  {
+   "fieldname": "column_break_19",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "icon-cog",
@@ -241,7 +260,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-03-11 18:52:05.601996",
+ "modified": "2021-04-30 15:25:10.381008",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
index f7d471b..78e7ff6 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
@@ -15,7 +15,7 @@
 	},
 
 	refresh: function (frm) {
-		frappe.require("assets/js/bank-reconciliation-tool.min.js", () =>
+		frappe.require("bank-reconciliation-tool.bundle.js", () =>
 			frm.trigger("make_reconciliation_tool")
 		);
 		frm.upload_statement_button = frm.page.set_secondary_action(
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
index 3dbd605..0a2e0bc 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
@@ -239,6 +239,7 @@
 					"withdrawal",
 					"description",
 					"reference_number",
+					"bank_account"
 				],
 			},
 		});
@@ -319,7 +320,7 @@
 			return;
 		}
 
-		frappe.require("/assets/js/data_import_tools.min.js", () => {
+		frappe.require("data_import_tools.bundle.js", () => {
 			frm.import_preview = new frappe.data_import.ImportPreview({
 				wrapper: frm.get_field("import_preview").$wrapper,
 				doctype: frm.doc.reference_doctype,
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
index 5e913cc..7ffff02 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
@@ -146,7 +146,7 @@
   },
   {
    "depends_on": "eval:!doc.__islocal && !doc.import_file\n",
-   "description": "Must be a publicly accessible Google Sheets URL",
+   "description": "Must be a publicly accessible Google Sheets URL and adding Bank Account column is necessary for importing via Google Sheets",
    "fieldname": "google_sheets_url",
    "fieldtype": "Data",
    "label": "Import from Google Sheets"
@@ -202,7 +202,7 @@
  ],
  "hide_toolbar": 1,
  "links": [],
- "modified": "2021-02-10 19:29:59.027325",
+ "modified": "2021-05-12 14:17:37.777246",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Bank Statement Import",
@@ -224,4 +224,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
index 9f41b13..5f110e2 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -47,6 +47,13 @@
 
 	def start_import(self):
 
+		preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template(
+			self.import_file, self.google_sheets_url
+		)
+
+		if 'Bank Account' not in json.dumps(preview):
+			frappe.throw(_("Please add the Bank Account column"))
+
 		from frappe.core.page.background_jobs.background_jobs import get_info
 		from frappe.utils.scheduler import is_scheduler_inactive
 
@@ -67,6 +74,7 @@
 				data_import=self.name,
 				bank_account=self.bank_account,
 				import_file_path=self.import_file,
+				google_sheets_url=self.google_sheets_url,
 				bank=self.bank,
 				template_options=self.template_options,
 				now=frappe.conf.developer_mode or frappe.flags.in_test,
@@ -90,18 +98,20 @@
 	data_import = frappe.get_doc("Bank Statement Import", data_import_name)
 	data_import.export_errored_rows()
 
-def start_import(data_import, bank_account, import_file_path, bank, template_options):
+def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
 	"""This method runs in background job"""
 
 	update_mapping_db(bank, template_options)
 
 	data_import = frappe.get_doc("Bank Statement Import", data_import)
+	file = import_file_path if import_file_path else google_sheets_url
 
-	import_file = ImportFile("Bank Transaction", file = import_file_path, import_type="Insert New Records")
+	import_file = ImportFile("Bank Transaction", file = file, import_type="Insert New Records")
 	data = import_file.raw_data
 
-	add_bank_account(data, bank_account)
-	write_files(import_file, data)
+	if import_file_path:
+		add_bank_account(data, bank_account)
+		write_files(import_file, data)
 
 	try:
 		i = Importer(data_import.reference_doctype, data_import=data_import)
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index d76641d..957a50f 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -194,19 +194,19 @@
 	refresh_field("accounts");
 }
 
-erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
-	onload: function() {
+erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Controller {
+	onload() {
 		this.load_defaults();
 		this.setup_queries();
 		this.setup_balance_formatter();
 		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
-	},
+	}
 
-	onload_post_render: function() {
+	onload_post_render() {
 		cur_frm.get_field("accounts").grid.set_multiple_add("account");
-	},
+	}
 
-	load_defaults: function() {
+	load_defaults() {
 		//this.frm.show_print_first = true;
 		if(this.frm.doc.__islocal && this.frm.doc.company) {
 			frappe.model.set_default_values(this.frm.doc);
@@ -216,9 +216,9 @@
 			var posting_date = this.frm.doc.posting_date;
 			if(!this.frm.doc.amended_from) this.frm.set_value('posting_date', posting_date || frappe.datetime.get_today());
 		}
-	},
+	}
 
-	setup_queries: function() {
+	setup_queries() {
 		var me = this;
 
 		me.frm.set_query("account", "accounts", function(doc, cdt, cdn) {
@@ -324,9 +324,9 @@
 		});
 
 
-	},
+	}
 
-	setup_balance_formatter: function() {
+	setup_balance_formatter() {
 		const formatter = function(value, df, options, doc) {
 			var currency = frappe.meta.get_field_currency(df, doc);
 			var dr_or_cr = value ? ('<label>' + (value > 0.0 ? __("Dr") : __("Cr")) + '</label>') : "";
@@ -337,9 +337,9 @@
 		};
 		this.frm.fields_dict.accounts.grid.update_docfield_property('balance', 'formatter', formatter);
 		this.frm.fields_dict.accounts.grid.update_docfield_property('party_balance', 'formatter', formatter);
-	},
+	}
 
-	reference_name: function(doc, cdt, cdn) {
+	reference_name(doc, cdt, cdn) {
 		var d = frappe.get_doc(cdt, cdn);
 
 		if(d.reference_name) {
@@ -351,9 +351,9 @@
 				this.get_outstanding('Journal Entry', d.reference_name, doc.company, d);
 			}
 		}
-	},
+	}
 
-	get_outstanding: function(doctype, docname, company, child, due_date) {
+	get_outstanding(doctype, docname, company, child, due_date) {
 		var me = this;
 		var args = {
 			"doctype": doctype,
@@ -375,9 +375,9 @@
 				}
 			}
 		});
-	},
+	}
 
-	accounts_add: function(doc, cdt, cdn) {
+	accounts_add(doc, cdt, cdn) {
 		var row = frappe.get_doc(cdt, cdn);
 		$.each(doc.accounts, function(i, d) {
 			if(d.account && d.party && d.party_type) {
@@ -400,9 +400,9 @@
 		cur_frm.cscript.update_totals(doc);
 
 		erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'accounts');
-	},
+	}
 
-});
+};
 
 cur_frm.script_manager.make(erpnext.accounts.JournalEntry);
 
diff --git a/erpnext/accounts/doctype/party_account/party_account.json b/erpnext/accounts/doctype/party_account/party_account.json
index aa32d95..c9f15a6 100644
--- a/erpnext/accounts/doctype/party_account/party_account.json
+++ b/erpnext/accounts/doctype/party_account/party_account.json
@@ -1,87 +1,39 @@
 {
- "allow_copy": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2014-08-29 16:02:39.740505", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2014-08-29 16:02:39.740505",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "field_order": [
+  "company",
+  "account"
+ ],
  "fields": [
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "company", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Company", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "account", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Account", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Account", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
+   "fieldname": "account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Account",
+   "options": "Account"
   }
- ], 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
-
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2016-07-11 03:28:03.348246", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Party Account", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_seen": 0
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-04-07 18:13:08.833822",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Party Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index d1523cd..c71a62d 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -34,8 +34,8 @@
 	}
 });
 
-erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({
-	onload: function() {
+erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationController extends frappe.ui.form.Controller {
+	onload() {
 		var me = this;
 
 		this.frm.set_query("party", function() {
@@ -84,18 +84,18 @@
 				frappe.throw({message: __("Please Select Both Company and Party Type First"), title: title});
 			}
 		};
-	},
+	}
 
-	refresh: function() {
+	refresh() {
 		this.frm.disable_save();
 		this.toggle_primary_action();
-	},
+	}
 
-	onload_post_render: function() {
+	onload_post_render() {
 		this.toggle_primary_action();
-	},
+	}
 
-	party: function() {
+	party() {
 		var me = this
 		if (!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) {
 			return frappe.call({
@@ -112,9 +112,9 @@
 				}
 			});
 		}
-	},
+	}
 
-	get_unreconciled_entries: function() {
+	get_unreconciled_entries() {
 		var me = this;
 		return this.frm.call({
 			doc: me.frm.doc,
@@ -125,9 +125,9 @@
 			}
 		});
 
-	},
+	}
 
-	reconcile: function() {
+	reconcile() {
 		var me = this;
 		var show_dialog = me.frm.doc.payments.filter(d => d.difference_amount && !d.difference_account);
 
@@ -209,9 +209,9 @@
 		} else {
 			this.reconcile_payment_entries();
 		}
-	},
+	}
 
-	reconcile_payment_entries: function() {
+	reconcile_payment_entries() {
 		var me = this;
 
 		return this.frm.call({
@@ -222,9 +222,9 @@
 				me.toggle_primary_action();
 			}
 		});
-	},
+	}
 
-	set_invoice_options: function() {
+	set_invoice_options() {
 		var me = this;
 		var invoices = [];
 
@@ -244,9 +244,9 @@
 		}
 
 		refresh_field("payments");
-	},
+	}
 
-	toggle_primary_action: function() {
+	toggle_primary_action() {
 		if ((this.frm.doc.payments || []).length) {
 			this.frm.fields_dict.reconcile.$input
 				&& this.frm.fields_dict.reconcile.$input.addClass("btn-primary");
@@ -260,6 +260,6 @@
 		}
 	}
 
-});
+};
 
-$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index cf6ec18..6635128 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -114,7 +114,7 @@
 				'party_type': self.party_type,
 				'voucher_type': voucher_type,
 				'account': self.receivable_payable_account
-			}, as_dict=1, debug=1)
+			}, as_dict=1)
 
 	def add_payment_entries(self, entries):
 		self.set('payments', [])
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index aa0c53e..8c5a34a 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -101,15 +101,24 @@
 	},
 
 	before_save: function(frm) {
+		frm.set_value("grand_total", 0);
+		frm.set_value("net_total", 0);
+		frm.set_value("total_quantity", 0);
+		frm.set_value("taxes", []);
+
+		for (let row of frm.doc.payment_reconciliation) {
+			row.expected_amount = 0;
+		}
+
 		for (let row of frm.doc.pos_transactions) {
 			frappe.db.get_doc("POS Invoice", row.pos_invoice).then(doc => {
-				cur_frm.doc.grand_total -= flt(doc.grand_total);
-				cur_frm.doc.net_total -= flt(doc.net_total);
-				cur_frm.doc.total_quantity -= flt(doc.total_qty);
-				refresh_payments(doc, cur_frm, 1);
-				refresh_taxes(doc, cur_frm, 1);
-				refresh_fields(cur_frm);
-				set_html_data(cur_frm);
+				frm.doc.grand_total += flt(doc.grand_total);
+				frm.doc.net_total += flt(doc.net_total);
+				frm.doc.total_quantity += flt(doc.total_qty);
+				refresh_payments(doc, frm);
+				refresh_taxes(doc, frm);
+				refresh_fields(frm);
+				set_html_data(frm);
 			});
 		}
 	}
@@ -118,7 +127,7 @@
 frappe.ui.form.on('POS Closing Entry Detail', {
 	closing_amount: (frm, cdt, cdn) => {
 		const row = locals[cdt][cdn];
-		frappe.model.set_value(cdt, cdn, "difference", flt(row.expected_amount - row.closing_amount))
+		frappe.model.set_value(cdt, cdn, "difference", flt(row.expected_amount - row.closing_amount));
 	}
 })
 
@@ -142,28 +151,28 @@
 	})
 }
 
-function refresh_payments(d, frm, remove) {
+function refresh_payments(d, frm) {
 	d.payments.forEach(p => {
 		const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
 		if (payment) {
-			if (!remove) payment.expected_amount += flt(p.amount);
-			else payment.expected_amount -= flt(p.amount);
+			payment.expected_amount += flt(p.amount);
+			payment.difference = payment.closing_amount - payment.expected_amount;
 		} else {
 			frm.add_child("payment_reconciliation", {
 				mode_of_payment: p.mode_of_payment,
 				opening_amount: 0,
-				expected_amount: p.amount
+				expected_amount: p.amount,
+				closing_amount: 0
 			})
 		}
 	})
 }
 
-function refresh_taxes(d, frm, remove) {
+function refresh_taxes(d, frm) {
 	d.taxes.forEach(t => {
 		const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
 		if (tax) {
-			if (!remove) tax.amount += flt(t.tax_amount);
-			else tax.amount -= flt(t.tax_amount);
+			tax.amount += flt(t.tax_amount);
 		} else {
 			frm.add_child("taxes", {
 				account_head: t.account_head,
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
index 493bd44..181e9f8 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
@@ -4,18 +4,18 @@
 {% include 'erpnext/selling/sales_common.js' %};
 frappe.provide("erpnext.accounts");
 
-erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend({
+erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnext.selling.SellingController {
 	setup(doc) {
 		this.setup_posting_date_time_check();
-		this._super(doc);
-	},
+		super.setup(doc);
+	}
 
-	company: function() {
+	company() {
 		erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
-	},
+	}
 
 	onload(doc) {
-		this._super();
+		super.onload();
 		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log'];
 		if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
 			this.frm.script_manager.trigger("is_pos");
@@ -23,10 +23,10 @@
 		}
 
 		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
-	},
+	}
 
 	refresh(doc) {
-		this._super();
+		super.refresh();
 		if (doc.docstatus == 1 && !doc.is_return) {
 			this.frm.add_custom_button(__('Return'), this.make_sales_return, __('Create'));
 			this.frm.page.set_inner_btn_group_as_primary(__('Create'));
@@ -36,13 +36,13 @@
 			this.frm.return_print_format = "Sales Invoice Return";
 			this.frm.set_value('consolidated_invoice', '');
 		}
-	},
+	}
 
-	is_pos: function() {
+	is_pos() {
 		this.set_pos_data();
-	},
+	}
 
-	set_pos_data: async function() {
+	async set_pos_data() {
 		if(this.frm.doc.is_pos) {
 			this.frm.set_value("allocate_advances_automatically", 0);
 			if(!this.frm.doc.company) {
@@ -69,7 +69,7 @@
 				}
 			}
 		}
-	},
+	}
 
 	customer() {
 		if (!this.frm.doc.customer) return
@@ -86,13 +86,13 @@
 			}, () => {
 				this.apply_pricing_rule();
 			});
-	},
+	}
 
-	amount: function(){
+	amount(){
 		this.write_off_outstanding_amount_automatically()
-	},
+	}
 
-	change_amount: function(){
+	change_amount(){
 		if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
 			this.calculate_write_off_amount();
 		}else {
@@ -101,16 +101,16 @@
 		}
 
 		this.frm.refresh_fields();
-	},
+	}
 
-	loyalty_amount: function(){
+	loyalty_amount(){
 		this.calculate_outstanding_amount();
 		this.frm.refresh_field("outstanding_amount");
 		this.frm.refresh_field("paid_amount");
 		this.frm.refresh_field("base_paid_amount");
-	},
+	}
 
-	write_off_outstanding_amount_automatically: function() {
+	write_off_outstanding_amount_automatically() {
 		if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
 			frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
 			// this will make outstanding amount 0
@@ -125,17 +125,17 @@
 
 		this.calculate_outstanding_amount(false);
 		this.frm.refresh_fields();
-	},
+	}
 
-	make_sales_return: function() {
+	make_sales_return() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.accounts.doctype.pos_invoice.pos_invoice.make_sales_return",
 			frm: cur_frm
 		})
-	},
-})
+	}
+}
 
-$.extend(cur_frm.cscript, new erpnext.selling.POSInvoiceController({ frm: cur_frm }))
+extend_cscript(cur_frm.cscript, new erpnext.selling.POSInvoiceController({ frm: cur_frm }))
 
 frappe.ui.form.on('POS Invoice', {
 	redeem_loyalty_points: function(frm) {
@@ -235,4 +235,4 @@
 				});
 		});
 	}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 1e6a3d1..473db56 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -461,7 +461,17 @@
 		order by posting_date desc, posting_time desc
 		limit 1""", (item_code, warehouse), as_dict=1)
 
-	pos_sales_qty = frappe.db.sql("""select sum(p_item.qty) as qty
+	pos_sales_qty = get_pos_reserved_qty(item_code, warehouse)
+
+	sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0
+
+	if sle_qty and pos_sales_qty:
+		return sle_qty - pos_sales_qty
+	else:
+		return sle_qty
+
+def get_pos_reserved_qty(item_code, warehouse):
+	reserved_qty = frappe.db.sql("""select sum(p_item.qty) as qty
 		from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item
 		where p.name = p_item.parent
 		and p.consolidated_invoice is NULL
@@ -470,14 +480,8 @@
 		and p_item.item_code = %s
 		and p_item.warehouse = %s
 		""", (item_code, warehouse), as_dict=1)
-
-	sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0
-	pos_sales_qty = pos_sales_qty[0].qty or 0 if pos_sales_qty else 0
-
-	if sle_qty and pos_sales_qty:
-		return sle_qty - pos_sales_qty
-	else:
-		return sle_qty
+	
+	return reserved_qty[0].qty or 0 if reserved_qty else 0
 
 @frappe.whitelist()
 def make_sales_return(source_name, target_doc=None):
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
index f61aacb..db1fea1 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
@@ -1,24 +1,43 @@
-<h1 class="text-center" style="page-break-before:always">{{ filters.party[0] }}</h1>
-<h3 class="text-center">{{ _("Statement of Accounts") }}</h3>
+<div class="page-break">
+	<div id="header-html" class="hidden-pdf">
+		{% if letter_head %}
+		<div class="letter-head text-center">{{ letter_head.content }}</div>
+		<hr style="height:2px;border-width:0;color:black;background-color:black;">
+		{% endif %}
+	</div>
+	<div id="footer-html" class="visible-pdf">
+		{% if letter_head.footer %}
+		<div class="letter-head-footer">
+			<hr style="border-width:0;color:black;background-color:black;padding-bottom:2px;">
+			{{ letter_head.footer }}
+		</div>
+		{% endif %}
+	</div>
 
-<h5 class="text-center">
-    {{ frappe.format(filters.from_date, 'Date')}}
-	{{ _("to") }}
-	{{ frappe.format(filters.to_date, 'Date')}}
-</h5>
+	<h2 class="text-center">{{ _("STATEMENTS OF ACCOUNTS") }}</h2>
+	<div>
+		<h5 style="float: left;">{{ _("Customer: ") }} <b>{{filters.party[0] }}</b></h5>
+		<h5 style="float: right;">
+			{{ _("Date: ") }}
+			<b>{{ frappe.format(filters.from_date, 'Date')}}
+			{{ _("to") }}
+			{{ frappe.format(filters.to_date, 'Date')}}</b>
+			</h5>
+	</div>
+	<br>
 
-<table class="table table-bordered">
-	<thead>
-		<tr>
-			<th style="width: 12%">{{ _("Date") }}</th>
-			<th style="width: 15%">{{ _("Ref") }}</th>
-			<th style="width: 25%">{{ _("Party") }}</th>
-			<th style="width: 15%">{{ _("Debit") }}</th>
-			<th style="width: 15%">{{ _("Credit") }}</th>
-			<th style="width: 18%">{{ _("Balance (Dr - Cr)") }}</th>
-		</tr>
-	</thead>
-	<tbody>
+	<table class="table table-bordered">
+		<thead>
+			<tr>
+				<th style="width: 12%">{{ _("Date") }}</th>
+				<th style="width: 15%">{{ _("Reference") }}</th>
+				<th style="width: 25%">{{ _("Remarks") }}</th>
+				<th style="width: 15%">{{ _("Debit") }}</th>
+				<th style="width: 15%">{{ _("Credit") }}</th>
+				<th style="width: 18%">{{ _("Balance (Dr - Cr)") }}</th>
+			</tr>
+		</thead>
+		<tbody>
 		{% for row in data %}
 			<tr>
 			{% if(row.posting_date) %}
@@ -58,32 +77,34 @@
 			</tr>
 		{% endfor %}
 		</tbody>
-</table>
-<br><br>
-{% if ageing %}
-<h3 class="text-center">{{ _("Ageing Report Based On ") }} {{ ageing.ageing_based_on }}</h3>
-<h5 class="text-center">
-	{{ _("Up to " ) }}  {{ frappe.format(filters.to_date, 'Date')}}
-</h5>
-<br>
-
-<table class="table table-bordered">
-	<thead>
-		<tr>
-			<th style="width: 12%">30 Days</th>
-			<th style="width: 15%">60 Days</th>
-			<th style="width: 25%">90 Days</th>
-			<th style="width: 15%">120 Days</th>
-		</tr>
-	</thead>
-	<tbody>
-		<tr>
-			<td>{{ frappe.utils.fmt_money(ageing.range1, currency=filters.presentation_currency) }}</td>
-			<td>{{ frappe.utils.fmt_money(ageing.range2, currency=filters.presentation_currency) }}</td>
-			<td>{{ frappe.utils.fmt_money(ageing.range3, currency=filters.presentation_currency) }}</td>
-			<td>{{ frappe.utils.fmt_money(ageing.range4, currency=filters.presentation_currency) }}</td>
-		</tr>
-	</tbody>
-</table>
-{% endif %}
-<p class="text-right text-muted">Printed On {{ frappe.format(frappe.utils.get_datetime(), 'Datetime') }}</p>
\ No newline at end of file
+	</table>
+	<br>
+	{% if ageing %}
+	<h4 class="text-center">{{ _("Ageing Report based on ") }} {{ ageing.ageing_based_on }}
+		{{ _("up to " ) }}  {{ frappe.format(filters.to_date, 'Date')}}
+	</h4>
+	<table class="table table-bordered">
+		<thead>
+			<tr>
+				<th style="width: 25%">30 Days</th>
+				<th style="width: 25%">60 Days</th>
+				<th style="width: 25%">90 Days</th>
+				<th style="width: 25%">120 Days</th>
+			</tr>
+		</thead>
+		<tbody>
+			<tr>
+				<td>{{ frappe.utils.fmt_money(ageing.range1, currency=filters.presentation_currency) }}</td>
+				<td>{{ frappe.utils.fmt_money(ageing.range2, currency=filters.presentation_currency) }}</td>
+				<td>{{ frappe.utils.fmt_money(ageing.range3, currency=filters.presentation_currency) }}</td>
+				<td>{{ frappe.utils.fmt_money(ageing.range4, currency=filters.presentation_currency) }}</td>
+			</tr>
+		</tbody>
+	</table>
+	{% endif %}
+	{% if terms_and_conditions %}
+	<div>
+		{{ terms_and_conditions }}
+	</div>
+	{% endif %}
+</div>
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
index 4be0e2e..922f636 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
@@ -1,6 +1,5 @@
 {
  "actions": [],
- "allow_workflow": 1,
  "autoname": "Prompt",
  "creation": "2020-05-22 16:46:18.712954",
  "doctype": "DocType",
@@ -28,9 +27,11 @@
   "customers",
   "preferences",
   "orientation",
-  "section_break_14",
   "include_ageing",
   "ageing_based_on",
+  "section_break_14",
+  "letter_head",
+  "terms_and_conditions",
   "section_break_1",
   "enable_auto_email",
   "section_break_18",
@@ -270,10 +271,22 @@
    "fieldname": "body",
    "fieldtype": "Text Editor",
    "label": "Body"
+  },
+  {
+   "fieldname": "letter_head",
+   "fieldtype": "Link",
+   "label": "Letter Head",
+   "options": "Letter Head"
+  },
+  {
+   "fieldname": "terms_and_conditions",
+   "fieldtype": "Link",
+   "label": "Terms and Conditions",
+   "options": "Terms and Conditions"
   }
  ],
  "links": [],
- "modified": "2020-08-08 08:47:09.185728",
+ "modified": "2021-05-13 12:44:19.574844",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Process Statement Of Accounts",
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index a0dbff3..cf79c03 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
@@ -64,6 +64,9 @@
 		tax_id = frappe.get_doc('Customer', entry.customer).tax_id
 		presentation_currency = get_party_account_currency('Customer', entry.customer, doc.company) \
 				or doc.currency or get_company_currency(doc.company)
+		if doc.letter_head:
+			from frappe.www.printview import get_letter_head
+			letter_head = get_letter_head(doc, 0)
 
 		filters= frappe._dict({
 			'from_date': doc.from_date,
@@ -91,8 +94,10 @@
 			continue
 
 		html = frappe.render_template(template_path, \
-			{"filters": filters, "data": res, "ageing": ageing[0] if (doc.include_ageing and ageing) else None})
-
+			{"filters": filters, "data": res, "ageing": ageing[0] if doc.include_ageing else None,
+				"letter_head": letter_head if doc.letter_head else None,
+				"terms_and_conditions": frappe.db.get_value('Terms and Conditions', doc.terms_and_conditions, 'terms')
+					if doc.terms_and_conditions else None})
 		html = frappe.render_template(base_template_path, {"body": html, \
 			"css": get_print_style(), "title": "Statement For " + entry.customer})
 		statement_dict[entry.customer] = html
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index f58c8f4..617b5b4 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -4,10 +4,10 @@
 frappe.provide("erpnext.accounts");
 {% include 'erpnext/public/js/controllers/buying.js' %};
 
-erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
-	setup: function(doc) {
+erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.BuyingController {
+	setup(doc) {
 		this.setup_posting_date_time_check();
-		this._super(doc);
+		super.setup(doc);
 
 		// formatter for purchase invoice item
 		if(this.frm.doc.update_stock) {
@@ -25,14 +25,14 @@
 				}
 			};
 		});
-	},
+	}
 
-	company: function() {
+	company() {
 		erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
-	},
+	}
 
-	onload: function() {
-		this._super();
+	onload() {
+		super.onload();
 
 		if(!this.frm.doc.__islocal) {
 			// show credit_to in print format
@@ -48,11 +48,11 @@
 		}
 
 		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
-	},
+	}
 
-	refresh: function(doc) {
+	refresh(doc) {
 		const me = this;
-		this._super();
+		super.refresh();
 
 		hide_fields(this.frm.doc);
 		// Show / Hide button
@@ -161,26 +161,26 @@
 		}
 
 		this.frm.set_df_property("tax_withholding_category", "hidden", doc.apply_tds ? 0 : 1);
-	},
+	}
 
-	unblock_invoice: function() {
+	unblock_invoice() {
 		const me = this;
 		frappe.call({
 			'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.unblock_invoice',
 			'args': {'name': me.frm.doc.name},
 			'callback': (r) => me.frm.reload_doc()
 		});
-	},
+	}
 
-	block_invoice: function() {
+	block_invoice() {
 		this.make_comment_dialog_and_block_invoice();
-	},
+	}
 
-	change_release_date: function() {
+	change_release_date() {
 		this.make_dialog_and_set_release_date();
-	},
+	}
 
-	can_change_release_date: function(date) {
+	can_change_release_date(date) {
 		const diff = frappe.datetime.get_diff(date, frappe.datetime.nowdate());
 		if (diff < 0) {
 			frappe.throw(__('New release date should be in the future'));
@@ -188,9 +188,9 @@
 		} else {
 			return true;
 		}
-	},
+	}
 
-	make_comment_dialog_and_block_invoice: function(){
+	make_comment_dialog_and_block_invoice(){
 		const me = this;
 
 		const title = __('Block Invoice');
@@ -232,9 +232,9 @@
 		});
 
 		this.dialog.show();
-	},
+	}
 
-	make_dialog_and_set_release_date: function() {
+	make_dialog_and_set_release_date() {
 		const me = this;
 
 		const title = __('Set New Release Date');
@@ -263,17 +263,17 @@
 		});
 
 		this.dialog.show();
-	},
+	}
 
-	set_release_date: function(data) {
+	set_release_date(data) {
 		return frappe.call({
 			'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.change_release_date',
 			'args': data,
 			'callback': (r) => this.frm.reload_doc()
 		});
-	},
+	}
 
-	supplier: function() {
+	supplier() {
 		var me = this;
 
 		// Do not update if inter company reference is there as the details will already be updated
@@ -295,9 +295,9 @@
 				me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
 				me.frm.set_df_property("tax_withholding_category", "hidden", me.frm.supplier_tds ? 0 : 1);
 			})
-	},
+	}
 
-	apply_tds: function(frm) {
+	apply_tds(frm) {
 		var me = this;
 
 		if (!me.frm.doc.apply_tds) {
@@ -307,9 +307,9 @@
 			me.frm.set_value("tax_withholding_category", me.frm.supplier_tds);
 			me.frm.set_df_property("tax_withholding_category", "hidden", 0);
 		}
-	},
+	}
 
-	credit_to: function() {
+	credit_to() {
 		var me = this;
 		if(this.frm.doc.credit_to) {
 			me.frm.call({
@@ -327,16 +327,16 @@
 				}
 			});
 		}
-	},
+	}
 
-	make_inter_company_invoice: function(frm) {
+	make_inter_company_invoice(frm) {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_inter_company_sales_invoice",
 			frm: frm
 		});
-	},
+	}
 
-	is_paid: function() {
+	is_paid() {
 		hide_fields(this.frm.doc);
 		if(cint(this.frm.doc.is_paid)) {
 			this.frm.set_value("allocate_advances_automatically", 0);
@@ -347,44 +347,44 @@
 		}
 		this.calculate_outstanding_amount();
 		this.frm.refresh_fields();
-	},
+	}
 
-	write_off_amount: function() {
+	write_off_amount() {
 		this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
 		this.calculate_outstanding_amount();
 		this.frm.refresh_fields();
-	},
+	}
 
-	paid_amount: function() {
+	paid_amount() {
 		this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
 		this.write_off_amount();
 		this.frm.refresh_fields();
-	},
+	}
 
-	allocated_amount: function() {
+	allocated_amount() {
 		this.calculate_total_advance();
 		this.frm.refresh_fields();
-	},
+	}
 
-	items_add: function(doc, cdt, cdn) {
+	items_add(doc, cdt, cdn) {
 		var row = frappe.get_doc(cdt, cdn);
 		this.frm.script_manager.copy_from_first_row("items", row,
 			["expense_account", "cost_center", "project"]);
-	},
+	}
 
-	on_submit: function() {
+	on_submit() {
 		$.each(this.frm.doc["items"] || [], function(i, row) {
 			if(row.purchase_receipt) frappe.model.clear_doc("Purchase Receipt", row.purchase_receipt)
 		})
-	},
+	}
 
-	make_debit_note: function() {
+	make_debit_note() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_debit_note",
 			frm: cur_frm
 		})
-	},
-});
+	}
+};
 
 cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
 
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 24e67fe..d3d3ffa 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -1380,7 +1380,7 @@
  "idx": 204,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-30 22:45:58.334107",
+ "modified": "2021-04-30 22:45:58.334107",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 8a42d9e..16e70dd 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -5,19 +5,19 @@
 frappe.provide("erpnext.accounts");
 
 
-erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.extend({
-	setup: function(doc) {
+erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends erpnext.selling.SellingController {
+	setup(doc) {
 		this.setup_posting_date_time_check();
-		this._super(doc);
-	},
-	company: function() {
+		super.setup(doc);
+	}
+	company() {
 		erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
-	},
-	onload: function() {
+	}
+	onload() {
 		var me = this;
-		this._super();
+		super.onload();
 
-		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice'];
+		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet'];
 		if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
 			// show debit_to in print format
 			this.frm.set_df_property("debit_to", "print_hide", 0);
@@ -35,11 +35,11 @@
 		}
 		erpnext.queries.setup_warehouse_query(this.frm);
 		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
-	},
+	}
 
-	refresh: function(doc, dt, dn) {
+	refresh(doc, dt, dn) {
 		const me = this;
-		this._super();
+		super.refresh();
 		if(cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) {
 			// hide new msgbox
 			cur_frm.msgbox.hide();
@@ -138,16 +138,16 @@
 				}, __('Create'));
 			}
 		}
-	},
+	}
 
-	make_maintenance_schedule: function() {
+	make_maintenance_schedule() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule",
 			frm: cur_frm
 		})
-	},
+	}
 
-	on_submit: function(doc, dt, dn) {
+	on_submit(doc, dt, dn) {
 		var me = this;
 
 		if (frappe.get_route()[0] != 'Form') {
@@ -157,9 +157,9 @@
 		$.each(doc["items"], function(i, row) {
 			if(row.delivery_note) frappe.model.clear_doc("Delivery Note", row.delivery_note)
 		})
-	},
+	}
 
-	set_default_print_format: function() {
+	set_default_print_format() {
 		// set default print format to POS type or Credit Note
 		if(cur_frm.doc.is_pos) {
 			if(cur_frm.pos_print_format) {
@@ -180,9 +180,9 @@
 				cur_frm.meta._default_print_format = null;
 			}
 		}
-	},
+	}
 
-	sales_order_btn: function() {
+	sales_order_btn() {
 		var me = this;
 		this.$sales_order_btn = this.frm.add_custom_button(__('Sales Order'),
 			function() {
@@ -201,9 +201,9 @@
 					}
 				})
 			}, __("Get Items From"));
-	},
+	}
 
-	quotation_btn: function() {
+	quotation_btn() {
 		var me = this;
 		this.$quotation_btn = this.frm.add_custom_button(__('Quotation'),
 			function() {
@@ -225,9 +225,9 @@
 					}
 				})
 			}, __("Get Items From"));
-	},
+	}
 
-	delivery_note_btn: function() {
+	delivery_note_btn() {
 		var me = this;
 		this.$delivery_note_btn = this.frm.add_custom_button(__('Delivery Note'),
 			function() {
@@ -253,12 +253,12 @@
 					}
 				});
 			}, __("Get Items From"));
-	},
+	}
 
-	tc_name: function() {
+	tc_name() {
 		this.get_terms();
-	},
-	customer: function() {
+	}
+	customer() {
 		if (this.frm.doc.is_pos){
 			var pos_profile = this.frm.doc.pos_profile;
 		}
@@ -289,16 +289,16 @@
 				}
 			});
 		}
-	},
+	}
 
-	make_inter_company_invoice: function() {
+	make_inter_company_invoice() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_inter_company_purchase_invoice",
 			frm: me.frm
 		});
-	},
+	}
 
-	debit_to: function() {
+	debit_to() {
 		var me = this;
 		if(this.frm.doc.debit_to) {
 			me.frm.call({
@@ -316,14 +316,14 @@
 				}
 			});
 		}
-	},
+	}
 
-	allocated_amount: function() {
+	allocated_amount() {
 		this.calculate_total_advance();
 		this.frm.refresh_fields();
-	},
+	}
 
-	write_off_outstanding_amount_automatically: function() {
+	write_off_outstanding_amount_automatically() {
 		if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
 			frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
 			// this will make outstanding amount 0
@@ -338,39 +338,39 @@
 
 		this.calculate_outstanding_amount(false);
 		this.frm.refresh_fields();
-	},
+	}
 
-	write_off_amount: function() {
+	write_off_amount() {
 		this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
 		this.write_off_outstanding_amount_automatically();
-	},
+	}
 
-	items_add: function(doc, cdt, cdn) {
+	items_add(doc, cdt, cdn) {
 		var row = frappe.get_doc(cdt, cdn);
 		this.frm.script_manager.copy_from_first_row("items", row, ["income_account", "cost_center"]);
-	},
+	}
 
-	set_dynamic_labels: function() {
-		this._super();
+	set_dynamic_labels() {
+		super.set_dynamic_labels();
 		this.frm.events.hide_fields(this.frm)
-	},
+	}
 
-	items_on_form_rendered: function() {
-		erpnext.setup_serial_no();
-	},
+	items_on_form_rendered() {
+		erpnext.setup_serial_or_batch_no();
+	}
 
-	packed_items_on_form_rendered: function(doc, grid_row) {
-		erpnext.setup_serial_no();
-	},
+	packed_items_on_form_rendered(doc, grid_row) {
+		erpnext.setup_serial_or_batch_no();
+	}
 
-	make_sales_return: function() {
+	make_sales_return() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return",
 			frm: cur_frm
 		})
-	},
+	}
 
-	asset: function(frm, cdt, cdn) {
+	asset(frm, cdt, cdn) {
 		var row = locals[cdt][cdn];
 		if(row.asset) {
 			frappe.call({
@@ -384,18 +384,18 @@
 				}
 			})
 		}
-	},
+	}
 
-	is_pos: function(frm){
+	is_pos(frm){
 		this.set_pos_data();
-	},
+	}
 
-	pos_profile: function() {
+	pos_profile() {
 		this.frm.doc.taxes = []
 		this.set_pos_data();
-	},
+	}
 
-	set_pos_data: function() {
+	set_pos_data() {
 		if(this.frm.doc.is_pos) {
 			this.frm.set_value("allocate_advances_automatically", 0);
 			if(!this.frm.doc.company) {
@@ -425,13 +425,13 @@
 			}
 		}
 		else this.frm.trigger("refresh");
-	},
+	}
 
-	amount: function(){
+	amount(){
 		this.write_off_outstanding_amount_automatically()
-	},
+	}
 
-	change_amount: function(){
+	change_amount(){
 		if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
 			this.calculate_write_off_amount();
 		}else {
@@ -440,18 +440,18 @@
 		}
 
 		this.frm.refresh_fields();
-	},
+	}
 
-	loyalty_amount: function(){
+	loyalty_amount(){
 		this.calculate_outstanding_amount();
 		this.frm.refresh_field("outstanding_amount");
 		this.frm.refresh_field("paid_amount");
 		this.frm.refresh_field("base_paid_amount");
 	}
-});
+};
 
 // for backward compatibility: combine new and previous states
-$.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm}));
 
 cur_frm.cscript['Make Delivery Note'] = function() {
 	frappe.model.open_mapped_doc({
@@ -685,14 +685,16 @@
 	},
 
 	project: function(frm){
-		frm.call({
-			method: "add_timesheet_data",
-			doc: frm.doc,
-			callback: function(r, rt) {
-				refresh_field(['timesheets'])
-			}
-		})
-		frm.refresh();
+		if (!frm.doc.is_return) {
+			frm.call({
+				method: "add_timesheet_data",
+				doc: frm.doc,
+				callback: function(r, rt) {
+					refresh_field(['timesheets'])
+				}
+			})
+			frm.refresh();
+		}
 	},
 
 	onload: function(frm) {
@@ -807,14 +809,27 @@
 		}
 	},
 
+	add_timesheet_row: function(frm, row, exchange_rate) {
+		frm.add_child('timesheets', {
+			'activity_type': row.activity_type,
+			'description': row.description,
+			'time_sheet': row.parent,
+			'billing_hours': row.billing_hours,
+			'billing_amount': flt(row.billing_amount) * flt(exchange_rate),
+			'timesheet_detail': row.name
+		});
+		frm.refresh_field('timesheets');
+		calculate_total_billing_amount(frm);
+	},
+
 	refresh: function(frm) {
-		if (frm.doc.project) {
+		if (frm.doc.docstatus===0 && !frm.doc.is_return) {
 			frm.add_custom_button(__('Fetch Timesheet'), function() {
 				let d = new frappe.ui.Dialog({
 					title: __('Fetch Timesheet'),
 					fields: [
 						{
-							"label" : "From",
+							"label" : __("From"),
 							"fieldname": "from_time",
 							"fieldtype": "Date",
 							"reqd": 1,
@@ -824,11 +839,18 @@
 							fieldname: 'col_break_1',
 						},
 						{
-							"label" : "To",
+							"label" : __("To"),
 							"fieldname": "to_time",
 							"fieldtype": "Date",
 							"reqd": 1,
-						}
+						},
+						{
+							"label" : __("Project"),
+							"fieldname": "project",
+							"fieldtype": "Link",
+							"options": "Project",
+							"default": frm.doc.project
+						},
 					],
 					primary_action: function() {
 						let data = d.get_values();
@@ -837,27 +859,35 @@
 							args: {
 								from_time: data.from_time,
 								to_time: data.to_time,
-								project: frm.doc.project
+								project: data.project
 							},
 							callback: function(r) {
-								if(!r.exc) {
-									if(r.message.length > 0) {
-										frm.clear_table('timesheets')
-										r.message.forEach((d) => {
-											frm.add_child('timesheets',{
-												'time_sheet': d.parent,
-												'billing_hours': d.billing_hours,
-												'billing_amount': d.billing_amt,
-												'timesheet_detail': d.name
+								if (!r.exc && r.message.length > 0) {
+									frm.clear_table('timesheets')
+									r.message.forEach((d) => {
+										let exchange_rate = 1.0;
+										if (frm.doc.currency != d.currency) {
+											frappe.call({
+												method: 'erpnext.setup.utils.get_exchange_rate',
+												args: {
+													from_currency: d.currency,
+													to_currency: frm.doc.currency
+												},
+												callback: function(r) {
+													if (r.message) {
+														exchange_rate = r.message;
+														frm.events.add_timesheet_row(frm, d, exchange_rate);
+													}
+												}
 											});
-										});
-										frm.refresh_field('timesheets')
-									}
-									else {
-										frappe.msgprint(__('No Timesheet Found.'))
-									}
-									d.hide();
+										} else {
+											frm.events.add_timesheet_row(frm, d, exchange_rate);
+										}
+									});
+								} else {
+									frappe.msgprint(__('No Timesheets found with the selected filters.'))
 								}
+								d.hide();
 							}
 						});
 					},
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index c6c67b4..52bc409 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -748,6 +748,7 @@
   {
    "collapsible": 1,
    "collapsible_depends_on": "eval:doc.total_billing_amount > 0",
+   "depends_on": "eval: !doc.is_return",
    "fieldname": "time_sheet_list",
    "fieldtype": "Section Break",
    "hide_days": 1,
@@ -770,6 +771,7 @@
    "hide_days": 1,
    "hide_seconds": 1,
    "label": "Total Billing Amount",
+   "options": "currency",
    "print_hide": 1,
    "read_only": 1
   },
@@ -1953,6 +1955,12 @@
   },
   {
    "default": "0",
+   "fieldname": "is_debit_note",
+   "fieldtype": "Check",
+   "label": "Is Debit Note"
+  },
+  {
+   "default": "0",
    "depends_on": "grand_total",
    "fieldname": "disable_rounded_total",
    "fieldtype": "Check",
@@ -1969,7 +1977,7 @@
    "link_fieldname": "consolidated_invoice"
   }
  ],
- "modified": "2021-04-15 23:57:58.766651",
+ "modified": "2021-05-20 22:48:33.988881",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 4461f29..a008742 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -125,6 +125,8 @@
 		self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
 		if not self.is_return:
 			self.validate_serial_numbers()
+		else:
+			self.timesheets = []
 		self.update_packing_list()
 		self.set_billing_hours_and_amount()
 		self.update_timesheet_billing_for_project()
@@ -337,7 +339,7 @@
 
 		if "Healthcare" in active_domains:
 			manage_invoice_submit_cancel(self, "on_cancel")
-
+		self.unlink_sales_invoice_from_timesheets()
 		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
 
 	def update_status_updater_args(self):
@@ -393,6 +395,18 @@
 		if validate_against_credit_limit:
 			check_credit_limit(self.customer, self.company, bypass_credit_limit_check_at_sales_order)
 
+	def unlink_sales_invoice_from_timesheets(self):
+		for row in self.timesheets:
+			timesheet = frappe.get_doc('Timesheet', row.time_sheet)
+			for time_log in timesheet.time_logs:
+				if time_log.sales_invoice == self.name:
+					time_log.sales_invoice = None
+			timesheet.calculate_total_amounts()
+			timesheet.calculate_percentage_billed()
+			timesheet.flags.ignore_validate_update_after_submit = True
+			timesheet.set_status()
+			timesheet.db_update_all()
+
 	@frappe.whitelist()
 	def set_missing_values(self, for_validate=False):
 		pos = self.set_pos_fields(for_validate)
@@ -427,7 +441,7 @@
 				timesheet.calculate_percentage_billed()
 				timesheet.flags.ignore_validate_update_after_submit = True
 				timesheet.set_status()
-				timesheet.save()
+				timesheet.db_update_all()
 
 	def update_time_sheet_detail(self, timesheet, args, sales_invoice):
 		for data in timesheet.time_logs:
@@ -741,8 +755,10 @@
 				self.append('timesheets', {
 						'time_sheet': data.parent,
 						'billing_hours': data.billing_hours,
-						'billing_amount': data.billing_amt,
-						'timesheet_detail': data.name
+						'billing_amount': data.billing_amount,
+						'timesheet_detail': data.name,
+						'activity_type': data.activity_type,
+						'description': data.description
 					})
 
 			self.calculate_billing_amount_for_timesheet()
@@ -1111,7 +1127,7 @@
 			if not item.serial_no:
 				continue
 
-			for serial_no in item.serial_no.split("\n"):
+			for serial_no in get_serial_nos(item.serial_no):
 				if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code:
 					frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice)
 
@@ -1755,15 +1771,10 @@
 		item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item))
 
 def get_delivery_note_details(internal_reference):
-	so_item_map = {}
-
 	si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'],
 		filters={'parent': internal_reference})
 
-	for d in si_item_details:
-		so_item_map.setdefault(d.name, d.so_detail)
-
-	return so_item_map
+	return {d.name: d.so_detail for d in si_item_details if d.so_detail}
 
 def get_sales_invoice_details(internal_reference):
 	dn_item_map = {}
diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
index f7b9aef..f069e8d 100644
--- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
+++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
@@ -1,172 +1,78 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-06-14 19:21:34.321662", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2016-06-14 19:21:34.321662",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "activity_type",
+  "description",
+  "billing_hours",
+  "billing_amount",
+  "time_sheet",
+  "timesheet_detail"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "time_sheet", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Time Sheet", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Timesheet", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "time_sheet",
+   "fieldtype": "Link",
+   "in_global_search": 1,
+   "in_list_view": 1,
+   "label": "Time Sheet",
+   "options": "Timesheet",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "billing_hours", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Billing Hours", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_hours",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Billing Hours",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "billing_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Billing Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Billing Amount",
+   "options": "currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "timesheet_detail", 
-   "fieldtype": "Data", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Timesheet Detail", 
-   "length": 0, 
-   "no_copy": 1, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "allow_on_submit": 1,
+   "fieldname": "timesheet_detail",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Timesheet Detail",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "activity_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Activity Type",
+   "options": "Activity Type",
+   "read_only": 1
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "in_list_view": 1,
+   "label": "Description",
+   "read_only": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2019-02-18 18:50:44.770361", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Sales Invoice Timesheet", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-20 22:33:57.234846",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice Timesheet",
+ "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/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 09db7fe..5c1cbaa 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -21,7 +21,10 @@
 	else:
 		party_type = 'Supplier'
 		party = inv.supplier
-	
+
+	if not party:
+		frappe.throw(_("Please select {0} first").format(party_type))
+
 	return party_type, party
 
 def get_party_tax_withholding_details(inv, tax_withholding_category=None):
@@ -324,7 +327,7 @@
 		net_total, ldc.certificate_limit
 	):
 		tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
-	
+
 	return tds_amount
 
 def get_debit_note_amount(suppliers, fiscal_year_details, company=None):
diff --git a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json
index bd7a126..4c7faf4 100644
--- a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json
+++ b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json
@@ -1,5 +1,6 @@
 {
  "attach_print": 0,
+ "channel": "Email",
  "condition": "doc.auto_created",
  "creation": "2018-04-25 14:19:05.440361",
  "days_in_advance": 0,
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 444b40e..db605f7 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -364,7 +364,7 @@
 		payment_terms_details = frappe.db.sql("""
 			select
 				si.name, si.party_account_currency, si.currency, si.conversion_rate,
-				ps.due_date, ps.payment_amount, ps.description, ps.paid_amount, ps.discounted_amount
+				ps.due_date, ps.payment_term, ps.payment_amount, ps.description, ps.paid_amount, ps.discounted_amount
 			from `tab{0}` si, `tabPayment Schedule` ps
 			where
 				si.name = ps.parent and
@@ -394,7 +394,7 @@
 			"due_date": d.due_date,
 			"invoiced": invoiced,
 			"invoice_grand_total": row.invoiced,
-			"payment_term": d.description,
+			"payment_term": d.description or d.payment_term,
 			"paid": d.paid_amount + d.discounted_amount,
 			"credit_note": 0.0,
 			"outstanding": invoiced - d.paid_amount - d.discounted_amount
diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py
index 1729abc..26bb44f 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.py
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py
@@ -5,7 +5,8 @@
 import frappe
 from frappe import _
 from frappe.utils import flt, cint
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
+from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data,
+	get_filtered_list_for_consolidated_report)
 
 def execute(filters=None):
 	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
@@ -132,6 +133,10 @@
 	if filters.get('accumulated_values'):
 		period_list = [period_list[-1]]
 
+	# from consolidated financial statement
+	if filters.get('accumulated_in_group_company'):
+		period_list = get_filtered_list_for_consolidated_report(filters, period_list)
+
 	for period in period_list:
 		key = period if consolidated else period.key
 		if asset:
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py
index cf0946b..3577457 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/cash_flow.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe import _
 from frappe.utils import cint, cstr
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
+from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data, get_filtered_list_for_consolidated_report)
 from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss
 from erpnext.accounts.utils import get_fiscal_year
 from six import iteritems
@@ -67,9 +67,9 @@
 			section_data.append(account_data)
 
 		add_total_row_account(data, section_data, cash_flow_account['section_footer'],
-			period_list, company_currency, summary_data)
+			period_list, company_currency, summary_data, filters)
 
-	add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency, summary_data)
+	add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters)
 	columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
 
 	chart = get_chart_data(columns, data)
@@ -162,18 +162,26 @@
 
 	return start_date
 
-def add_total_row_account(out, data, label, period_list, currency, summary_data, consolidated = False):
+def add_total_row_account(out, data, label, period_list, currency, summary_data, filters, consolidated=False):
 	total_row = {
 		"account_name": "'" + _("{0}").format(label) + "'",
 		"account": "'" + _("{0}").format(label) + "'",
 		"currency": currency
 	}
+
+	summary_data[label] = 0
+
+	# from consolidated financial statement
+	if filters.get('accumulated_in_group_company'):
+		period_list = get_filtered_list_for_consolidated_report(filters, period_list)
+
 	for row in data:
 		if row.get("parent_account"):
 			for period in period_list:
 				key = period if consolidated else period['key']
 				total_row.setdefault(key, 0.0)
 				total_row[key] += row.get(key, 0.0)
+				summary_data[label] += row.get(key)
 
 			total_row.setdefault("total", 0.0)
 			total_row["total"] += row["total"]
@@ -181,7 +189,6 @@
 	out.append(total_row)
 	out.append({})
 
-	summary_data[label] = total_row["total"]
 
 def get_report_summary(summary_data, currency):
 	report_summary = []
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 0947922..1363b53 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
@@ -2,118 +2,128 @@
 // For license information, please see license.txt
 /* eslint-disable */
 
-frappe.query_reports["Consolidated Financial Statement"] = {
-	"filters": [
-		{
-			"fieldname":"company",
-			"label": __("Company"),
-			"fieldtype": "Link",
-			"options": "Company",
-			"default": frappe.defaults.get_user_default("Company"),
-			"reqd": 1
-		},
-		{
-			"fieldname":"filter_based_on",
-			"label": __("Filter Based On"),
-			"fieldtype": "Select",
-			"options": ["Fiscal Year", "Date Range"],
-			"default": ["Fiscal Year"],
-			"reqd": 1,
-			on_change: function() {
-				let filter_based_on = frappe.query_report.get_filter_value('filter_based_on');
-				frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range');
-				frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range');
-				frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year');
-				frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year');
+frappe.require("assets/erpnext/js/financial_statements.js", function() {
+	frappe.query_reports["Consolidated Financial Statement"] = {
+		"filters": [
+			{
+				"fieldname":"company",
+				"label": __("Company"),
+				"fieldtype": "Link",
+				"options": "Company",
+				"default": frappe.defaults.get_user_default("Company"),
+				"reqd": 1
+			},
+			{
+				"fieldname":"filter_based_on",
+				"label": __("Filter Based On"),
+				"fieldtype": "Select",
+				"options": ["Fiscal Year", "Date Range"],
+				"default": ["Fiscal Year"],
+				"reqd": 1,
+				on_change: function() {
+					let filter_based_on = frappe.query_report.get_filter_value('filter_based_on');
+					frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range');
+					frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range');
+					frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year');
+					frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year');
 
-				frappe.query_report.refresh();
+					frappe.query_report.refresh();
+				}
+			},
+			{
+				"fieldname":"period_start_date",
+				"label": __("Start Date"),
+				"fieldtype": "Date",
+				"hidden": 1,
+				"reqd": 1
+			},
+			{
+				"fieldname":"period_end_date",
+				"label": __("End Date"),
+				"fieldtype": "Date",
+				"hidden": 1,
+				"reqd": 1
+			},
+			{
+				"fieldname":"from_fiscal_year",
+				"label": __("Start Year"),
+				"fieldtype": "Link",
+				"options": "Fiscal Year",
+				"default": frappe.defaults.get_user_default("fiscal_year"),
+				"reqd": 1
+			},
+			{
+				"fieldname":"to_fiscal_year",
+				"label": __("End Year"),
+				"fieldtype": "Link",
+				"options": "Fiscal Year",
+				"default": frappe.defaults.get_user_default("fiscal_year"),
+				"reqd": 1
+			},
+			{
+				"fieldname":"finance_book",
+				"label": __("Finance Book"),
+				"fieldtype": "Link",
+				"options": "Finance Book"
+			},
+			{
+				"fieldname":"report",
+				"label": __("Report"),
+				"fieldtype": "Select",
+				"options": ["Profit and Loss Statement", "Balance Sheet", "Cash Flow"],
+				"default": "Balance Sheet",
+				"reqd": 1
+			},
+			{
+				"fieldname": "presentation_currency",
+				"label": __("Currency"),
+				"fieldtype": "Select",
+				"options": erpnext.get_presentation_currency_list(),
+				"default": frappe.defaults.get_user_default("Currency")
+			},
+			{
+				"fieldname":"accumulated_in_group_company",
+				"label": __("Accumulated Values in Group Company"),
+				"fieldtype": "Check",
+				"default": 0
+			},
+			{
+				"fieldname": "include_default_book_entries",
+				"label": __("Include Default Book Entries"),
+				"fieldtype": "Check",
+				"default": 1
 			}
-		},
-		{
-			"fieldname":"period_start_date",
-			"label": __("Start Date"),
-			"fieldtype": "Date",
-			"hidden": 1,
-			"reqd": 1
-		},
-		{
-			"fieldname":"period_end_date",
-			"label": __("End Date"),
-			"fieldtype": "Date",
-			"hidden": 1,
-			"reqd": 1
-		},
-		{
-			"fieldname":"from_fiscal_year",
-			"label": __("Start Year"),
-			"fieldtype": "Link",
-			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
-			"reqd": 1
-		},
-		{
-			"fieldname":"to_fiscal_year",
-			"label": __("End Year"),
-			"fieldtype": "Link",
-			"options": "Fiscal Year",
-			"default": frappe.defaults.get_user_default("fiscal_year"),
-			"reqd": 1
-		},
-		{
-			"fieldname":"finance_book",
-			"label": __("Finance Book"),
-			"fieldtype": "Link",
-			"options": "Finance Book"
-		},
-		{
-			"fieldname":"report",
-			"label": __("Report"),
-			"fieldtype": "Select",
-			"options": ["Profit and Loss Statement", "Balance Sheet", "Cash Flow"],
-			"default": "Balance Sheet",
-			"reqd": 1
-		},
-		{
-			"fieldname": "presentation_currency",
-			"label": __("Currency"),
-			"fieldtype": "Select",
-			"options": erpnext.get_presentation_currency_list(),
-			"default": frappe.defaults.get_user_default("Currency")
-		},
-		{
-			"fieldname":"accumulated_in_group_company",
-			"label": __("Accumulated Values in Group Company"),
-			"fieldtype": "Check",
-			"default": 0
-		},
-		{
-			"fieldname": "include_default_book_entries",
-			"label": __("Include Default Book Entries"),
-			"fieldtype": "Check",
-			"default": 1
-		}
-	],
-	"formatter": function(value, row, column, data, default_formatter) {
-		value = default_formatter(value, row, column, data);
+		],
+		"formatter": function(value, row, column, data, default_formatter) {			
+			if (data && column.fieldname=="account") {
+				value = data.account_name || value;
+				
+				column.link_onclick =
+				"erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
+				column.is_tree = true;
+			}
 
-		if (!data.parent_account) {
-			value = $(`<span>${value}</span>`);
+			value = default_formatter(value, row, column, data);
 
-			var $value = $(value).css("font-weight", "bold");
+			if (!data.parent_account) {
+				value = $(`<span>${value}</span>`);
 
-			value = $value.wrap("<p></p>").parent().html();
-		}
-		return value;
-	},
-	onload: function() {
-		let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
+				var $value = $(value).css("font-weight", "bold");
 
-		frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
-			var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
-			frappe.query_report.set_filter_value({
-				period_start_date: fy.year_start_date,
-				period_end_date: fy.year_end_date
+				value = $value.wrap("<p></p>").parent().html();
+			}
+			return value;
+		},
+		onload: function() {
+			let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
+
+			frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
+				var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
+				frappe.query_report.set_filter_value({
+					period_start_date: fy.year_start_date,
+					period_end_date: fy.year_end_date
+				});
 			});
-		});
+		}
 	}
-}
+});
\ No newline at end of file
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index 0c4a422..7793af7 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -94,7 +94,7 @@
 
 	chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
 
-	report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, True)
+	report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, filters, True)
 
 	return data, None, chart, report_summary
 
@@ -149,9 +149,9 @@
 			section_data.append(account_data)
 
 		add_total_row_account(data, section_data, cash_flow_account['section_footer'],
-			companies, company_currency, summary_data, True)
+			companies, company_currency, summary_data, filters, True)
 
-	add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, summary_data, True)
+	add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, summary_data, filters, True)
 
 	report_summary = get_cash_flow_summary(summary_data, company_currency)
 
@@ -329,8 +329,9 @@
 		has_value = False
 		total = 0
 		row = frappe._dict({
-			"account_name": _(d.account_name),
-			"account": _(d.account_name),
+			"account_name": ('%s - %s' %(_(d.account_number), _(d.account_name))
+				if d.account_number else _(d.account_name)),
+			"account": _(d.name),
 			"parent_account": _(d.parent_account),
 			"indent": flt(d.indent),
 			"year_start_date": start_date,
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 14efa1f..d20ddbd 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -119,10 +119,10 @@
 
 def validate_dates(from_date, to_date):
 	if not from_date or not to_date:
-		frappe.throw("From Date and To Date are mandatory")
+		frappe.throw(_("From Date and To Date are mandatory"))
 
 	if to_date < from_date:
-		frappe.throw("To Date cannot be less than From Date")
+		frappe.throw(_("To Date cannot be less than From Date"))
 
 def get_months(start_date, end_date):
 	diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month)
@@ -522,4 +522,12 @@
 				"width": 150
 			})
 
-	return columns
\ No newline at end of file
+	return columns
+
+def get_filtered_list_for_consolidated_report(filters, period_list):
+	filtered_summary_list = []
+	for period in period_list:
+		if period == filters.get('company'):
+			filtered_summary_list.append(period)
+
+	return filtered_summary_list
diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py
index 52f7fe2..cfbd7fd 100644
--- a/erpnext/accounts/report/pos_register/pos_register.py
+++ b/erpnext/accounts/report/pos_register/pos_register.py
@@ -116,22 +116,19 @@
 		frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method"))
 
 def get_conditions(filters):
-	conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s".format(
-		company=filters.get("company"),
-		from_date=filters.get("from_date"),
-		to_date=filters.get("to_date"))
+	conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s"
 
 	if filters.get("pos_profile"):
-		conditions += " AND pos_profile = %(pos_profile)s".format(pos_profile=filters.get("pos_profile"))
+		conditions += " AND pos_profile = %(pos_profile)s"
 	
 	if filters.get("owner"):
-		conditions += " AND owner = %(owner)s".format(owner=filters.get("owner"))
+		conditions += " AND owner = %(owner)s"
 	
 	if filters.get("customer"):
-		conditions += " AND customer = %(customer)s".format(customer=filters.get("customer"))
+		conditions += " AND customer = %(customer)s"
 	
 	if filters.get("is_return"):
-		conditions += " AND is_return = %(is_return)s".format(is_return=filters.get("is_return"))
+		conditions += " AND is_return = %(is_return)s"
 	
 	if filters.get("mode_of_payment"):
 		conditions += """
diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
index fe261b3..5d04824 100644
--- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
+++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
@@ -5,7 +5,8 @@
 import frappe
 from frappe import _
 from frappe.utils import flt
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
+from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data,
+	get_filtered_list_for_consolidated_report)
 
 def execute(filters=None):
 	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
@@ -33,13 +34,17 @@
 	chart = get_chart_data(filters, columns, income, expense, net_profit_loss)
 
 	currency = filters.presentation_currency or frappe.get_cached_value('Company', filters.company, "default_currency")
-	report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, currency)
+	report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters)
 
 	return columns, data, None, chart, report_summary
 
-def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, currency, consolidated=False):
+def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, currency, filters, consolidated=False):
 	net_income, net_expense, net_profit = 0.0, 0.0, 0.0
 
+	# from consolidated financial statement
+	if filters.get('accumulated_in_group_company'):
+		period_list = get_filtered_list_for_consolidated_report(filters, period_list)
+
 	for period in period_list:
 		key = period if consolidated else period.key
 		if income:
diff --git a/erpnext/accounts/report/tax_detail/__init__.py b/erpnext/accounts/report/tax_detail/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/report/tax_detail/__init__.py
diff --git a/erpnext/accounts/report/tax_detail/tax_detail.js b/erpnext/accounts/report/tax_detail/tax_detail.js
new file mode 100644
index 0000000..ed6fac4
--- /dev/null
+++ b/erpnext/accounts/report/tax_detail/tax_detail.js
@@ -0,0 +1,451 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+// Contributed by Case Solved and sponsored by Nulight Studios
+/* eslint-disable */
+
+frappe.provide('frappe.query_reports');
+
+frappe.query_reports["Tax Detail"] = {
+	filters: [
+		{
+			fieldname: "company",
+			label: __("Company"),
+			fieldtype: "Link",
+			options: "Company",
+			default: frappe.defaults.get_user_default("company"),
+			reqd: 1
+		},
+		{
+			fieldname: "from_date",
+			label: __("From Date"),
+			fieldtype: "Date",
+			default: frappe.datetime.month_start(frappe.datetime.get_today()),
+			reqd: 1,
+			width: "60px"
+		},
+		{
+			fieldname: "to_date",
+			label: __("To Date"),
+			fieldtype: "Date",
+			default: frappe.datetime.month_end(frappe.datetime.get_today()),
+			reqd: 1,
+			width: "60px"
+		},
+		{
+			fieldname: "report_name",
+			label: __("Report Name"),
+			fieldtype: "Read Only",
+			default: frappe.query_report.report_name,
+			hidden: 1,
+			reqd: 1
+		},
+		{
+			fieldname: "mode",
+			label: __("Mode"),
+			fieldtype: "Read Only",
+			default: "edit",
+			hidden: 1,
+			reqd: 1
+		}
+	],
+	onload: function onload(report) {
+		// Remove Add Column and Save from menu
+		report.page.add_inner_button(__("New Report"), () => new_report(), __("Custom Report"));
+		report.page.add_inner_button(__("Load Report"), () => load_report(), __("Custom Report"));
+		hide_filters(report);
+	}
+};
+
+function hide_filters(report) {
+	report.page.page_form[0].querySelectorAll('.form-group.frappe-control').forEach(function setHidden(field) {
+		if (field.dataset.fieldtype == "Read Only") {
+			field.classList.add("hidden");
+		}
+	});
+}
+
+erpnext.TaxDetail = class TaxDetail {
+	constructor() {
+		this.patch();
+		this.load_report();
+	}
+	// Monkey patch the QueryReport class
+	patch() {
+		this.qr = frappe.query_report;
+		this.super = {
+			refresh_report: this.qr.refresh_report,
+			show_footer_message: this.qr.show_footer_message
+		}
+		this.qr.refresh_report = () => this.refresh_report();
+		this.qr.show_footer_message = () => this.show_footer_message();
+	}
+	show_footer_message() {
+		// The last thing to run after datatable_render in refresh()
+		this.super.show_footer_message.apply(this.qr);
+		if (this.qr.report_name !== 'Tax Detail') {
+			this.show_help();
+			if (this.loading) {
+				this.set_section('');
+			} else {
+				this.reload_component('');
+			}
+		}
+		this.loading = false;
+	}
+	refresh_report() {
+		// Infrequent report build (onload), load filters & data
+		// super function runs a refresh() serially
+		// already run within frappe.run_serially
+		this.loading = true;
+		this.super.refresh_report.apply(this.qr);
+		if (this.qr.report_name !== 'Tax Detail') {
+			frappe.call({
+				method: 'erpnext.accounts.report.tax_detail.tax_detail.get_custom_reports',
+				args: {name: this.qr.report_name}
+			}).then((r) => {
+				const data = JSON.parse(r.message[this.qr.report_name]['json']);
+				this.create_controls();
+				this.sections = data.sections || {};
+				this.controls['show_detail'].set_input(data.show_detail);
+			});
+		}
+	}
+	load_report() {
+		// One-off report build like titles, menu, etc
+		// Run when this object is created which happens in qr.load_report
+		this.qr.menu_items = this.get_menu_items();
+	}
+	get_menu_items() {
+		// Replace Save action
+		let new_items = [];
+		const save = __('Save');
+
+		for (let item of this.qr.menu_items) {
+			if (item.label === save) {
+				new_items.push({
+					label: save,
+					action: () => this.save_report(),
+					standard: false
+				});
+			} else {
+				new_items.push(item);
+			}
+		}
+		return new_items;
+	}
+	save_report() {
+		this.check_datatable();
+		if (this.qr.report_name !== 'Tax Detail') {
+			frappe.call({
+				method:'erpnext.accounts.report.tax_detail.tax_detail.save_custom_report',
+				args: {
+					reference_report: 'Tax Detail',
+					report_name: this.qr.report_name,
+					data: {
+						columns: this.qr.get_visible_columns(),
+						sections: this.sections,
+						show_detail: this.controls['show_detail'].get_input_value()
+					}
+				},
+				freeze: true
+			}).then((r) => {
+				this.set_section('');
+			});
+		}
+	}
+	check_datatable() {
+		if (!this.qr.datatable) {
+			frappe.throw(__('Please change the date range to load data first'));
+		}
+	}
+	set_section(name) {
+		// Sets the given section name and then reloads the data
+		if (name && !this.sections[name]) {
+			this.sections[name] = {};
+		}
+		let options = Object.keys(this.sections);
+		options.unshift('');
+		this.controls['section_name'].$wrapper.find("select").empty().add_options(options);
+		const org_mode = this.qr.get_filter_value('mode');
+		let refresh = false;
+		if (name) {
+			this.controls['section_name'].set_input(name);
+			this.qr.set_filter_value('mode', 'edit');
+			if (org_mode === 'run') {
+				refresh = true;
+			}
+		} else {
+			this.controls['section_name'].set_input('');
+			this.qr.set_filter_value('mode', 'run');
+			if (org_mode === 'edit') {
+				refresh = true;
+			}
+		}
+		if (refresh) {
+			this.qr.refresh();
+		}
+		this.reload_component('');
+	}
+	reload_component(component_name) {
+		const section_name = this.controls['section_name'].get_input_value();
+		if (section_name) {
+			const section = this.sections[section_name];
+			const component_names = Object.keys(section);
+			component_names.unshift('');
+			this.controls['component'].$wrapper.find("select").empty().add_options(component_names);
+			this.controls['component'].set_input(component_name);
+			if (component_name) {
+				this.controls['component_type'].set_input(section[component_name].type);
+			}
+		} else {
+			this.controls['component'].$wrapper.find("select").empty();
+			this.controls['component'].set_input('');
+		}
+		this.set_table_filters();
+	}
+	set_table_filters() {
+		let filters = {};
+		const section_name = this.controls['section_name'].get_input_value();
+		const component_name = this.controls['component'].get_input_value();
+		if (section_name && component_name) {
+			const component_type = this.sections[section_name][component_name].type;
+			if (component_type === 'filter') {
+				filters = this.sections[section_name][component_name]['filters'];
+			}
+		}
+		this.setAppliedFilters(filters);
+	}
+	setAppliedFilters(filters) {
+		if (this.qr.datatable) {
+			Array.from(this.qr.datatable.header.querySelectorAll('.dt-filter')).map(function setFilters(input) {
+				let idx = input.dataset.colIndex;
+				if (filters[idx]) {
+					input.value = filters[idx];
+				} else {
+					input.value = null;
+				}
+			});
+			this.qr.datatable.columnmanager.applyFilter(filters);
+		}
+	}
+	delete(name, type) {
+		if (type === 'section') {
+			delete this.sections[name];
+			const new_section = Object.keys(this.sections)[0] || '';
+			this.set_section(new_section);
+		}
+		if (type === 'component') {
+			const cur_section = this.controls['section_name'].get_input_value();
+			delete this.sections[cur_section][name];
+			this.reload_component('');
+		}
+	}
+	create_controls() {
+		let controls = {};
+		// SELECT in data.js
+		controls['section_name'] = this.qr.page.add_field({
+			label: __('Section'),
+			fieldtype: 'Select',
+			fieldname: 'section_name',
+			change: (e) => {
+				this.set_section(this.controls['section_name'].get_input_value());
+			}
+		});
+		// BUTTON in button.js
+		controls['new_section'] = this.qr.page.add_field({
+			label: __('New Section'),
+			fieldtype: 'Button',
+			fieldname: 'new_section',
+			click: () => {
+				frappe.prompt({
+					label: __('Section Name'),
+					fieldname: 'name',
+					fieldtype: 'Data'
+				}, (values) => {
+					this.set_section(values.name);
+				});
+			}
+		});
+		controls['delete_section'] = this.qr.page.add_field({
+			label: __('Delete Section'),
+			fieldtype: 'Button',
+			fieldname: 'delete_section',
+			click: () => {
+				let cur_section = this.controls['section_name'].get_input_value();
+				if (cur_section) {
+					frappe.confirm(__('Are you sure you want to delete section') + ' ' + cur_section + '?',
+					() => {this.delete(cur_section, 'section')});
+				}
+			}
+		});
+		controls['component'] = this.qr.page.add_field({
+			label: __('Component'),
+			fieldtype: 'Select',
+			fieldname: 'component',
+			change: (e) => {
+				this.reload_component(this.controls['component'].get_input_value());
+			}
+		});
+		controls['component_type'] = this.qr.page.add_field({
+			label: __('Component Type'),
+			fieldtype: 'Select',
+			fieldname: 'component_type',
+			default: 'filter',
+			options: [
+				{label: __('Filtered Row Subtotal'), value: 'filter'},
+				{label: __('Section Subtotal'), value: 'section'}
+			]
+		});
+		controls['add_component'] = this.qr.page.add_field({
+			label: __('Add Component'),
+			fieldtype: 'Button',
+			fieldname: 'add_component',
+			click: () => {
+				this.check_datatable();
+				let section_name = this.controls['section_name'].get_input_value();
+				if (section_name) {
+					const component_type = this.controls['component_type'].get_input_value();
+					let idx = 0;
+					const names = Object.keys(this.sections[section_name]);
+					if (names.length > 0) {
+						const idxs = names.map((key) => parseInt(key.match(/\d+$/)) || 0);
+						idx = Math.max(...idxs) + 1;
+					}
+					const filters = this.qr.datatable.columnmanager.getAppliedFilters();
+					if (component_type === 'filter') {
+						const name = 'Filter' + idx.toString();
+						let data = {
+							type: component_type,
+							filters: filters
+						}
+						this.sections[section_name][name] = data;
+						this.reload_component(name);
+					} else if (component_type === 'section') {
+						if (filters && Object.keys(filters).length !== 0) {
+							frappe.show_alert({
+								message: __('Column filters ignored'),
+								indicator: 'yellow'
+							});
+						}
+						let data = {
+							type: component_type
+						}
+						frappe.prompt({
+							label: __('Section'),
+							fieldname: 'section',
+							fieldtype: 'Select',
+							options: Object.keys(this.sections)
+						}, (values) => {
+							this.sections[section_name][values.section] = data;
+							this.reload_component(values.section);
+						});
+					} else {
+						frappe.throw(__('Please select the Component Type first'));
+					}
+				} else {
+					frappe.throw(__('Please select the Section first'));
+				}
+			}
+		});
+		controls['delete_component'] = this.qr.page.add_field({
+			label: __('Delete Component'),
+			fieldtype: 'Button',
+			fieldname: 'delete_component',
+			click: () => {
+				const component = this.controls['component'].get_input_value();
+				if (component) {
+					frappe.confirm(__('Are you sure you want to delete component') + ' ' + component + '?',
+					() => {this.delete(component, 'component')});
+				}
+			}
+		});
+		controls['save'] = this.qr.page.add_field({
+			label: __('Save & Run'),
+			fieldtype: 'Button',
+			fieldname: 'save',
+			click: () => {
+				this.save_report();
+			}
+		});
+		controls['show_detail'] = this.qr.page.add_field({
+			label: __('Show Detail'),
+			fieldtype: 'Check',
+			fieldname: 'show_detail',
+			default: 1
+		});
+		this.controls = controls;
+	}
+	show_help() {
+		const help = __('Your custom report is built from General Ledger Entries within the date range. You can add multiple sections to the report using the New Section button. Each component added to a section adds a subset of the data into the specified section. Beware of duplicated data rows. The Filtered Row component type saves the datatable column filters to specify the added data. The Section component type refers to the data in a previously defined section, but it cannot refer to its parent section. The Amount column is summed to give the section subtotal. Use the Show Detail box to see the data rows included in each section in the final report. Once finished, hit Save & Run. Report contributed by');
+		this.qr.$report_footer.append('<div class="col-md-12"><strong>' + __('Help') + `: </strong>${help}<a href="https://www.casesolved.co.uk"> Case Solved</a></div>`);
+	}
+}
+
+if (!window.taxdetail) {
+	window.taxdetail = new erpnext.TaxDetail();
+}
+
+function get_reports(cb) {
+	frappe.call({
+		method: 'erpnext.accounts.report.tax_detail.tax_detail.get_custom_reports',
+		freeze: true
+	}).then((r) => {
+		cb(r.message);
+	})
+}
+
+function new_report() {
+	const dialog = new frappe.ui.Dialog({
+		title: __('New Report'),
+		fields: [
+			{
+				fieldname: 'report_name',
+				label: __('Report Name'),
+				fieldtype: 'Data',
+				default: 'VAT Return'
+			}
+		],
+		primary_action_label: __('Create'),
+		primary_action: function new_report_pa(values) {
+			frappe.call({
+				method:'erpnext.accounts.report.tax_detail.tax_detail.save_custom_report',
+				args: {
+					reference_report: 'Tax Detail',
+					report_name: values.report_name,
+					data: {
+						columns: [],
+						sections: {},
+						show_detail: 1
+					}
+				},
+				freeze: true
+			}).then((r) => {
+				frappe.set_route('query-report', values.report_name);
+			});
+			dialog.hide();
+		}
+	});
+	dialog.show();
+}
+
+function load_report() {
+	get_reports(function load_report_cb(reports) {
+		const dialog = new frappe.ui.Dialog({
+			title: __('Load Report'),
+			fields: [
+				{
+					fieldname: 'report_name',
+					label: __('Report Name'),
+					fieldtype: 'Select',
+					options: Object.keys(reports)
+				}
+			],
+			primary_action_label: __('Load'),
+			primary_action: function load_report_pa(values) {
+				dialog.hide();
+				frappe.set_route('query-report', values.report_name);
+			}
+		});
+		dialog.show();
+	});
+}
diff --git a/erpnext/accounts/report/tax_detail/tax_detail.json b/erpnext/accounts/report/tax_detail/tax_detail.json
new file mode 100644
index 0000000..d52ffd0
--- /dev/null
+++ b/erpnext/accounts/report/tax_detail/tax_detail.json
@@ -0,0 +1,32 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-02-19 16:44:21.175113",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-02-19 16:44:21.175113",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Tax Detail",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "GL Entry",
+ "report_name": "Tax Detail",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Accounts User"
+  },
+  {
+   "role": "Accounts Manager"
+  },
+  {
+   "role": "Auditor"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/tax_detail/tax_detail.py b/erpnext/accounts/report/tax_detail/tax_detail.py
new file mode 100644
index 0000000..18436de
--- /dev/null
+++ b/erpnext/accounts/report/tax_detail/tax_detail.py
@@ -0,0 +1,296 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+# Contributed by Case Solved and sponsored by Nulight Studios
+
+from __future__ import unicode_literals
+import frappe
+import json
+from frappe import _
+
+# NOTE: Payroll is implemented using Journal Entries which are included as GL Entries
+
+# field lists in multiple doctypes will be coalesced
+required_sql_fields = {
+	("GL Entry", 1): ["posting_date"],
+	("Account",): ["root_type", "account_type"],
+	("GL Entry", 2): ["account", "voucher_type", "voucher_no", "debit", "credit"],
+	("Purchase Invoice Item", "Sales Invoice Item"): ["base_net_amount", "item_tax_rate", "item_tax_template", "item_group", "item_name"],
+	("Purchase Invoice", "Sales Invoice"): ["taxes_and_charges", "tax_category"],
+}
+
+
+def execute(filters=None):
+	if not filters:
+		return [], []
+
+	fieldlist = required_sql_fields
+	fieldstr = get_fieldstr(fieldlist)
+
+	gl_entries = frappe.db.sql("""
+		select {fieldstr}
+		from `tabGL Entry` ge
+		inner join `tabAccount` a on
+			ge.account=a.name and ge.company=a.company
+		left join `tabSales Invoice` si on
+			ge.company=si.company and ge.voucher_type='Sales Invoice' and ge.voucher_no=si.name
+		left join `tabSales Invoice Item` sii on
+			a.root_type='Income' and si.name=sii.parent
+		left join `tabPurchase Invoice` pi on
+			ge.company=pi.company and ge.voucher_type='Purchase Invoice' and ge.voucher_no=pi.name
+		left join `tabPurchase Invoice Item` pii on
+			a.root_type='Expense' and pi.name=pii.parent
+		where
+			ge.company=%(company)s and
+			ge.posting_date>=%(from_date)s and
+			ge.posting_date<=%(to_date)s
+		order by ge.posting_date, ge.voucher_no
+		""".format(fieldstr=fieldstr), filters, as_dict=1)
+
+	report_data = modify_report_data(gl_entries)
+	summary = None
+	if filters['mode'] == 'run' and filters['report_name'] != 'Tax Detail':
+		report_data, summary = run_report(filters['report_name'], report_data)
+
+	# return columns, data, message, chart, report_summary
+	return get_columns(fieldlist), report_data, None, None, summary
+
+def run_report(report_name, data):
+	"Applies the sections and filters saved in the custom report"
+	report_config = json.loads(frappe.get_doc('Report', report_name).json)
+	# Columns indexed from 1 wrt colno
+	columns = report_config.get('columns')
+	sections = report_config.get('sections', {})
+	show_detail = report_config.get('show_detail', 1)
+	report = {}
+	new_data = []
+	summary = []
+	for section_name, section in sections.items():
+		report[section_name] = {'rows': [], 'subtotal': 0.0}
+		for component_name, component in section.items():
+			if component['type'] == 'filter':
+				for row in data:
+					matched = True
+					for colno, filter_string in component['filters'].items():
+						filter_field = columns[int(colno) - 1]['fieldname']
+						if not filter_match(row[filter_field], filter_string):
+							matched = False
+							break
+					if matched:
+						report[section_name]['rows'] += [row]
+						report[section_name]['subtotal'] += row['amount']
+			if component['type'] == 'section':
+				if component_name == section_name:
+					frappe.throw(_("A report component cannot refer to its parent section") + ": " + section_name)
+				try:
+					report[section_name]['rows'] += report[component_name]['rows']
+					report[section_name]['subtotal'] += report[component_name]['subtotal']
+				except KeyError:
+					frappe.throw(_("A report component can only refer to an earlier section") + ": " + section_name)
+
+		if show_detail:
+			new_data += report[section_name]['rows']
+		new_data += [{'voucher_no': section_name, 'amount': report[section_name]['subtotal']}]
+		summary += [{'label': section_name, 'datatype': 'Currency', 'value': report[section_name]['subtotal']}]
+		if show_detail:
+			new_data += [{}]
+	return new_data or data, summary or None
+
+def filter_match(value, string):
+	"Approximation to datatable filters"
+	import datetime
+	if string == '':
+		return True
+	if value is None:
+		value = -999999999999999
+	elif isinstance(value, datetime.date):
+		return True
+
+	if isinstance(value, str):
+		value = value.lower()
+		string = string.lower()
+		if string[0] == '<':
+			return True if string[1:].strip() else False
+		elif string[0] == '>':
+			return False if string[1:].strip() else True
+		elif string[0] == '=':
+			return string[1:] in value if string[1:] else False
+		elif string[0:2] == '!=':
+			return string[2:] not in value
+		elif len(string.split(':')) == 2:
+			pre, post = string.split(':')
+			return (True if not pre.strip() and post.strip() in value else False)
+		else:
+			return string in value
+	else:
+		if string[0] in ['<', '>', '=']:
+			operator = string[0]
+			if operator == '=':
+				operator = '=='
+			string = string[1:].strip()
+		elif string[0:2] == '!=':
+			operator = '!='
+			string = string[2:].strip()
+		elif len(string.split(':')) == 2:
+			pre, post = string.split(':')
+			try:
+				return (True if float(pre) <= value and float(post) >= value else False)
+			except ValueError:
+				return (False if pre.strip() else True)
+		else:
+			return string in str(value)
+
+	try:
+		num = float(string) if string.strip() else 0
+		return frappe.safe_eval(f'{value} {operator} {num}')
+	except ValueError:
+		if operator == '<':
+			return True
+		return False
+
+
+def abbrev(dt):
+	return ''.join(l[0].lower() for l in dt.split(' ')) + '.'
+
+def doclist(dt, dfs):
+	return [abbrev(dt) + f for f in dfs]
+
+def as_split(fields):
+	for field in fields:
+		split = field.split(' as ')
+		yield (split[0], split[1] if len(split) > 1 else split[0])
+
+def coalesce(doctypes, fields):
+	coalesce = []
+	for name, new_name in as_split(fields):
+		sharedfields = ', '.join(abbrev(dt) + name for dt in doctypes)
+		coalesce += [f'coalesce({sharedfields}) as {new_name}']
+	return coalesce
+
+def get_fieldstr(fieldlist):
+	fields = []
+	for doctypes, docfields in fieldlist.items():
+		if len(doctypes) == 1 or isinstance(doctypes[1], int):
+			fields += doclist(doctypes[0], docfields)
+		else:
+			fields += coalesce(doctypes, docfields)
+	return ', '.join(fields)
+
+def get_columns(fieldlist):
+	columns = {}
+	for doctypes, docfields in fieldlist.items():
+		fieldmap = {name: new_name for name, new_name in as_split(docfields)}
+		for doctype in doctypes:
+			if isinstance(doctype, int):
+				break
+			meta = frappe.get_meta(doctype)
+			# get column field metadata from the db
+			fieldmeta = {}
+			for field in meta.get('fields'):
+				if field.fieldname in fieldmap.keys():
+					new_name = fieldmap[field.fieldname]
+					fieldmeta[new_name] = {
+						"label": _(field.label),
+						"fieldname": new_name,
+						"fieldtype": field.fieldtype,
+						"options": field.options
+					}
+			# edit the columns to match the modified data
+			for field in fieldmap.values():
+				col = modify_report_columns(doctype, field, fieldmeta[field])
+				if col:
+					columns[col["fieldname"]] = col
+	# use of a dict ensures duplicate columns are removed
+	return list(columns.values())
+
+def modify_report_columns(doctype, field, column):
+	"Because data is rearranged into other columns"
+	if doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
+		if field in ["item_tax_rate", "base_net_amount"]:
+			return None
+
+	if doctype == "GL Entry" and field in ["debit", "credit"]:
+		column.update({"label": _("Amount"), "fieldname": "amount"})
+
+	if field == "taxes_and_charges":
+		column.update({"label": _("Taxes and Charges Template")})
+	return column
+
+def modify_report_data(data):
+	import json
+	new_data = []
+	for line in data:
+		if line.debit:
+			line.amount = -line.debit
+		else:
+			line.amount = line.credit
+		# Remove Invoice GL Tax Entries and generate Tax entries from the invoice lines
+		if "Invoice" in line.voucher_type:
+			if line.account_type not in ("Tax", "Round Off"):
+				new_data += [line]
+				if line.item_tax_rate:
+					tax_rates = json.loads(line.item_tax_rate)
+					for account, rate in tax_rates.items():
+						tax_line = line.copy()
+						tax_line.account_type = "Tax"
+						tax_line.account = account
+						if line.voucher_type == "Sales Invoice":
+							line.amount = line.base_net_amount
+							tax_line.amount = line.base_net_amount * (rate / 100)
+						if line.voucher_type == "Purchase Invoice":
+							line.amount = -line.base_net_amount
+							tax_line.amount = -line.base_net_amount * (rate / 100)
+						new_data += [tax_line]
+		else:
+			new_data += [line]
+	return new_data
+
+
+# JS client utilities
+
+custom_report_dict = {
+	'ref_doctype': 'GL Entry',
+	'report_type': 'Custom Report',
+	'reference_report': 'Tax Detail'
+}
+
+@frappe.whitelist()
+def get_custom_reports(name=None):
+	filters = custom_report_dict.copy()
+	if name:
+		filters['name'] = name
+	reports = frappe.get_list('Report',
+		filters = filters,
+		fields = ['name', 'json'],
+		as_list=False
+	)
+	reports_dict = {rep.pop('name'): rep for rep in reports}
+	# Prevent custom reports with the same name
+	reports_dict['Tax Detail'] = {'json': None}
+	return reports_dict
+
+@frappe.whitelist()
+def save_custom_report(reference_report, report_name, data):
+	if reference_report != 'Tax Detail':
+		frappe.throw(_("The wrong report is referenced."))
+	if report_name == 'Tax Detail':
+		frappe.throw(_("The parent report cannot be overwritten."))
+
+	doc = {
+		'doctype': 'Report',
+		'report_name': report_name,
+		'is_standard': 'No',
+		'module': 'Accounts',
+		'json': data
+	}
+	doc.update(custom_report_dict)
+
+	try:
+		newdoc = frappe.get_doc(doc)
+		newdoc.insert()
+		frappe.msgprint(_("Report created successfully"))
+	except frappe.exceptions.DuplicateEntryError:
+		dbdoc = frappe.get_doc('Report', report_name)
+		dbdoc.update(doc)
+		dbdoc.save()
+		frappe.msgprint(_("Report updated successfully"))
+	return report_name
diff --git a/erpnext/accounts/report/tax_detail/test_tax_detail.json b/erpnext/accounts/report/tax_detail/test_tax_detail.json
new file mode 100644
index 0000000..3a4b175
--- /dev/null
+++ b/erpnext/accounts/report/tax_detail/test_tax_detail.json
@@ -0,0 +1,840 @@
+[
+ {
+  "account_manager": null,
+  "accounts": [],
+  "companies": [],
+  "credit_limits": [],
+  "customer_details": null,
+  "customer_group": "All Customer Groups",
+  "customer_name": "_Test Customer",
+  "customer_pos_id": null,
+  "customer_primary_address": null,
+  "customer_primary_contact": null,
+  "customer_type": "Company",
+  "default_bank_account": null,
+  "default_commission_rate": 0.0,
+  "default_currency": null,
+  "default_price_list": null,
+  "default_sales_partner": null,
+  "disabled": 0,
+  "dn_required": 0,
+  "docstatus": 0,
+  "doctype": "Customer",
+  "email_id": null,
+  "gender": null,
+  "image": null,
+  "industry": null,
+  "is_frozen": 0,
+  "is_internal_customer": 0,
+  "language": "en",
+  "lead_name": null,
+  "loyalty_program": null,
+  "loyalty_program_tier": null,
+  "market_segment": null,
+  "mobile_no": null,
+  "modified": "2021-02-15 05:18:03.624724",
+  "name": "_Test Customer",
+  "naming_series": "CUST-.YYYY.-",
+  "pan": null,
+  "parent": null,
+  "parentfield": null,
+  "parenttype": null,
+  "payment_terms": null,
+  "primary_address": null,
+  "represents_company": "",
+  "sales_team": [],
+  "salutation": null,
+  "so_required": 0,
+  "tax_category": null,
+  "tax_id": null,
+  "tax_withholding_category": null,
+  "territory": "All Territories",
+  "website": null
+ },{
+  "accounts": [],
+  "allow_purchase_invoice_creation_without_purchase_order": 0,
+  "allow_purchase_invoice_creation_without_purchase_receipt": 0,
+  "companies": [],
+  "country": "United Kingdom",
+  "default_bank_account": null,
+  "default_currency": null,
+  "default_price_list": null,
+  "disabled": 0,
+  "docstatus": 0,
+  "doctype": "Supplier",
+  "hold_type": "",
+  "image": null,
+  "is_frozen": 0,
+  "is_internal_supplier": 0,
+  "is_transporter": 0,
+  "language": "en",
+  "modified": "2021-03-31 16:47:10.109316",
+  "name": "_Test Supplier",
+  "naming_series": "SUP-.YYYY.-",
+  "on_hold": 0,
+  "pan": null,
+  "parent": null,
+  "parentfield": null,
+  "parenttype": null,
+  "payment_terms": null,
+  "prevent_pos": 0,
+  "prevent_rfqs": 0,
+  "release_date": null,
+  "represents_company": null,
+  "supplier_details": null,
+  "supplier_group": "Raw Material",
+  "supplier_name": "_Test Supplier",
+  "supplier_type": "Company",
+  "tax_category": null,
+  "tax_id": null,
+  "tax_withholding_category": null,
+  "warn_pos": 0,
+  "warn_rfqs": 0,
+  "website": null
+ },{
+  "account_currency": "GBP",
+  "account_name": "Debtors",
+  "account_number": "",
+  "account_type": "Receivable",
+  "balance_must_be": "",
+  "company": "_T",
+  "disabled": 0,
+  "docstatus": 0,
+  "doctype": "Account",
+  "freeze_account": "No",
+  "include_in_gross": 0,
+  "inter_company_account": 0,
+  "is_group": 0,
+  "lft": 58,
+  "modified": "2021-03-26 04:44:19.955468",
+  "name": "Debtors - _T",
+  "old_parent": null,
+  "parent": null,
+  "parent_account": "Application of Funds (Assets) - _T",
+  "parentfield": null,
+  "parenttype": null,
+  "report_type": "Balance Sheet",
+  "rgt": 59,
+  "root_type": "Asset",
+  "tax_rate": 0.0
+ },{
+  "account_currency": "GBP",
+  "account_name": "Sales",
+  "account_number": "",
+  "account_type": "Income Account",
+  "balance_must_be": "",
+  "company": "_T",
+  "disabled": 0,
+  "docstatus": 0,
+  "doctype": "Account",
+  "freeze_account": "No",
+  "include_in_gross": 0,
+  "inter_company_account": 0,
+  "is_group": 0,
+  "lft": 291,
+  "modified": "2021-03-26 04:50:21.697703",
+  "name": "Sales - _T",
+  "old_parent": null,
+  "parent": null,
+  "parent_account": "Income - _T",
+  "parentfield": null,
+  "parenttype": null,
+  "report_type": "Profit and Loss",
+  "rgt": 292,
+  "root_type": "Income",
+  "tax_rate": 0.0
+ },{
+  "account_currency": "GBP",
+  "account_name": "VAT on Sales",
+  "account_number": "",
+  "account_type": "Tax",
+  "balance_must_be": "",
+  "company": "_T",
+  "disabled": 0,
+  "docstatus": 0,
+  "doctype": "Account",
+  "freeze_account": "No",
+  "include_in_gross": 0,
+  "inter_company_account": 0,
+  "is_group": 0,
+  "lft": 317,
+  "modified": "2021-03-26 04:50:21.697703",
+  "name": "VAT on Sales - _T",
+  "old_parent": null,
+  "parent": null,
+  "parent_account": "Source of Funds (Liabilities) - _T",
+  "parentfield": null,
+  "parenttype": null,
+  "report_type": "Balance Sheet",
+  "rgt": 318,
+  "root_type": "Liability",
+  "tax_rate": 0.0
+ },{
+  "account_currency": "GBP",
+  "account_name": "Cost of Goods Sold",
+  "account_number": "",
+  "account_type": "Cost of Goods Sold",
+  "balance_must_be": "",
+  "company": "_T",
+  "disabled": 0,
+  "docstatus": 0,
+  "doctype": "Account",
+  "freeze_account": "No",
+  "include_in_gross": 0,
+  "inter_company_account": 0,
+  "is_group": 0,
+  "lft": 171,
+  "modified": "2021-03-26 04:44:19.994857",
+  "name": "Cost of Goods Sold - _T",
+  "old_parent": null,
+  "parent": null,
+  "parent_account": "Expenses - _T",
+  "parentfield": null,
+  "parenttype": null,
+  "report_type": "Profit and Loss",
+  "rgt": 172,
+  "root_type": "Expense",
+  "tax_rate": 0.0
+ },{
+  "account_currency": "GBP",
+  "account_name": "VAT on Purchases",
+  "account_number": "",
+  "account_type": "Tax",
+  "balance_must_be": "",
+  "company": "_T",
+  "disabled": 0,
+  "docstatus": 0,
+  "doctype": "Account",
+  "freeze_account": "No",
+  "include_in_gross": 0,
+  "inter_company_account": 0,
+  "is_group": 0,
+  "lft": 80,
+  "modified": "2021-03-26 04:44:19.961983",
+  "name": "VAT on Purchases - _T",
+  "old_parent": null,
+  "parent": null,
+  "parent_account": "Application of Funds (Assets) - _T",
+  "parentfield": null,
+  "parenttype": null,
+  "report_type": "Balance Sheet",
+  "rgt": 81,
+  "root_type": "Asset",
+  "tax_rate": 0.0
+ },{
+  "account_currency": "GBP",
+  "account_name": "Creditors",
+  "account_number": "",
+  "account_type": "Payable",
+  "balance_must_be": "",
+  "company": "_T",
+  "disabled": 0,
+  "docstatus": 0,
+  "doctype": "Account",
+  "freeze_account": "No",
+  "include_in_gross": 0,
+  "inter_company_account": 0,
+  "is_group": 0,
+  "lft": 302,
+  "modified": "2021-03-26 04:50:21.697703",
+  "name": "Creditors - _T",
+  "old_parent": null,
+  "parent": null,
+  "parent_account": "Source of Funds (Liabilities) - _T",
+  "parentfield": null,
+  "parenttype": null,
+  "report_type": "Balance Sheet",
+  "rgt": 303,
+  "root_type": "Liability",
+  "tax_rate": 0.0
+ },{
+  "additional_discount_percentage": 0.0,
+  "address_display": null,
+  "adjust_advance_taxes": 0,
+  "advances": [],
+  "against_expense_account": "Cost of Goods Sold - _T",
+  "allocate_advances_automatically": 0,
+  "amended_from": null,
+  "apply_discount_on": "Grand Total",
+  "apply_tds": 0,
+  "auto_repeat": null,
+  "base_discount_amount": 0.0,
+  "base_grand_total": 511.68,
+  "base_in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.",
+  "base_net_total": 426.4,
+  "base_paid_amount": 0.0,
+  "base_rounded_total": 511.68,
+  "base_rounding_adjustment": 0.0,
+  "base_taxes_and_charges_added": 85.28,
+  "base_taxes_and_charges_deducted": 0.0,
+  "base_total": 426.4,
+  "base_total_taxes_and_charges": 85.28,
+  "base_write_off_amount": 0.0,
+  "bill_date": null,
+  "bill_no": null,
+  "billing_address": null,
+  "billing_address_display": null,
+  "buying_price_list": "Standard Buying",
+  "cash_bank_account": null,
+  "clearance_date": null,
+  "company": "_T",
+  "contact_display": null,
+  "contact_email": null,
+  "contact_mobile": null,
+  "contact_person": null,
+  "conversion_rate": 1.0,
+  "cost_center": null,
+  "credit_to": "Creditors - _T",
+  "currency": "GBP",
+  "disable_rounded_total": 0,
+  "discount_amount": 0.0,
+  "docstatus": 0,
+  "doctype": "Purchase Invoice",
+  "due_date": null,
+  "from_date": null,
+  "grand_total": 511.68,
+  "group_same_items": 0,
+  "hold_comment": null,
+  "ignore_pricing_rule": 0,
+  "in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.",
+  "inter_company_invoice_reference": null,
+  "is_internal_supplier": 0,
+  "is_opening": "No",
+  "is_paid": 0,
+  "is_return": 0,
+  "is_subcontracted": "No",
+  "items": [
+   {
+    "allow_zero_valuation_rate": 0,
+    "amount": 426.4,
+    "asset_category": null,
+    "asset_location": null,
+    "base_amount": 426.4,
+    "base_net_amount": 426.4,
+    "base_net_rate": 5.33,
+    "base_price_list_rate": 5.33,
+    "base_rate": 5.33,
+    "base_rate_with_margin": 0.0,
+    "batch_no": null,
+    "bom": null,
+    "brand": null,
+    "conversion_factor": 0.0,
+    "cost_center": "Main - _T",
+    "deferred_expense_account": null,
+    "description": "<div class=\"ql-editor read-mode\"><p>Fluid to make widgets</p></div>",
+    "discount_amount": 0.0,
+    "discount_percentage": 0.0,
+    "enable_deferred_expense": 0,
+    "expense_account": "Cost of Goods Sold - _T",
+    "from_warehouse": null,
+    "image": null,
+    "include_exploded_items": 0,
+    "is_fixed_asset": 0,
+    "is_free_item": 0,
+    "item_code": null,
+    "item_group": null,
+    "item_name": "Widget Fluid 1Litre",
+    "item_tax_amount": 0.0,
+    "item_tax_rate": "{\"VAT on Purchases - _T\": 20.0}",
+    "item_tax_template": null,
+    "landed_cost_voucher_amount": 0.0,
+    "manufacturer": null,
+    "manufacturer_part_no": null,
+    "margin_rate_or_amount": 0.0,
+    "margin_type": "",
+    "net_amount": 426.4,
+    "net_rate": 5.33,
+    "page_break": 0,
+    "parent": null,
+    "parentfield": "items",
+    "parenttype": "Purchase Invoice",
+    "po_detail": null,
+    "pr_detail": null,
+    "price_list_rate": 5.33,
+    "pricing_rules": null,
+    "project": null,
+    "purchase_invoice_item": null,
+    "purchase_order": null,
+    "purchase_receipt": null,
+    "qty": 80.0,
+    "quality_inspection": null,
+    "rate": 5.33,
+    "rate_with_margin": 0.0,
+    "received_qty": 0.0,
+    "rejected_qty": 0.0,
+    "rejected_serial_no": null,
+    "rejected_warehouse": null,
+    "rm_supp_cost": 0.0,
+    "sales_invoice_item": null,
+    "serial_no": null,
+    "service_end_date": null,
+    "service_start_date": null,
+    "service_stop_date": null,
+    "stock_qty": 0.0,
+    "stock_uom": "Nos",
+    "stock_uom_rate": 0.0,
+    "total_weight": 0.0,
+    "uom": "Nos",
+    "valuation_rate": 0.0,
+    "warehouse": null,
+    "weight_per_unit": 0.0,
+    "weight_uom": null
+   }
+  ],
+  "language": "en",
+  "letter_head": null,
+  "mode_of_payment": null,
+  "modified": "2021-04-03 03:33:09.180453",
+  "name": null,
+  "naming_series": "ACC-PINV-.YYYY.-",
+  "net_total": 426.4,
+  "on_hold": 0,
+  "other_charges_calculation": "<div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n\t<table class=\"table table-bordered table-hover\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-left\">Item</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">Taxable Amount</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">VAT on Purchases</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Widget Fluid 1Litre</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 426.40\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 85.28\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t</tbody>\n\t</table>\n</div>",
+  "outstanding_amount": 511.68,
+  "paid_amount": 0.0,
+  "parent": null,
+  "parentfield": null,
+  "parenttype": null,
+  "party_account_currency": "GBP",
+  "payment_schedule": [],
+  "payment_terms_template": null,
+  "plc_conversion_rate": 1.0,
+  "posting_date": null,
+  "posting_time": "16:59:56.789522",
+  "price_list_currency": "GBP",
+  "pricing_rules": [],
+  "project": null,
+  "rejected_warehouse": null,
+  "release_date": null,
+  "remarks": "No Remarks",
+  "represents_company": null,
+  "return_against": null,
+  "rounded_total": 511.68,
+  "rounding_adjustment": 0.0,
+  "scan_barcode": null,
+  "select_print_heading": null,
+  "set_from_warehouse": null,
+  "set_posting_time": 0,
+  "set_warehouse": null,
+  "shipping_address": null,
+  "shipping_address_display": "",
+  "shipping_rule": null,
+  "status": "Unpaid",
+  "supplied_items": [],
+  "supplier": "_Test Supplier",
+  "supplier_address": null,
+  "supplier_name": "_Test Supplier",
+  "supplier_warehouse": "Stores - _T",
+  "tax_category": null,
+  "tax_id": null,
+  "tax_withholding_category": null,
+  "taxes": [
+   {
+    "account_head": "VAT on Purchases - _T",
+    "add_deduct_tax": "Add",
+    "base_tax_amount": 85.28,
+    "base_tax_amount_after_discount_amount": 85.28,
+    "base_total": 511.68,
+    "category": "Total",
+    "charge_type": "On Net Total",
+    "cost_center": "Main - _T",
+    "description": "VAT on Purchases",
+    "included_in_print_rate": 0,
+    "item_wise_tax_detail": "{\"Widget Fluid 1Litre\":[20.0,85.28]}",
+    "parent": null,
+    "parentfield": "taxes",
+    "parenttype": "Purchase Invoice",
+    "rate": 0.0,
+    "row_id": null,
+    "tax_amount": 85.28,
+    "tax_amount_after_discount_amount": 85.28,
+    "total": 511.68
+   }
+  ],
+  "taxes_and_charges": null,
+  "taxes_and_charges_added": 85.28,
+  "taxes_and_charges_deducted": 0.0,
+  "tc_name": null,
+  "terms": null,
+  "title": "_Purchase Invoice",
+  "to_date": null,
+  "total": 426.4,
+  "total_advance": 0.0,
+  "total_net_weight": 0.0,
+  "total_qty": 80.0,
+  "total_taxes_and_charges": 85.28,
+  "unrealized_profit_loss_account": null,
+  "update_stock": 0,
+  "write_off_account": null,
+  "write_off_amount": 0.0,
+  "write_off_cost_center": null
+ },{
+  "account_for_change_amount": null,
+  "additional_discount_percentage": 0.0,
+  "address_display": null,
+  "advances": [],
+  "against_income_account": "Sales - _T",
+  "allocate_advances_automatically": 0,
+  "amended_from": null,
+  "apply_discount_on": "Grand Total",
+  "auto_repeat": null,
+  "base_change_amount": 0.0,
+  "base_discount_amount": 0.0,
+  "base_grand_total": 868.25,
+  "base_in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.",
+  "base_net_total": 825.0,
+  "base_paid_amount": 0.0,
+  "base_rounded_total": 868.25,
+  "base_rounding_adjustment": 0.0,
+  "base_total": 825.0,
+  "base_total_taxes_and_charges": 43.25,
+  "base_write_off_amount": 0.0,
+  "c_form_applicable": "No",
+  "c_form_no": null,
+  "campaign": null,
+  "cash_bank_account": null,
+  "change_amount": 0.0,
+  "commission_rate": 0.0,
+  "company": "_T",
+  "company_address": null,
+  "company_address_display": null,
+  "company_tax_id": null,
+  "contact_display": null,
+  "contact_email": null,
+  "contact_mobile": null,
+  "contact_person": null,
+  "conversion_rate": 1.0,
+  "cost_center": null,
+  "currency": "GBP",
+  "customer": "_Test Customer",
+  "customer_address": null,
+  "customer_group": "All Customer Groups",
+  "customer_name": "_Test Customer",
+  "debit_to": "Debtors - _T",
+  "discount_amount": 0.0,
+  "docstatus": 0,
+  "doctype": "Sales Invoice",
+  "due_date": null,
+  "from_date": null,
+  "grand_total": 868.25,
+  "group_same_items": 0,
+  "ignore_pricing_rule": 0,
+  "in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.",
+  "inter_company_invoice_reference": null,
+  "is_consolidated": 0,
+  "is_discounted": 0,
+  "is_internal_customer": 0,
+  "is_opening": "No",
+  "is_pos": 0,
+  "is_return": 0,
+  "items": [
+   {
+    "actual_batch_qty": 0.0,
+    "actual_qty": 0.0,
+    "allow_zero_valuation_rate": 0,
+    "amount": 200.0,
+    "asset": null,
+    "barcode": null,
+    "base_amount": 200.0,
+    "base_net_amount": 200.0,
+    "base_net_rate": 50.0,
+    "base_price_list_rate": 0.0,
+    "base_rate": 50.0,
+    "base_rate_with_margin": 0.0,
+    "batch_no": null,
+    "brand": null,
+    "conversion_factor": 1.0,
+    "cost_center": "Main - _T",
+    "customer_item_code": null,
+    "deferred_revenue_account": null,
+    "delivered_by_supplier": 0,
+    "delivered_qty": 0.0,
+    "delivery_note": null,
+    "description": "<div class=\"ql-editor read-mode\"><p>Used</p></div>",
+    "discount_amount": 0.0,
+    "discount_percentage": 0.0,
+    "dn_detail": null,
+    "enable_deferred_revenue": 0,
+    "expense_account": null,
+    "finance_book": null,
+    "image": null,
+    "income_account": "Sales - _T",
+    "incoming_rate": 0.0,
+    "is_fixed_asset": 0,
+    "is_free_item": 0,
+    "item_code": null,
+    "item_group": null,
+    "item_name": "Dunlop tyres",
+    "item_tax_rate": "{\"VAT on Sales - _T\": 20.0}",
+    "item_tax_template": null,
+    "margin_rate_or_amount": 0.0,
+    "margin_type": "",
+    "net_amount": 200.0,
+    "net_rate": 50.0,
+    "page_break": 0,
+    "parent": null,
+    "parentfield": "items",
+    "parenttype": "Sales Invoice",
+    "price_list_rate": 0.0,
+    "pricing_rules": null,
+    "project": null,
+    "qty": 4.0,
+    "quality_inspection": null,
+    "rate": 50.0,
+    "rate_with_margin": 0.0,
+    "sales_invoice_item": null,
+    "sales_order": null,
+    "serial_no": null,
+    "service_end_date": null,
+    "service_start_date": null,
+    "service_stop_date": null,
+    "so_detail": null,
+    "stock_qty": 4.0,
+    "stock_uom": "Nos",
+    "stock_uom_rate": 50.0,
+    "target_warehouse": null,
+    "total_weight": 0.0,
+    "uom": "Nos",
+    "warehouse": null,
+    "weight_per_unit": 0.0,
+    "weight_uom": null
+   },
+   {
+    "actual_batch_qty": 0.0,
+    "actual_qty": 0.0,
+    "allow_zero_valuation_rate": 0,
+    "amount": 65.0,
+    "asset": null,
+    "barcode": null,
+    "base_amount": 65.0,
+    "base_net_amount": 65.0,
+    "base_net_rate": 65.0,
+    "base_price_list_rate": 0.0,
+    "base_rate": 65.0,
+    "base_rate_with_margin": 0.0,
+    "batch_no": null,
+    "brand": null,
+    "conversion_factor": 1.0,
+    "cost_center": "Main - _T",
+    "customer_item_code": null,
+    "deferred_revenue_account": null,
+    "delivered_by_supplier": 0,
+    "delivered_qty": 0.0,
+    "delivery_note": null,
+    "description": "<div class=\"ql-editor read-mode\"><p>Used</p></div>",
+    "discount_amount": 0.0,
+    "discount_percentage": 0.0,
+    "dn_detail": null,
+    "enable_deferred_revenue": 0,
+    "expense_account": null,
+    "finance_book": null,
+    "image": null,
+    "income_account": "Sales - _T",
+    "incoming_rate": 0.0,
+    "is_fixed_asset": 0,
+    "is_free_item": 0,
+    "item_code": "",
+    "item_group": null,
+    "item_name": "Continental tyres",
+    "item_tax_rate": "{\"VAT on Sales - _T\": 5.0}",
+    "item_tax_template": null,
+    "margin_rate_or_amount": 0.0,
+    "margin_type": "",
+    "net_amount": 65.0,
+    "net_rate": 65.0,
+    "page_break": 0,
+    "parent": null,
+    "parentfield": "items",
+    "parenttype": "Sales Invoice",
+    "price_list_rate": 0.0,
+    "pricing_rules": null,
+    "project": null,
+    "qty": 1.0,
+    "quality_inspection": null,
+    "rate": 65.0,
+    "rate_with_margin": 0.0,
+    "sales_invoice_item": null,
+    "sales_order": null,
+    "serial_no": null,
+    "service_end_date": null,
+    "service_start_date": null,
+    "service_stop_date": null,
+    "so_detail": null,
+    "stock_qty": 1.0,
+    "stock_uom": null,
+    "stock_uom_rate": 65.0,
+    "target_warehouse": null,
+    "total_weight": 0.0,
+    "uom": "Nos",
+    "warehouse": null,
+    "weight_per_unit": 0.0,
+    "weight_uom": null
+   },
+   {
+    "actual_batch_qty": 0.0,
+    "actual_qty": 0.0,
+    "allow_zero_valuation_rate": 0,
+    "amount": 560.0,
+    "asset": null,
+    "barcode": null,
+    "base_amount": 560.0,
+    "base_net_amount": 560.0,
+    "base_net_rate": 70.0,
+    "base_price_list_rate": 0.0,
+    "base_rate": 70.0,
+    "base_rate_with_margin": 0.0,
+    "batch_no": null,
+    "brand": null,
+    "conversion_factor": 1.0,
+    "cost_center": "Main - _T",
+    "customer_item_code": null,
+    "deferred_revenue_account": null,
+    "delivered_by_supplier": 0,
+    "delivered_qty": 0.0,
+    "delivery_note": null,
+    "description": "<div class=\"ql-editor read-mode\"><p>New</p></div>",
+    "discount_amount": 0.0,
+    "discount_percentage": 0.0,
+    "dn_detail": null,
+    "enable_deferred_revenue": 0,
+    "expense_account": null,
+    "finance_book": null,
+    "image": null,
+    "income_account": "Sales - _T",
+    "incoming_rate": 0.0,
+    "is_fixed_asset": 0,
+    "is_free_item": 0,
+    "item_code": null,
+    "item_group": null,
+    "item_name": "Toyo tyres",
+    "item_tax_rate": "{\"VAT on Sales - _T\": 0.0}",
+    "item_tax_template": null,
+    "margin_rate_or_amount": 0.0,
+    "margin_type": "",
+    "net_amount": 560.0,
+    "net_rate": 70.0,
+    "page_break": 0,
+    "parent": null,
+    "parentfield": "items",
+    "parenttype": "Sales Invoice",
+    "price_list_rate": 0.0,
+    "pricing_rules": null,
+    "project": null,
+    "qty": 8.0,
+    "quality_inspection": null,
+    "rate": 70.0,
+    "rate_with_margin": 0.0,
+    "sales_invoice_item": null,
+    "sales_order": null,
+    "serial_no": null,
+    "service_end_date": null,
+    "service_start_date": null,
+    "service_stop_date": null,
+    "so_detail": null,
+    "stock_qty": 8.0,
+    "stock_uom": null,
+    "stock_uom_rate": 70.0,
+    "target_warehouse": null,
+    "total_weight": 0.0,
+    "uom": "Nos",
+    "warehouse": null,
+    "weight_per_unit": 0.0,
+    "weight_uom": null
+   }
+  ],
+  "language": "en",
+  "letter_head": null,
+  "loyalty_amount": 0.0,
+  "loyalty_points": 0,
+  "loyalty_program": null,
+  "loyalty_redemption_account": null,
+  "loyalty_redemption_cost_center": null,
+  "modified": "2021-02-16 05:18:59.755144",
+  "name": null,
+  "naming_series": "ACC-SINV-.YYYY.-",
+  "net_total": 825.0,
+  "other_charges_calculation": "<div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n\t<table class=\"table table-bordered table-hover\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-left\">Item</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">Taxable Amount</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">VAT on Sales</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Dunlop tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 200.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 40.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Continental tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 65.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(5.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 3.25\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Toyo tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 560.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(0.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 0.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t</tbody>\n\t</table>\n</div>",
+  "outstanding_amount": 868.25,
+  "packed_items": [],
+  "paid_amount": 0.0,
+  "parent": null,
+  "parentfield": null,
+  "parenttype": null,
+  "party_account_currency": "GBP",
+  "payment_schedule": [],
+  "payment_terms_template": null,
+  "payments": [],
+  "plc_conversion_rate": 1.0,
+  "po_date": null,
+  "po_no": "",
+  "pos_profile": null,
+  "posting_date": null,
+  "posting_time": "5:19:02.994077",
+  "price_list_currency": "GBP",
+  "pricing_rules": [],
+  "project": null,
+  "redeem_loyalty_points": 0,
+  "remarks": "No Remarks",
+  "represents_company": "",
+  "return_against": null,
+  "rounded_total": 868.25,
+  "rounding_adjustment": 0.0,
+  "sales_partner": null,
+  "sales_team": [],
+  "scan_barcode": null,
+  "select_print_heading": null,
+  "selling_price_list": "Standard Selling",
+  "set_posting_time": 0,
+  "set_target_warehouse": null,
+  "set_warehouse": null,
+  "shipping_address": null,
+  "shipping_address_name": "",
+  "shipping_rule": null,
+  "source": null,
+  "status": "Overdue",
+  "tax_category": "",
+  "tax_id": null,
+  "taxes": [
+   {
+    "account_head": "VAT on Sales - _T",
+    "base_tax_amount": 43.25,
+    "base_tax_amount_after_discount_amount": 43.25,
+    "base_total": 868.25,
+    "charge_type": "On Net Total",
+    "cost_center": "Main - _T",
+    "description": "VAT on Sales",
+    "included_in_print_rate": 0,
+    "item_wise_tax_detail": "{\"Dunlop tyres\":[20.0,40.0],\"Continental tyres\":[5.0,3.25],\"Toyo tyres\":[0.0,0.0]}",
+    "parent": null,
+    "parentfield": "taxes",
+    "parenttype": "Sales Invoice",
+    "rate": 0.0,
+    "row_id": null,
+    "tax_amount": 43.25,
+    "tax_amount_after_discount_amount": 43.25,
+    "total": 868.25
+   }
+  ],
+  "taxes_and_charges": null,
+  "tc_name": null,
+  "terms": null,
+  "territory": "All Territories",
+  "timesheets": [],
+  "title": "_Sales Invoice",
+  "to_date": null,
+  "total": 825.0,
+  "total_advance": 0.0,
+  "total_billing_amount": 0.0,
+  "total_commission": 0.0,
+  "total_net_weight": 0.0,
+  "total_qty": 13.0,
+  "total_taxes_and_charges": 43.25,
+  "unrealized_profit_loss_account": null,
+  "update_billed_amount_in_sales_order": 0,
+  "update_stock": 0,
+  "write_off_account": null,
+  "write_off_amount": 0.0,
+  "write_off_cost_center": null,
+  "write_off_outstanding_amount_automatically": 0
+ }
+]
diff --git a/erpnext/accounts/report/tax_detail/test_tax_detail.py b/erpnext/accounts/report/tax_detail/test_tax_detail.py
new file mode 100644
index 0000000..d3b8de5
--- /dev/null
+++ b/erpnext/accounts/report/tax_detail/test_tax_detail.py
@@ -0,0 +1,178 @@
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+import datetime
+import json
+import os
+from frappe.utils import getdate, add_to_date, get_first_day, get_last_day, get_year_start, get_year_ending
+from .tax_detail import filter_match, save_custom_report
+
+class TestTaxDetail(unittest.TestCase):
+	def load_testdocs(self):
+		from erpnext.accounts.utils import get_fiscal_year, FiscalYearError
+		datapath, _ = os.path.splitext(os.path.realpath(__file__))
+		with open(datapath + '.json', 'r') as fp:
+			docs = json.load(fp)
+
+		now = getdate()
+		self.from_date = get_first_day(now)
+		self.to_date = get_last_day(now)
+
+		try:
+			get_fiscal_year(now, company="_T")
+		except FiscalYearError:
+			docs = [{
+				"companies": [{
+					"company": "_T",
+					"parent": "_Test Fiscal",
+					"parentfield": "companies",
+					"parenttype": "Fiscal Year"
+				}],
+				"doctype": "Fiscal Year",
+				"year": "_Test Fiscal",
+				"year_end_date": get_year_ending(now),
+				"year_start_date": get_year_start(now)
+			}] + docs
+
+		docs = [{
+			"abbr": "_T",
+			"company_name": "_T",
+			"country": "United Kingdom",
+			"default_currency": "GBP",
+			"doctype": "Company",
+			"name": "_T"
+		}] + docs
+
+		for doc in docs:
+			try:
+				db_doc = frappe.get_doc(doc)
+				if 'Invoice' in db_doc.doctype:
+					db_doc.due_date = add_to_date(now, days=1)
+					db_doc.insert()
+					# Create GL Entries:
+					db_doc.submit()
+				else:
+					db_doc.insert()
+			except frappe.exceptions.DuplicateEntryError:
+				pass
+
+	def load_defcols(self):
+		self.company = frappe.get_doc('Company', '_T')
+		custom_report = frappe.get_doc('Report', 'Tax Detail')
+		self.default_columns, _ = custom_report.run_query_report(
+			filters={
+				'from_date': '2021-03-01',
+				'to_date': '2021-03-31',
+				'company': self.company.name,
+				'mode': 'run',
+				'report_name': 'Tax Detail'
+			}, user=frappe.session.user)
+
+	def rm_testdocs(self):
+		"Remove the Company and all data"
+		from erpnext.setup.doctype.company.delete_company_transactions import delete_company_transactions
+		delete_company_transactions(self.company.name)
+		self.company.delete()
+
+
+	def test_report(self):
+		self.load_testdocs()
+		self.load_defcols()
+		report_name = save_custom_report(
+			'Tax Detail',
+			'_Test Tax Detail',
+			json.dumps({
+				'columns': self.default_columns,
+				'sections': {
+					'Box1':{'Filter0':{'type':'filter','filters':{'4':'VAT on Sales'}}},
+					'Box2':{'Filter0':{'type':'filter','filters':{'4':'Acquisition'}}},
+					'Box3':{'Box1':{'type':'section'},'Box2':{'type':'section'}},
+					'Box4':{'Filter0':{'type':'filter','filters':{'4':'VAT on Purchases'}}},
+					'Box5':{'Box3':{'type':'section'},'Box4':{'type':'section'}},
+					'Box6':{'Filter0':{'type':'filter','filters':{'3':'!=Tax','4':'Sales'}}},
+					'Box7':{'Filter0':{'type':'filter','filters':{'2':'Expense','3':'!=Tax'}}},
+					'Box8':{'Filter0':{'type':'filter','filters':{'3':'!=Tax','4':'Sales','12':'EU'}}},
+					'Box9':{'Filter0':{'type':'filter','filters':{'2':'Expense','3':'!=Tax','12':'EU'}}}
+				},
+				'show_detail': 1
+			}))
+		data = frappe.desk.query_report.run(report_name,
+			filters={
+				'from_date': self.from_date,
+				'to_date': self.to_date,
+				'company': self.company.name,
+				'mode': 'run',
+				'report_name': report_name
+			}, user=frappe.session.user)
+
+		self.assertListEqual(data.get('columns'), self.default_columns)
+		expected = (('Box1', 43.25), ('Box2', 0.0), ('Box3', 43.25), ('Box4', -85.28), ('Box5', -42.03),
+			('Box6', 825.0), ('Box7', -426.40), ('Box8', 0.0), ('Box9', 0.0))
+		exrow = iter(expected)
+		for row in data.get('result'):
+			if row.get('voucher_no') and not row.get('posting_date'):
+				label, value = next(exrow)
+				self.assertDictEqual(row, {'voucher_no': label, 'amount': value})
+		self.assertListEqual(data.get('report_summary'),
+			[{'label': label, 'datatype': 'Currency', 'value': value} for label, value in expected])
+
+		self.rm_testdocs()
+
+	def test_filter_match(self):
+		# None - treated as -inf number except range
+		self.assertTrue(filter_match(None, '!='))
+		self.assertTrue(filter_match(None, '<'))
+		self.assertTrue(filter_match(None, '<jjj'))
+		self.assertTrue(filter_match(None, '  :  '))
+		self.assertTrue(filter_match(None, ':56'))
+		self.assertTrue(filter_match(None, ':de'))
+		self.assertFalse(filter_match(None, '3.4'))
+		self.assertFalse(filter_match(None, '='))
+		self.assertFalse(filter_match(None, '=3.4'))
+		self.assertFalse(filter_match(None, '>3.4'))
+		self.assertFalse(filter_match(None, '   <'))
+		self.assertFalse(filter_match(None, 'ew'))
+		self.assertFalse(filter_match(None, ' '))
+		self.assertFalse(filter_match(None, ' f :'))
+
+		# Numbers
+		self.assertTrue(filter_match(3.4, '3.4'))
+		self.assertTrue(filter_match(3.4, '.4'))
+		self.assertTrue(filter_match(3.4, '3'))
+		self.assertTrue(filter_match(-3.4, '< -3'))
+		self.assertTrue(filter_match(-3.4, '> -4'))
+		self.assertTrue(filter_match(3.4, '= 3.4 '))
+		self.assertTrue(filter_match(3.4, '!=4.5'))
+		self.assertTrue(filter_match(3.4, ' 3 : 4 '))
+		self.assertTrue(filter_match(0.0, '  :  '))
+		self.assertFalse(filter_match(3.4, '=4.5'))
+		self.assertFalse(filter_match(3.4, ' = 3.4 '))
+		self.assertFalse(filter_match(3.4, '!=3.4'))
+		self.assertFalse(filter_match(3.4, '>6'))
+		self.assertFalse(filter_match(3.4, '<-4.5'))
+		self.assertFalse(filter_match(3.4, '4.5'))
+		self.assertFalse(filter_match(3.4, '5:9'))
+
+		# Strings
+		self.assertTrue(filter_match('ACC-SINV-2021-00001', 'SINV'))
+		self.assertTrue(filter_match('ACC-SINV-2021-00001', 'sinv'))
+		self.assertTrue(filter_match('ACC-SINV-2021-00001', '-2021'))
+		self.assertTrue(filter_match(' ACC-SINV-2021-00001', ' acc'))
+		self.assertTrue(filter_match('ACC-SINV-2021-00001', '=2021'))
+		self.assertTrue(filter_match('ACC-SINV-2021-00001', '!=zz'))
+		self.assertTrue(filter_match('ACC-SINV-2021-00001', '<   zzz  '))
+		self.assertTrue(filter_match('ACC-SINV-2021-00001', '  :  sinv  '))
+		self.assertFalse(filter_match('ACC-SINV-2021-00001', '  sinv  :'))
+		self.assertFalse(filter_match('ACC-SINV-2021-00001', ' acc'))
+		self.assertFalse(filter_match('ACC-SINV-2021-00001', '= 2021 '))
+		self.assertFalse(filter_match('ACC-SINV-2021-00001', '!=sinv'))
+		self.assertFalse(filter_match('ACC-SINV-2021-00001', ' >'))
+		self.assertFalse(filter_match('ACC-SINV-2021-00001', '>aa'))
+		self.assertFalse(filter_match('ACC-SINV-2021-00001', ' <'))
+		self.assertFalse(filter_match('ACC-SINV-2021-00001', '<   '))
+		self.assertFalse(filter_match('ACC-SINV-2021-00001', ' ='))
+		self.assertFalse(filter_match('ACC-SINV-2021-00001', '='))
+
+		# Date - always match
+		self.assertTrue(filter_match(datetime.date(2021, 3, 19), ' kdsjkldfs '))
diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json
index df68318..b04b03c 100644
--- a/erpnext/accounts/workspace/accounting/accounting.json
+++ b/erpnext/accounts/workspace/accounting/accounting.json
@@ -438,22 +438,34 @@
    "dependencies": "GL Entry",
    "hidden": 0,
    "is_query_report": 1,
-   "label": "DATEV Export",
-   "link_to": "DATEV",
+   "label": "Tax Detail",
+   "link_to": "Tax Detail",
    "link_type": "Report",
    "onboard": 0,
    "type": "Link"
   },
   {
-    "dependencies": "GL Entry",
-    "hidden": 0,
-    "is_query_report": 1,
-    "label": "UAE VAT 201",
-    "link_to": "UAE VAT 201",
-    "link_type": "Report",
-    "onboard": 0,
-    "type": "Link"
-   },
+   "dependencies": "GL Entry",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "DATEV Export",
+   "link_to": "DATEV",
+   "link_type": "Report",
+   "onboard": 0,
+   "only_for": "Germany",
+   "type": "Link"
+  },
+  {
+   "dependencies": "GL Entry",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "UAE VAT 201",
+   "link_to": "UAE VAT 201",
+   "link_type": "Report",
+   "onboard": 0,
+   "only_for": "United Arab Emirates",
+   "type": "Link"
+  },
   {
    "hidden": 0,
    "is_query_report": 0,
@@ -684,6 +696,7 @@
    "is_query_report": 0,
    "label": "Goods and Services Tax (GST India)",
    "onboard": 0,
+   "only_for": "India",
    "type": "Card Break"
   },
   {
@@ -1052,7 +1065,7 @@
    "type": "Link"
   }
  ],
- "modified": "2021-05-12 11:48:01.905144",
+ "modified": "2021-05-13 13:44:56.249888",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounting",
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 9aff144..8799275 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -195,8 +195,7 @@
 				# If depreciation is already completed (for double declining balance)
 				if skip_row: continue
 
-				depreciation_amount = self.get_depreciation_amount(value_after_depreciation,
-					d.total_number_of_depreciations, d)
+				depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d)
 
 				if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
 					schedule_date = add_months(d.depreciation_start_date,
@@ -208,7 +207,7 @@
 
 				# For first row
 				if has_pro_rata and n==0:
-					depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount,
+					depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
 						self.available_for_use_date, d.depreciation_start_date)
 
 					# For first depr schedule date will be the start date
@@ -220,7 +219,7 @@
 					to_date = add_months(self.available_for_use_date,
 						n * cint(d.frequency_of_depreciation))
 
-					depreciation_amount, days, months = get_pro_rata_amt(d,
+					depreciation_amount, days, months = self.get_pro_rata_amt(d,
 						depreciation_amount, schedule_date, to_date)
 
 					monthly_schedule_date = add_months(schedule_date, 1)
@@ -365,24 +364,6 @@
 	def get_value_after_depreciation(self, idx):
 		return flt(self.get('finance_books')[cint(idx)-1].value_after_depreciation)
 
-	def get_depreciation_amount(self, depreciable_value, total_number_of_depreciations, row):
-		precision = self.precision("gross_purchase_amount")
-
-		if row.depreciation_method in ("Straight Line", "Manual"):
-			depreciation_left = (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked))
-
-			if not depreciation_left:
-				frappe.msgprint(_("All the depreciations has been booked"))
-				depreciation_amount = flt(row.expected_value_after_useful_life)
-				return depreciation_amount
-
-			depreciation_amount = (flt(row.value_after_depreciation) -
-				flt(row.expected_value_after_useful_life)) / depreciation_left
-		else:
-			depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision)
-
-		return depreciation_amount
-
 	def validate_expected_value_after_useful_life(self):
 		for row in self.get('finance_books'):
 			accumulated_depreciation_after_full_schedule = [d.accumulated_depreciation_amount
@@ -575,6 +556,13 @@
 
 			return 100 * (1 - flt(depreciation_rate, float_precision))
 
+	def get_pro_rata_amt(self, row, depreciation_amount, from_date, to_date):
+		days = date_diff(to_date, from_date)
+		months = month_diff(to_date, from_date)
+		total_days = get_total_days(to_date, row.frequency_of_depreciation)
+
+		return (depreciation_amount * flt(days)) / flt(total_days), days, months
+
 def update_maintenance_status():
 	assets = frappe.get_all(
 		"Asset", filters={"docstatus": 1, "maintenance_required": 1}
@@ -758,15 +746,20 @@
 def is_cwip_accounting_enabled(asset_category):
 	return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting"))
 
-def get_pro_rata_amt(row, depreciation_amount, from_date, to_date):
-	days = date_diff(to_date, from_date)
-	months = month_diff(to_date, from_date)
-	total_days = get_total_days(to_date, row.frequency_of_depreciation)
-
-	return (depreciation_amount * flt(days)) / flt(total_days), days, months
-
 def get_total_days(date, frequency):
 	period_start_date = add_months(date,
 		cint(frequency) * -1)
 
 	return date_diff(date, period_start_date)
+
+@erpnext.allow_regional
+def get_depreciation_amount(asset, depreciable_value, row):
+	depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
+
+	if row.depreciation_method in ("Straight Line", "Manual"):
+		depreciation_amount = (flt(row.value_after_depreciation) -
+			flt(row.expected_value_after_useful_life)) / depreciation_left
+	else:
+		depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100))
+
+	return depreciation_amount
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index f12a33d..3cd4b80 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -635,6 +635,45 @@
 		frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
 		frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc)
 
+	def test_discounted_wdv_depreciation_rate_for_indian_region(self):
+		# set indian company
+		company_flag = frappe.flags.company
+		frappe.flags.company = "_Test Company"
+
+		pr = make_purchase_receipt(item_code="Macbook Pro",
+			qty=1, rate=8000.0, location="Test Location")
+
+		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+		asset = frappe.get_doc('Asset', asset_name)
+		asset.calculate_depreciation = 1
+		asset.available_for_use_date = '2030-06-12'
+		asset.purchase_date = '2030-01-01'
+		asset.append("finance_books", {
+			"expected_value_after_useful_life": 1000,
+			"depreciation_method": "Written Down Value",
+			"total_number_of_depreciations": 3,
+			"frequency_of_depreciation": 12,
+			"depreciation_start_date": "2030-12-31"
+		})
+		asset.save(ignore_permissions=True)
+
+		self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
+
+		expected_schedules = [
+			["2030-12-31", 1106.85, 1106.85],
+			["2031-12-31", 3446.58, 4553.43],
+			["2032-12-31", 1723.29, 6276.72],
+			["2033-06-12", 723.28, 7000.00]
+		]
+
+		schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+			for d in asset.get("schedules")]
+
+		self.assertEqual(schedules, expected_schedules)
+
+		# reset indian company
+		frappe.flags.company = company_flag
+
 def create_asset_data():
 	if not frappe.db.exists("Asset Category", "Computers"):
 		create_asset_category()
diff --git a/erpnext/assets/doctype/asset_category/asset_category.js b/erpnext/assets/doctype/asset_category/asset_category.js
index 74963c2..51ce157 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.js
+++ b/erpnext/assets/doctype/asset_category/asset_category.js
@@ -4,7 +4,7 @@
 frappe.ui.form.on('Asset Category', {
 	onload: function(frm) {
 		frm.add_fetch('company_name', 'accumulated_depreciation_account', 'accumulated_depreciation_account');
-		frm.add_fetch('company_name', 'depreciation_expense_account', 'accumulated_depreciation_account');
+		frm.add_fetch('company_name', 'depreciation_expense_account', 'depreciation_expense_account');
 
 		frm.set_query('fixed_asset_account', 'accounts', function(doc, cdt, cdn) {
 			var d  = locals[cdt][cdn];
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index dd0f065..384bbc5 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -61,8 +61,8 @@
 	}
 });
 
-erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
-	setup: function() {
+erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends erpnext.buying.BuyingController {
+	setup() {
 		this.frm.custom_make_buttons = {
 			'Purchase Receipt': 'Purchase Receipt',
 			'Purchase Invoice': 'Purchase Invoice',
@@ -70,13 +70,13 @@
 			'Payment Entry': 'Payment',
 		}
 
-		this._super();
+		super.setup();
 
-	},
+	}
 
-	refresh: function(doc, cdt, cdn) {
+	refresh(doc, cdt, cdn) {
 		var me = this;
-		this._super();
+		super.refresh();
 		var allow_receipt = false;
 		var is_drop_ship = false;
 
@@ -182,9 +182,9 @@
 		} else if(doc.docstatus===0) {
 			cur_frm.cscript.add_from_mappers();
 		}
-	},
+	}
 
-	get_items_from_open_material_requests: function() {
+	get_items_from_open_material_requests() {
 		erpnext.utils.map_current_doc({
 			method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order_based_on_supplier",
 			args: {
@@ -202,17 +202,17 @@
 			},
 			get_query_method: "erpnext.stock.doctype.material_request.material_request.get_material_requests_based_on_supplier"
 		});
-	},
+	}
 
-	validate: function() {
+	validate() {
 		set_schedule_date(this.frm);
-	},
+	}
 
-	has_unsupplied_items: function() {
+	has_unsupplied_items() {
 		return this.frm.doc['supplied_items'].some(item => item.required_qty != item.supplied_qty)
-	},
+	}
 
-	make_stock_entry: function() {
+	make_stock_entry() {
 		var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; });
 		var me = this;
 
@@ -326,9 +326,9 @@
 			me.dialog.hide();
 		});
 
-	},
+	}
 
-	_make_rm_stock_entry: function(rm_items) {
+	_make_rm_stock_entry(rm_items) {
 		frappe.call({
 			method:"erpnext.buying.doctype.purchase_order.purchase_order.make_rm_stock_entry",
 			args: {
@@ -341,31 +341,31 @@
 				frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
 			}
 		});
-	},
+	}
 
-	make_inter_company_order: function(frm) {
+	make_inter_company_order(frm) {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.buying.doctype.purchase_order.purchase_order.make_inter_company_sales_order",
 			frm: frm
 		});
-	},
+	}
 
-	make_purchase_receipt: function() {
+	make_purchase_receipt() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
 			frm: cur_frm,
 			freeze_message: __("Creating Purchase Receipt ...")
 		})
-	},
+	}
 
-	make_purchase_invoice: function() {
+	make_purchase_invoice() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice",
 			frm: cur_frm
 		})
-	},
+	}
 
-	add_from_mappers: function() {
+	add_from_mappers() {
 		var me = this;
 		this.frm.add_custom_button(__('Material Request'),
 			function() {
@@ -471,13 +471,13 @@
 				}
 			});
 		}, __("Tools"));
-	},
+	}
 
-	tc_name: function() {
+	tc_name() {
 		this.get_terms();
-	},
+	}
 
-	items_add: function(doc, cdt, cdn) {
+	items_add(doc, cdt, cdn) {
 		var row = frappe.get_doc(cdt, cdn);
 		if(doc.schedule_date) {
 			row.schedule_date = doc.schedule_date;
@@ -485,13 +485,13 @@
 		} else {
 			this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
 		}
-	},
+	}
 
-	unhold_purchase_order: function(){
+	unhold_purchase_order(){
 		cur_frm.cscript.update_status("Resume", "Draft")
-	},
+	}
 
-	hold_purchase_order: function(){
+	hold_purchase_order(){
 		var me = this;
 		var d = new frappe.ui.Dialog({
 			title: __('Reason for Hold'),
@@ -523,31 +523,31 @@
 			}
 		});
 		d.show();
-	},
+	}
 
-	unclose_purchase_order: function(){
+	unclose_purchase_order(){
 		cur_frm.cscript.update_status('Re-open', 'Submitted')
-	},
+	}
 
-	close_purchase_order: function(){
+	close_purchase_order(){
 		cur_frm.cscript.update_status('Close', 'Closed')
-	},
+	}
 
-	delivered_by_supplier: function(){
+	delivered_by_supplier(){
 		cur_frm.cscript.update_status('Deliver', 'Delivered')
-	},
+	}
 
-	items_on_form_rendered: function() {
-		set_schedule_date(this.frm);
-	},
-
-	schedule_date: function() {
+	items_on_form_rendered() {
 		set_schedule_date(this.frm);
 	}
-});
+
+	schedule_date() {
+		set_schedule_date(this.frm);
+	}
+};
 
 // for backward compatibility: combine new and previous states
-$.extend(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
 
 cur_frm.cscript.update_status= function(label, status){
 	frappe.call({
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 b76c378..bde00cb 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -205,10 +205,10 @@
 
 })
 
-erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.extend({
-	refresh: function() {
+erpnext.buying.RequestforQuotationController = class RequestforQuotationController extends erpnext.buying.BuyingController {
+	refresh() {
 		var me = this;
-		this._super();
+		super.refresh();
 		if (this.frm.doc.docstatus===0) {
 			this.frm.add_custom_button(__('Material Request'),
 				function() {
@@ -302,17 +302,17 @@
 					me.get_suppliers_button(me.frm);
 				}, __("Tools"));
 		}
-	},
+	}
 
-	calculate_taxes_and_totals: function() {
+	calculate_taxes_and_totals() {
 		return;
-	},
+	}
 
-	tc_name: function() {
+	tc_name() {
 		this.get_terms();
-	},
+	}
 
-	get_suppliers_button: function (frm) {
+	get_suppliers_button (frm) {
 		var doc = frm.doc;
 		var dialog = new frappe.ui.Dialog({
 			title: __("Get Suppliers"),
@@ -410,8 +410,8 @@
 		});
 
 		dialog.show();
-	},
-});
+	}
+};
 
 // for backward compatibility: combine new and previous states
-$.extend(cur_frm.cscript, new erpnext.buying.RequestforQuotationController({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.buying.RequestforQuotationController({frm: cur_frm}));
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
index a0187b0..dc9c590 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
@@ -4,19 +4,19 @@
 // attach required files
 {% include 'erpnext/public/js/controllers/buying.js' %};
 
-erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({
-	setup: function() {
+erpnext.buying.SupplierQuotationController = class SupplierQuotationController extends erpnext.buying.BuyingController {
+	setup() {
 		this.frm.custom_make_buttons = {
 			'Purchase Order': 'Purchase Order',
 			'Quotation': 'Quotation'
 		}
 
-		this._super();
-	},
+		super.setup();
+	}
 
-	refresh: function() {
+	refresh() {
 		var me = this;
-		this._super();
+		super.refresh();
 
 		if (this.frm.doc.__islocal && !this.frm.doc.valid_till) {
 			this.frm.set_value('valid_till', frappe.datetime.add_months(this.frm.doc.transaction_date, 1));
@@ -77,25 +77,25 @@
 				})
 			}, __("Get Items From"));
 		}
-	},
+	}
 
-	make_purchase_order: function() {
+	make_purchase_order() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_purchase_order",
 			frm: cur_frm
 		})
-	},
-	make_quotation: function() {
+	}
+	make_quotation() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_quotation",
 			frm: cur_frm
 		})
 
 	}
-});
+};
 
 // for backward compatibility: combine new and previous states
-$.extend(cur_frm.cscript, new erpnext.buying.SupplierQuotationController({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.buying.SupplierQuotationController({frm: cur_frm}));
 
 cur_frm.fields_dict['items'].grid.get_field('project').get_query =
 	function(doc, cdt, cdn) {
diff --git a/erpnext/change_log/v13/v13.0.2.md b/erpnext/change_log/v13/v13_0_2.md
similarity index 100%
rename from erpnext/change_log/v13/v13.0.2.md
rename to erpnext/change_log/v13/v13_0_2.md
diff --git a/erpnext/change_log/v13/v13_3_0.md b/erpnext/change_log/v13/v13_3_0.md
new file mode 100644
index 0000000..016dbb0
--- /dev/null
+++ b/erpnext/change_log/v13/v13_3_0.md
@@ -0,0 +1,73 @@
+# Version 13.3.0 Release Notes
+
+### Features & Enhancements
+
+- Purchase receipt creation from purchase invoice ([#25126](https://github.com/frappe/erpnext/pull/25126))
+- New Document Transaction Deletion ([#25354](https://github.com/frappe/erpnext/pull/25354))
+- Employee Referral ([#24997](https://github.com/frappe/erpnext/pull/24997))
+- Add Create Expense Claim button in Delivery Trip ([#25526](https://github.com/frappe/erpnext/pull/25526))
+- Reduced rate of asset depreciation as per IT Act ([#25648](https://github.com/frappe/erpnext/pull/25648))
+- Improve DATEV export ([#25238](https://github.com/frappe/erpnext/pull/25238))
+- Add pick batch button ([#25413](https://github.com/frappe/erpnext/pull/25413))
+- Enable custom field search on POS ([#25421](https://github.com/frappe/erpnext/pull/25421))
+- New check field in subscriptions for (not) submitting invoices ([#25394](https://github.com/frappe/erpnext/pull/25394))
+- Show POS reserved stock in stock projected qty report ([#25593](https://github.com/frappe/erpnext/pull/25593))
+- e-way bill validity field ([#25555](https://github.com/frappe/erpnext/pull/25555))
+- Significant reduction in time taken to save sales documents ([#25475](https://github.com/frappe/erpnext/pull/25475))
+
+### Fixes
+
+- Bank statement import via google sheet ([#25677](https://github.com/frappe/erpnext/pull/25677))
+- Invoices not getting fetched during payment reconciliation ([#25598](https://github.com/frappe/erpnext/pull/25598))
+- Error on applying TDS without party ([#25632](https://github.com/frappe/erpnext/pull/25632))
+- Allow to cancel loan with cancelled repayment entry ([#25507](https://github.com/frappe/erpnext/pull/25507))
+- Can't open general ledger from consolidated financial report ([#25542](https://github.com/frappe/erpnext/pull/25542))
+- Add 'Partially Received' to Status drop-down list in Material Request ([#24857](https://github.com/frappe/erpnext/pull/24857))
+- Updated item filters for material request ([#25531](https://github.com/frappe/erpnext/pull/25531))
+- Added validation in stock entry to check duplicate serial nos ([#25611](https://github.com/frappe/erpnext/pull/25611))
+- Update shopify api version ([#25600](https://github.com/frappe/erpnext/pull/25600))
+- Dialog variable assignment after definition in POS ([#25680](https://github.com/frappe/erpnext/pull/25680))
+- Added tax_types list ([#25587](https://github.com/frappe/erpnext/pull/25587))
+- Include search fields in Project Link field query ([#25505](https://github.com/frappe/erpnext/pull/25505))
+- Item stock levels displaying inconsistently ([#25506](https://github.com/frappe/erpnext/pull/25506))
+- Change today to now to get data for reposting ([#25703](https://github.com/frappe/erpnext/pull/25703))
+- Parameter for get_filtered_list_for_consolidated_report in consolidated balance sheet ([#25700](https://github.com/frappe/erpnext/pull/25700))
+- Minor fixes in loan ([#25546](https://github.com/frappe/erpnext/pull/25546))
+- Fieldname when updating docfield property ([#25516](https://github.com/frappe/erpnext/pull/25516))
+- Use get_serial_nos for splitting ([#25590](https://github.com/frappe/erpnext/pull/25590))
+- Show item's full name on hover over item in POS ([#25554](https://github.com/frappe/erpnext/pull/25554))
+- Stock ledger entry created against draft stock entry ([#25540](https://github.com/frappe/erpnext/pull/25540))
+- Incorrect expense account set in pos invoice ([#25543](https://github.com/frappe/erpnext/pull/25543))
+- Stock balance and batch-wise balance history report showing different closing stock ([#25575](https://github.com/frappe/erpnext/pull/25575))
+- Make strings translatable ([#25521](https://github.com/frappe/erpnext/pull/25521))
+- Serial no changed after saving stock reconciliation ([#25541](https://github.com/frappe/erpnext/pull/25541))
+- Ignore fraction difference while making round off gl entry ([#25438](https://github.com/frappe/erpnext/pull/25438))
+- Sync shopify customer addresses ([#25481](https://github.com/frappe/erpnext/pull/25481))
+- Total stock summary report not working ([#25551](https://github.com/frappe/erpnext/pull/25551))
+- Rename field has not updated value of deposit and withdrawal fields ([#25545](https://github.com/frappe/erpnext/pull/25545))
+- Unexpected keyword argument 'merge_logs' ([#25489](https://github.com/frappe/erpnext/pull/25489))
+- Validation message of quality inspection in purchase receipt ([#25667](https://github.com/frappe/erpnext/pull/25667))
+- Added is_stock_item filter ([#25530](https://github.com/frappe/erpnext/pull/25530))
+- Fetch total stock at company in PO ([#25532](https://github.com/frappe/erpnext/pull/25532))
+- Updated filters for process statement of accounts ([#25384](https://github.com/frappe/erpnext/pull/25384))
+- Incorrect expense account set in pos invoice ([#25571](https://github.com/frappe/erpnext/pull/25571))
+- Client script breaking while settings tax labels ([#25653](https://github.com/frappe/erpnext/pull/25653))
+- Empty payment term column in accounts receivable report ([#25556](https://github.com/frappe/erpnext/pull/25556))
+- Designation insufficient permission on lead doctype. ([#25331](https://github.com/frappe/erpnext/pull/25331))
+- Force https for shopify webhook registration ([#25630](https://github.com/frappe/erpnext/pull/25630))
+- Patch regional fields for old companies ([#25673](https://github.com/frappe/erpnext/pull/25673))
+- Woocommerce order sync issue ([#25692](https://github.com/frappe/erpnext/pull/25692))
+- Allow to receive same serial numbers multiple times ([#25471](https://github.com/frappe/erpnext/pull/25471))
+- Update Allocated amount after Paid Amount is changed in PE ([#25515](https://github.com/frappe/erpnext/pull/25515))
+- Updating Standard Notification's channel field ([#25564](https://github.com/frappe/erpnext/pull/25564))
+- Report summary showing inflated values when values are accumulated in Group Company ([#25577](https://github.com/frappe/erpnext/pull/25577))
+- UI fixes related to overflowing payment section ([#25652](https://github.com/frappe/erpnext/pull/25652))
+- List invoices in Payment Reconciliation Payment ([#25524](https://github.com/frappe/erpnext/pull/25524))
+- Ageing errors in PSOA ([#25490](https://github.com/frappe/erpnext/pull/25490))
+- Prevent spurious defaults for items when making prec from dnote ([#25559](https://github.com/frappe/erpnext/pull/25559))
+- Stock reconciliation getting time out error during submission ([#25557](https://github.com/frappe/erpnext/pull/25557))
+- Timesheet filter date exclusive issue ([#25626](https://github.com/frappe/erpnext/pull/25626))
+- Update cost center in the item table fetched from POS Profile ([#25609](https://github.com/frappe/erpnext/pull/25609))
+- Updated modified time in purchase invoice to pull new fields ([#25678](https://github.com/frappe/erpnext/pull/25678))
+- Stock and Accounts Settings form refactor ([#25534](https://github.com/frappe/erpnext/pull/25534))
+- Payment amount showing in foreign currency ([#25292](https://github.com/frappe/erpnext/pull/25292))
\ No newline at end of file
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index b686dc0..3f2d339 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -838,9 +838,10 @@
 		if not self.get("items"):
 			return
 
-		earliest_schedule_date = min([d.schedule_date for d in self.get("items")])
-		if earliest_schedule_date:
-			self.schedule_date = earliest_schedule_date
+		if any(d.schedule_date for d in self.get("items")):
+			# Select earliest schedule_date.
+			self.schedule_date = min(d.schedule_date for d in self.get("items")
+							if d.schedule_date is not None)
 
 		if self.schedule_date:
 			for d in self.get('items'):
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index bc1ac5e..b31724f 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -292,11 +292,14 @@
 		cond = """(`tabProject`.customer = %s or
 			ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer")))
 
-	fields = get_fields("Project", ["name"])
+	fields = get_fields("Project", ["name", "project_name"])
+	searchfields = frappe.get_meta("Project").get_search_fields()
+	searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
 
 	return frappe.db.sql("""select {fields} from `tabProject`
-		where `tabProject`.status not in ("Completed", "Cancelled")
-			and {cond} `tabProject`.name like %(txt)s {match_cond}
+		where
+			`tabProject`.status not in ("Completed", "Cancelled")
+			and {cond} {match_cond} {scond}
 		order by
 			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
 			idx desc,
@@ -304,6 +307,7 @@
 		limit {start}, {page_len}""".format(
 			fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]),
 			cond=cond,
+			scond=searchfields,
 			match_cond=get_match_cond(doctype),
 			start=start,
 			page_len=page_len), {
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 4bb6138..ed3aee5 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -100,6 +100,10 @@
 		["Queued", "eval:self.status == 'Queued'"],
 		["Failed", "eval:self.status == 'Failed'"],
 		["Cancelled", "eval:self.docstatus == 2"],
+	],
+	"Transaction Deletion Record": [
+		["Draft", None],
+		["Completed", "eval:self.docstatus == 1"],
 	]
 }
 
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index b14c274..41ca404 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -379,8 +379,7 @@
 					link = frappe.utils.get_link_to_form('Quality Inspection', d.quality_inspection)
 					frappe.throw(_("Quality Inspection: {0} is not submitted for the item: {1} in row {2}").format(link, d.item_code, d.idx), QualityInspectionNotSubmittedError)
 
-				qa_failed = any([r.status=="Rejected" for r in qa_doc.readings])
-				if qa_failed:
+				if qa_doc.status != 'Accepted':
 					frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}")
 						.format(d.idx, d.item_code), QualityInspectionRejectedError)
 			elif qa_required :
diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js
index 0c88d28..ebe8524 100644
--- a/erpnext/crm/doctype/lead/lead.js
+++ b/erpnext/crm/doctype/lead/lead.js
@@ -4,8 +4,8 @@
 frappe.provide("erpnext");
 cur_frm.email_field = "email_id";
 
-erpnext.LeadController = frappe.ui.form.Controller.extend({
-	setup: function () {
+erpnext.LeadController = class LeadController extends frappe.ui.form.Controller {
+	setup () {
 		this.frm.make_methods = {
 			'Customer': this.make_customer,
 			'Quotation': this.make_quotation,
@@ -13,9 +13,9 @@
 		};
 
 		this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
-	},
+	}
 
-	onload: function () {
+	onload () {
 		this.frm.set_query("customer", function (doc, cdt, cdn) {
 			return { query: "erpnext.controllers.queries.customer_query" }
 		});
@@ -27,9 +27,9 @@
 		this.frm.set_query("contact_by", function (doc, cdt, cdn) {
 			return { query: "frappe.core.doctype.user.user.user_query" }
 		});
-	},
+	}
 
-	refresh: function () {
+	refresh () {
 		let doc = this.frm.doc;
 		erpnext.toggle_naming_series();
 		frappe.dynamic_link = { doc: doc, fieldname: 'name', doctype: 'Lead' }
@@ -45,47 +45,47 @@
 		} else {
 			frappe.contacts.clear_address_and_contact(this.frm);
 		}
-	},
+	}
 
-	make_customer: function () {
+	make_customer () {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.crm.doctype.lead.lead.make_customer",
 			frm: cur_frm
 		})
-	},
+	}
 
-	make_opportunity: function () {
+	make_opportunity () {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.crm.doctype.lead.lead.make_opportunity",
 			frm: cur_frm
 		})
-	},
+	}
 
-	make_quotation: function () {
+	make_quotation () {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.crm.doctype.lead.lead.make_quotation",
 			frm: cur_frm
 		})
-	},
+	}
 
-	organization_lead: function () {
+	organization_lead () {
 		this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
 		this.frm.toggle_reqd("company_name", this.frm.doc.organization_lead);
-	},
+	}
 
-	company_name: function () {
+	company_name () {
 		if (this.frm.doc.organization_lead && !this.frm.doc.lead_name) {
 			this.frm.set_value("lead_name", this.frm.doc.company_name);
 		}
-	},
+	}
 
-	contact_date: function () {
+	contact_date () {
 		if (this.frm.doc.contact_date) {
 			let d = moment(this.frm.doc.contact_date);
 			d.add(1, "day");
 			this.frm.set_value("ends_on", d.format(frappe.defaultDatetimeFormat));
 		}
 	}
-});
+};
 
-$.extend(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));
+extend_cscript(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));
diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js
index ac374a9..43e1b99 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.js
+++ b/erpnext/crm/doctype/opportunity/opportunity.js
@@ -145,8 +145,8 @@
 })
 
 // TODO commonify this code
-erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
-	onload: function() {
+erpnext.crm.Opportunity = class Opportunity extends frappe.ui.form.Controller {
+	onload() {
 
 		if(!this.frm.doc.status) {
 			frm.set_value('status', 'Open');
@@ -159,9 +159,9 @@
 		}
 
 		this.setup_queries();
-	},
+	}
 
-	setup_queries: function() {
+	setup_queries() {
 		var me = this;
 
 		if(this.frm.fields_dict.contact_by.df.options.match(/^User/)) {
@@ -185,17 +185,17 @@
 		else if (me.frm.doc.opportunity_from == "Customer") {
 			me.frm.set_query('party_name', erpnext.queries['customer']);
 		}
-	},
+	}
 
-	create_quotation: function() {
+	create_quotation() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
 			frm: cur_frm
 		})
 	}
-});
+};
 
-$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
 
 cur_frm.cscript.item_code = function(doc, cdt, cdn) {
 	var d = locals[cdt][cdn];
@@ -213,4 +213,4 @@
 			}
 		})
 	}
-}
\ No newline at end of file
+}
diff --git a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js
index b59d848..68e7780 100644
--- a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js
+++ b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js
@@ -72,8 +72,8 @@
 });
 
 
-education.StudentsEditor = Class.extend({
-	init: function(frm, wrapper, students) {
+education.StudentsEditor = class StudentsEditor {
+	constructor(frm, wrapper, students) {
 		this.wrapper = wrapper;
 		this.frm = frm;
 		if(students.length > 0) {
@@ -81,8 +81,8 @@
 		} else {
 			this.show_empty_state();
 		}
-	},
-	make: function(frm, students) {
+	}
+	make(frm, students) {
 		var me = this;
 
 		$(this.wrapper).empty();
@@ -173,13 +173,13 @@
 		});
 
 		$(htmls.join("")).appendTo(me.wrapper);
-	},
+	}
 
-	show_empty_state: function() {
+	show_empty_state() {
 		$(this.wrapper).html(
 			`<div class="text-center text-muted" style="line-height: 100px;">
 				${__("No Students in")} ${this.frm.doc.student_group}
 			</div>`
 		);
 	}
-});
+};
diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py
index f0a05ed..5d5b2e1 100644
--- a/erpnext/erpnext_integrations/connectors/shopify_connection.py
+++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py
@@ -335,13 +335,13 @@
 
 	if not last_order_id:
 		if shopify_settings.sync_based_on == 'Date':
-			url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&created_at_min={0}&since_id=0".format(
+			url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&created_at_min={0}&since_id=0".format(
 				get_datetime(shopify_settings.from_date)), shopify_settings)
 		else:
-			url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(
+			url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(
 				shopify_settings.from_order_id), shopify_settings)
 	else:
-		url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings)
+		url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings)
 
 	return url
 
diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
index 6dedaa8..a505ee0 100644
--- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
+++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
@@ -1,6 +1,7 @@
 
 from __future__ import unicode_literals
 import frappe, base64, hashlib, hmac, json
+from frappe.utils import cstr
 from frappe import _
 
 def verify_request():
@@ -146,22 +147,19 @@
 
 def link_items(items_list, woocommerce_settings, sys_lang):
 	for item_data in items_list:
-		item_woo_com_id = item_data.get("product_id")
+		item_woo_com_id = cstr(item_data.get("product_id"))
 
-		if frappe.get_value("Item", {"woocommerce_id": item_woo_com_id}):
-			#Edit Item
-			item = frappe.get_doc("Item", {"woocommerce_id": item_woo_com_id})
-		else:
+		if not frappe.db.get_value("Item", {"woocommerce_id": item_woo_com_id}, 'name'):
 			#Create Item
 			item = frappe.new_doc("Item")
+			item.item_code = _("woocommerce - {0}", sys_lang).format(item_woo_com_id)
+			item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang)
+			item.item_group = _("WooCommerce Products", sys_lang)
 
-		item.item_name = item_data.get("name")
-		item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id"))
-		item.woocommerce_id = item_data.get("product_id")
-		item.item_group = _("WooCommerce Products", sys_lang)
-		item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang)
-		item.flags.ignore_mandatory = True
-		item.save()
+			item.item_name = item_data.get("name")
+			item.woocommerce_id = item_woo_com_id
+			item.flags.ignore_mandatory = True
+			item.save()
 
 def create_sales_order(order, woocommerce_settings, customer_name, sys_lang):
 	new_sales_order = frappe.new_doc("Sales Order")
@@ -194,12 +192,12 @@
 
 	for item in order.get("line_items"):
 		woocomm_item_id = item.get("product_id")
-		found_item = frappe.get_doc("Item", {"woocommerce_id": woocomm_item_id})
+		found_item = frappe.get_doc("Item", {"woocommerce_id": cstr(woocomm_item_id)})
 
 		ordered_items_tax = item.get("total_tax")
 
-		new_sales_order.append("items",{
-			"item_code": found_item.item_code,
+		new_sales_order.append("items", {
+			"item_code": found_item.name,
 			"item_name": found_item.item_name,
 			"description": found_item.item_name,
 			"delivery_date": new_sales_order.delivery_date,
@@ -207,7 +205,7 @@
 			"qty": item.get("quantity"),
 			"rate": item.get("price"),
 			"warehouse": woocommerce_settings.warehouse or default_warehouse
-			})
+		})
 
 		add_tax_details(new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account)
 
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
index cbdf906..381c5e5 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
@@ -30,14 +30,14 @@
 		webhooks = ["orders/create", "orders/paid", "orders/fulfilled"]
 		# url = get_shopify_url('admin/webhooks.json', self)
 		created_webhooks = [d.method for d in self.webhooks]
-		url = get_shopify_url('admin/api/2020-04/webhooks.json', self)
+		url = get_shopify_url('admin/api/2021-04/webhooks.json', self)
 		for method in webhooks:
 			session = get_request_session()
 			try:
 				res = session.post(url, data=json.dumps({
 					"webhook": {
 						"topic": method,
-						"address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'),
+						"address": get_webhook_address(connector_name='shopify_connection', method='store_request_data', force_https=True),
 						"format": "json"
 						}
 					}), headers=get_header(self))
@@ -56,7 +56,7 @@
 		deleted_webhooks = []
 
 		for d in self.webhooks:
-			url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self)
+			url = get_shopify_url('admin/api/2021-04/webhooks/{0}.json'.format(d.webhook_id), self)
 			try:
 				res = session.delete(url, headers=get_header(self))
 				res.raise_for_status()
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py
index 7866fde..2af57f4 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py
@@ -32,10 +32,12 @@
 		raise e
 
 def create_customer_address(customer, shopify_customer):
-	if not shopify_customer.get("addresses"):
-		return
+	addresses = shopify_customer.get("addresses", [])
 
-	for i, address in enumerate(shopify_customer.get("addresses")):
+	if not addresses and "default_address" in shopify_customer:
+		addresses.append(shopify_customer["default_address"])
+
+	for i, address in enumerate(addresses):
 		address_title, address_type = get_address_title_and_type(customer.customer_name, i)
 		try :
 			frappe.get_doc({
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
index f9f0bb3..16efb6c 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
@@ -8,7 +8,7 @@
 shopify_variants_attr_list = ["option1", "option2", "option3"]
 
 def sync_item_from_shopify(shopify_settings, item):
-	url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
+	url = get_shopify_url("admin/api/2021-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
 	session = get_request_session()
 
 	try:
diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py
index 362f6cf..3840e78 100644
--- a/erpnext/erpnext_integrations/utils.py
+++ b/erpnext/erpnext_integrations/utils.py
@@ -28,7 +28,7 @@
 
 	return innerfn
 
-def get_webhook_address(connector_name, method, exclude_uri=False):
+def get_webhook_address(connector_name, method, exclude_uri=False, force_https=False):
 	endpoint = "erpnext.erpnext_integrations.connectors.{0}.{1}".format(connector_name, method)
 
 	if exclude_uri:
@@ -39,7 +39,11 @@
 	except RuntimeError:
 		url = "http://localhost:8000"
 
-	server_url = '{uri.scheme}://{uri.netloc}/api/method/{endpoint}'.format(uri=urlparse(url), endpoint=endpoint)
+	url_data = urlparse(url)
+	scheme = "https" if force_https else url_data.scheme
+	netloc = url_data.netloc
+
+	server_url = f"{scheme}://{netloc}/api/method/{endpoint}"
 
 	return server_url
 
diff --git a/erpnext/healthcare/doctype/exercise_type/exercise_type.js b/erpnext/healthcare/doctype/exercise_type/exercise_type.js
index b49b00e..0614604 100644
--- a/erpnext/healthcare/doctype/exercise_type/exercise_type.js
+++ b/erpnext/healthcare/doctype/exercise_type/exercise_type.js
@@ -9,14 +9,14 @@
 	}
 });
 
-erpnext.ExerciseEditor = Class.extend({
-	init: function(frm, wrapper) {
+erpnext.ExerciseEditor = class ExerciseEditor {
+	constructor(frm, wrapper) {
 		this.wrapper = wrapper;
 		this.frm = frm;
 		this.make(frm, wrapper);
-	},
+	}
 
-	make: function(frm, wrapper) {
+	make(frm, wrapper) {
 		$(this.wrapper).empty();
 
 		this.exercise_toolbar = $('<p>\
@@ -38,9 +38,9 @@
 			this.make_cards(frm);
 			this.make_buttons(frm);
 		}
-	},
+	}
 
-	make_cards: function(frm) {
+	make_cards(frm) {
 		var me = this;
 		$(me.exercise_cards).empty();
 
@@ -60,9 +60,9 @@
 					</div>
 			</div>`, {image_src: step.image, title: step.title, description: step.description, col_id: "col-"+i, card_id: "card-"+i, id: i})).appendTo(me.row);
 		});
-	},
+	}
 
-	make_buttons: function(frm) {
+	make_buttons(frm) {
 		let me = this;
 		$('.btn-edit').on('click', function() {
 			let id = $(this).attr('data-id');
@@ -82,9 +82,9 @@
 				frm.dirty();
 			}, 300);
 		});
-	},
+	}
 
-	show_add_card_dialog: function(frm) {
+	show_add_card_dialog(frm) {
 		let me = this;
 		let d = new frappe.ui.Dialog({
 			title: __('Add Exercise Step'),
@@ -137,9 +137,9 @@
 			primary_action_label: __('Add')
 		});
 		d.show();
-	},
+	}
 
-	show_edit_card_dialog: function(frm, id) {
+	show_edit_card_dialog(frm, id) {
 		let new_dialog = new frappe.ui.Dialog({
 			title: __("Edit Exercise Step"),
 			fields: [
@@ -183,4 +183,4 @@
 		});
 		new_dialog.show();
 	}
-});
+};
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index bb6cd8b..a1d37e2 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -15,10 +15,11 @@
 
 develop_version = '13.x.x-develop'
 
-app_include_js = "/assets/js/erpnext.min.js"
-app_include_css = "/assets/css/erpnext.css"
-web_include_js = "/assets/js/erpnext-web.min.js"
-web_include_css = "/assets/css/erpnext-web.css"
+app_include_js = "erpnext.bundle.js"
+app_include_css = "erpnext.bundle.css"
+web_include_js = "erpnext-web.bundle.js"
+web_include_css = "erpnext-web.bundle.css"
+email_css = "email_erpnext.bundle.css"
 
 doctype_js = {
 	"Address": "public/js/address.js",
@@ -426,7 +427,8 @@
 		'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption',
 		'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period',
 		'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries',
-		'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields'
+		'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields',
+		'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount'
 	},
 	'United Arab Emirates': {
 		'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data',
diff --git a/erpnext/hr/doctype/designation/designation.json b/erpnext/hr/doctype/designation/designation.json
index 4c3888b..bab6b90 100644
--- a/erpnext/hr/doctype/designation/designation.json
+++ b/erpnext/hr/doctype/designation/designation.json
@@ -182,6 +182,10 @@
    "share": 1,
    "submit": 0,
    "write": 1
+  },
+  {
+   "read": 1,
+   "role": "Sales User"
   }
  ],
  "quick_entry": 1,
@@ -191,4 +195,4 @@
  "track_changes": 0,
  "track_seen": 0,
  "track_views": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/employee/employee.js b/erpnext/hr/doctype/employee/employee.js
index c21d4b8..5639cc9 100755
--- a/erpnext/hr/doctype/employee/employee.js
+++ b/erpnext/hr/doctype/employee/employee.js
@@ -2,8 +2,8 @@
 // License: GNU General Public License v3. See license.txt
 
 frappe.provide("erpnext.hr");
-erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({
-	setup: function() {
+erpnext.hr.EmployeeController = class EmployeeController extends frappe.ui.form.Controller {
+	setup() {
 		this.frm.fields_dict.user_id.get_query = function(doc, cdt, cdn) {
 			return {
 				query: "frappe.core.doctype.user.user.user_query",
@@ -12,30 +12,30 @@
 		}
 		this.frm.fields_dict.reports_to.get_query = function(doc, cdt, cdn) {
 			return { query: "erpnext.controllers.queries.employee_query"} }
-	},
+	}
 
-	refresh: function() {
+	refresh() {
 		var me = this;
 		erpnext.toggle_naming_series();
-	},
+	}
 
-	date_of_birth: function() {
+	date_of_birth() {
 		return cur_frm.call({
 			method: "get_retirement_date",
 			args: {date_of_birth: this.frm.doc.date_of_birth}
 		});
-	},
+	}
 
-	salutation: function() {
+	salutation() {
 		if(this.frm.doc.salutation) {
 			this.frm.set_value("gender", {
 				"Mr": "Male",
 				"Ms": "Female"
 			}[this.frm.doc.salutation]);
 		}
-	},
+	}
 
-});
+};
 frappe.ui.form.on('Employee',{
 	setup: function(frm) {
 		frm.set_query("leave_policy", function() {
diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js
index 3205a92..ab965d5 100644
--- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js
+++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js
@@ -68,13 +68,13 @@
 	}
 }
 
-erpnext.MarkedEmployee = Class.extend({
-	init: function(frm, wrapper, employee) {
+erpnext.MarkedEmployee = class MarkedEmployee {
+	constructor(frm, wrapper, employee) {
 		this.wrapper = wrapper;
 		this.frm = frm;
 		this.make(frm, employee);
-	},
-	make: function(frm, employee) {
+	}
+	make(frm, employee) {
 		var me = this;
 		$(this.wrapper).empty();
 
@@ -104,16 +104,16 @@
 				})).appendTo(row);
 		});
 	}
-});
+};
 
 
-erpnext.EmployeeSelector = Class.extend({
-	init: function(frm, wrapper, employee) {
+erpnext.EmployeeSelector = class EmployeeSelector {
+	constructor(frm, wrapper, employee) {
 		this.wrapper = wrapper;
 		this.frm = frm;
 		this.make(frm, employee);
-	},
-	make: function(frm, employee) {
+	}
+	make(frm, employee) {
 		var me = this;
 
 		$(this.wrapper).empty();
@@ -266,6 +266,6 @@
 
 		mark_employee_toolbar.appendTo($(this.wrapper));
 	}
-});
+};
 
 
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json
index e3e6e80..a268c15 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.json
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.json
@@ -14,6 +14,7 @@
   "column_break_5",
   "expense_approver",
   "approval_status",
+  "delivery_trip",
   "is_paid",
   "expense_details",
   "expenses",
@@ -365,13 +366,20 @@
    "label": "Total Taxes and Charges",
    "options": "Company:company:default_currency",
    "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.delivery_trip",
+   "fieldname": "delivery_trip",
+   "fieldtype": "Link",
+   "label": "Delivery Trip",
+   "options": "Delivery Trip"
   }
  ],
  "icon": "fa fa-money",
  "idx": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-09-18 17:26:09.703215",
+ "modified": "2021-05-04 05:35:12.040199",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Expense Claim",
diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.js b/erpnext/hr/doctype/upload_attendance/upload_attendance.js
index 29aa854..bbafc82 100644
--- a/erpnext/hr/doctype/upload_attendance/upload_attendance.js
+++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.js
@@ -5,19 +5,19 @@
 
 frappe.provide("erpnext.hr");
 
-erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
-	onload: function() {
+erpnext.hr.AttendanceControlPanel = class AttendanceControlPanel extends frappe.ui.form.Controller {
+	onload() {
 		this.frm.set_value("att_fr_date", frappe.datetime.get_today());
 		this.frm.set_value("att_to_date", frappe.datetime.get_today());
-	},
+	}
 
-	refresh: function() {
+	refresh() {
 		this.frm.disable_save();
 		this.show_upload();
 		this.setup_import_progress();
-	},
+	}
 
-	get_template:function() {
+	get_template() {
 		if(!this.frm.doc.att_fr_date || !this.frm.doc.att_to_date) {
 			frappe.msgprint(__("Attendance From Date and Attendance To Date is mandatory"));
 			return;
@@ -28,7 +28,7 @@
 			from_date: this.frm.doc.att_fr_date,
 			to_date: this.frm.doc.att_to_date,
 		});
-	},
+	}
 
 	show_upload() {
 		var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty();
@@ -36,7 +36,7 @@
 			wrapper: $wrapper,
 			method: 'erpnext.hr.doctype.upload_attendance.upload_attendance.upload'
 		});
-	},
+	}
 
 	setup_import_progress() {
 		var $log_wrapper = $(this.frm.fields_dict.import_log.wrapper).empty();
@@ -64,6 +64,6 @@
 			}
 		});
 	}
-})
+}
 
 cur_frm.cscript = new erpnext.hr.AttendanceControlPanel({frm: cur_frm});
diff --git a/erpnext/hr/notification/training_feedback/training_feedback.json b/erpnext/hr/notification/training_feedback/training_feedback.json
index 2cc064f..92b68a9 100644
--- a/erpnext/hr/notification/training_feedback/training_feedback.json
+++ b/erpnext/hr/notification/training_feedback/training_feedback.json
@@ -1,5 +1,6 @@
 {
  "attach_print": 0,
+ "channel": "Email",
  "creation": "2017-08-11 03:17:11.769210",
  "days_in_advance": 0,
  "docstatus": 0,
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
index ddbcdfd..2371d96 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
@@ -45,8 +45,8 @@
 })
 
 // TODO commonify this code
-erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
-	refresh: function() {
+erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frappe.ui.form.Controller {
+	refresh() {
 		frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
 
 		var me = this;
@@ -76,21 +76,21 @@
 				});
 			}, __('Create'));
 		}
-	},
+	}
 
-	start_date: function(doc, cdt, cdn) {
+	start_date(doc, cdt, cdn) {
 		this.set_no_of_visits(doc, cdt, cdn);
-	},
+	}
 
-	end_date: function(doc, cdt, cdn) {
+	end_date(doc, cdt, cdn) {
 		this.set_no_of_visits(doc, cdt, cdn);
-	},
+	}
 
-	periodicity: function(doc, cdt, cdn) {
+	periodicity(doc, cdt, cdn) {
 		this.set_no_of_visits(doc, cdt, cdn);
-	},
+	}
 
-	set_no_of_visits: function(doc, cdt, cdn) {
+	set_no_of_visits(doc, cdt, cdn) {
 		var item = frappe.get_doc(cdt, cdn);
 
 		if (item.start_date && item.end_date && item.periodicity) {
@@ -112,8 +112,8 @@
 			var no_of_visits = cint(date_diff / days_in_period[item.periodicity]);
 			frappe.model.set_value(item.doctype, item.name, "no_of_visits", no_of_visits);
 		}
-	},
-});
+	}
+};
 
-$.extend(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm}));
 
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
index 4cbb02a..5032530 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
@@ -41,8 +41,8 @@
 })
 
 // TODO commonify this code
-erpnext.maintenance.MaintenanceVisit = frappe.ui.form.Controller.extend({
-	refresh: function() {
+erpnext.maintenance.MaintenanceVisit = class MaintenanceVisit extends frappe.ui.form.Controller {
+	refresh() {
 		frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
 
 		var me = this;
@@ -96,7 +96,7 @@
 					})
 				}, __("Get Items From"));
 		}
-	},
-});
+	}
+};
 
-$.extend(cur_frm.cscript, new erpnext.maintenance.MaintenanceVisit({frm: cur_frm}));
\ No newline at end of file
+extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceVisit({frm: cur_frm}));
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index fbfd801..44f841f 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -29,7 +29,10 @@
 
 		frm.set_query("item", function() {
 			return {
-				query: "erpnext.manufacturing.doctype.bom.bom.item_query"
+				query: "erpnext.manufacturing.doctype.bom.bom.item_query",
+				filters: {
+					"is_stock_item": 1
+				}
 			};
 		});
 
@@ -357,16 +360,16 @@
 	}
 });
 
-erpnext.bom.BomController = erpnext.TransactionController.extend({
-	conversion_rate: function(doc) {
+erpnext.bom.BomController = class BomController extends erpnext.TransactionController {
+	conversion_rate(doc) {
 		if(this.frm.doc.currency === this.get_company_currency()) {
 			this.frm.set_value("conversion_rate", 1.0);
 		} else {
 			erpnext.bom.update_cost(doc);
 		}
-	},
+	}
 
-	item_code: function(doc, cdt, cdn){
+	item_code(doc, cdt, cdn){
 		var scrap_items = false;
 		var child = locals[cdt][cdn];
 		if (child.doctype == 'BOM Scrap Item') {
@@ -378,19 +381,19 @@
 		}
 
 		get_bom_material_detail(doc, cdt, cdn, scrap_items);
-	},
+	}
 
-	buying_price_list: function(doc) {
+	buying_price_list(doc) {
 		this.apply_price_list();
-	},
+	}
 
-	plc_conversion_rate: function(doc) {
+	plc_conversion_rate(doc) {
 		if (!this.in_apply_price_list) {
 			this.apply_price_list(null, true);
 		}
-	},
+	}
 
-	conversion_factor: function(doc, cdt, cdn) {
+	conversion_factor(doc, cdt, cdn) {
 		if (frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
 			var item = frappe.get_doc(cdt, cdn);
 			frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
@@ -399,10 +402,10 @@
 			this.toggle_conversion_factor(item);
 			this.frm.events.update_cost(this.frm);
 		}
-	},
-});
+	}
+};
 
-$.extend(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm}));
 
 cur_frm.cscript.hour_rate = function(doc) {
 	erpnext.bom.calculate_op_cost(doc);
@@ -659,4 +662,4 @@
 		frm.set_value("operations", []);
 	}
 	toggle_operations(frm);
-});
\ No newline at end of file
+});
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 979f7ca..d1f6385 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -973,6 +973,9 @@
 		if not has_variants:
 			query_filters["has_variants"] = 0
 
+	if filters and filters.get("is_stock_item"):
+		query_filters["is_stock_item"] = 1
+		
 	return frappe.get_all("Item",
 		fields = fields, filters=query_filters,
 		or_filters = or_cond_filters, order_by=order_by,
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 9ef949c..f94644d 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -774,4 +774,8 @@
 erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021
 erpnext.patches.v13_0.update_shipment_status
 erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting
+erpnext.patches.v12_0.add_ewaybill_validity_field
+erpnext.patches.v13_0.germany_make_custom_fields
+erpnext.patches.v13_0.germany_fill_debtor_creditor_number
 erpnext.patches.v13_0.set_pos_closing_as_failed
+erpnext.patches.v13_0.update_timesheet_changes
diff --git a/erpnext/patches/v12_0/add_ewaybill_validity_field.py b/erpnext/patches/v12_0/add_ewaybill_validity_field.py
new file mode 100644
index 0000000..87d98f1
--- /dev/null
+++ b/erpnext/patches/v12_0/add_ewaybill_validity_field.py
@@ -0,0 +1,16 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	custom_fields = {
+		'Sales Invoice': [
+			dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
+				depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill')
+		]
+	}
+	create_custom_fields(custom_fields, update=True)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
index 06331d7..a6471eb 100644
--- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
+++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
@@ -44,9 +44,11 @@
 		# make current item's tax map
 		item_tax_map = {}
 		for d in old_item_taxes[item_code]:
-			item_tax_map[d.tax_type] = d.tax_rate
+			if d.tax_type not in item_tax_map:
+				item_tax_map[d.tax_type] = d.tax_rate
 
-		item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code)
+		tax_types = []
+		item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code, tax_types=tax_types)
 
 		# update the item tax table
 		frappe.db.sql("delete from `tabItem Tax` where parent=%s and parenttype='Item'", item_code)
@@ -68,7 +70,7 @@
 								and item_tax_template is NULL""".format(dt), as_dict=1):
 			item_tax_map = json.loads(d.item_tax_rate)
 			item_tax_template_name = get_item_tax_template(item_tax_templates,
-				item_tax_map, d.item_code, d.parenttype, d.parent)
+				item_tax_map, d.item_code, d.parenttype, d.parent, tax_types=tax_types)
 			frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template_name)
 
 	frappe.db.auto_commit_on_many_writes = False
@@ -78,7 +80,7 @@
 	settings.determine_address_tax_category_from = "Billing Address"
 	settings.save()
 
-def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None):
+def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None, tax_types=None):
 	# search for previously created item tax template by comparing tax maps
 	for template, item_tax_template_map in iteritems(item_tax_templates):
 		if item_tax_map == item_tax_template_map:
@@ -126,7 +128,9 @@
 		account_type = frappe.get_cached_value("Account", tax_type, "account_type")
 
 		if tax_type and account_type in ('Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation'):
-			item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
+			if tax_type not in tax_types:
+				item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
+				tax_types.append(tax_type)
 			item_tax_templates.setdefault(item_tax_template.title, {})
 			item_tax_templates[item_tax_template.title][tax_type] = tax_rate
 	if item_tax_template.get("taxes"):
diff --git a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
new file mode 100644
index 0000000..11e1e9b
--- /dev/null
+++ b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
@@ -0,0 +1,31 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+
+
+def execute():
+	"""Move account number into the new custom field debtor_creditor_number.
+
+	German companies used to use a dedicated payable/receivable account for
+	every party to mimick party accounts in the external accounting software
+	"DATEV". This is no longer necessary. The reference ID for DATEV will be
+	stored in a new custom field "debtor_creditor_number". 
+	"""
+	company_list = frappe.get_all('Company', filters={'country': 'Germany'})
+
+	for company in company_list:
+		party_account_list = frappe.get_all('Party Account', filters={'company': company.name}, fields=['name', 'account', 'debtor_creditor_number'])
+		for party_account in party_account_list:
+			if (not party_account.account) or party_account.debtor_creditor_number:
+				# account empty or debtor_creditor_number already filled
+				continue
+
+			account_number = frappe.db.get_value('Account', party_account.account, 'account_number')
+			if not account_number:
+				continue
+
+			frappe.db.set_value('Party Account', party_account.name, 'debtor_creditor_number', account_number)
+			frappe.db.set_value('Party Account', party_account.name, 'account', '')
diff --git a/erpnext/patches/v13_0/germany_make_custom_fields.py b/erpnext/patches/v13_0/germany_make_custom_fields.py
new file mode 100644
index 0000000..41ab945
--- /dev/null
+++ b/erpnext/patches/v13_0/germany_make_custom_fields.py
@@ -0,0 +1,20 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from erpnext.regional.germany.setup import make_custom_fields
+
+
+def execute():
+	"""Execute the make_custom_fields method for german companies.
+
+	It is usually run once at setup of a new company. Since it's new, run it
+	once for existing companies as well.
+	"""
+	company_list = frappe.get_all('Company', filters = {'country': 'Germany'})
+	if not company_list:
+		return
+
+	make_custom_fields()
diff --git a/erpnext/patches/v13_0/update_timesheet_changes.py b/erpnext/patches/v13_0/update_timesheet_changes.py
new file mode 100644
index 0000000..93b7f8e
--- /dev/null
+++ b/erpnext/patches/v13_0/update_timesheet_changes.py
@@ -0,0 +1,25 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+	frappe.reload_doc("projects", "doctype", "timesheet")
+	frappe.reload_doc("projects", "doctype", "timesheet_detail")
+
+	if frappe.db.has_column("Timesheet Detail", "billable"):
+		rename_field("Timesheet Detail", "billable", "is_billable")
+
+	base_currency = frappe.defaults.get_global_default('currency')
+
+	frappe.db.sql("""UPDATE `tabTimesheet Detail`
+			SET base_billing_rate = billing_rate,
+			base_billing_amount = billing_amount,
+			base_costing_rate = costing_rate,
+			base_costing_amount = costing_amount""")
+
+	frappe.db.sql("""UPDATE `tabTimesheet`
+			SET currency = '{0}',
+			exchange_rate = 1.0,
+			base_total_billable_amount = total_billable_amount,
+			base_total_billed_amount = total_billed_amount,
+			base_total_costing_amount = total_costing_amount""".format(base_currency))
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py b/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
index 3af6622..8c60b5b 100644
--- a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
+++ b/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
@@ -51,7 +51,7 @@
 
 def get_timelog_data(data):
 	return {
-		'billable': data.billable,
+		'is_billable': data.billable,
 		'from_time': data.from_time,
 		'hours': data.hours,
 		'to_time': data.to_time,
diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.json b/erpnext/payroll/notification/retention_bonus/retention_bonus.json
index 50db033..37381fa 100644
--- a/erpnext/payroll/notification/retention_bonus/retention_bonus.json
+++ b/erpnext/payroll/notification/retention_bonus/retention_bonus.json
@@ -1,5 +1,6 @@
 {
  "attach_print": 0,
+ "channel": "Email",
  "condition": "doc.docstatus==1",
  "creation": "2018-05-15 18:52:36.362838",
  "date_changed": "bonus_payment_date",
diff --git a/erpnext/projects/doctype/activity_type/activity_type.js b/erpnext/projects/doctype/activity_type/activity_type.js
index 7eb3571..f1ba882 100644
--- a/erpnext/projects/doctype/activity_type/activity_type.js
+++ b/erpnext/projects/doctype/activity_type/activity_type.js
@@ -1,4 +1,8 @@
 frappe.ui.form.on("Activity Type", {
+	onload: function(frm) {
+		frm.set_currency_labels(["billing_rate", "costing_rate"], frappe.defaults.get_global_default('currency'));
+	},
+
 	refresh: function(frm) {
 		frm.add_custom_button(__("Activity Cost per Employee"), function() {
 			frappe.route_options = {"activity_type": frm.doc.name};
diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json
index 3cdfcb2..2570df7 100644
--- a/erpnext/projects/doctype/project/project.json
+++ b/erpnext/projects/doctype/project/project.json
@@ -458,7 +458,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "max_attachments": 4,
- "modified": "2020-09-02 11:54:01.223620",
+ "modified": "2021-04-28 16:36:11.654632",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Project",
@@ -495,11 +495,11 @@
   }
  ],
  "quick_entry": 1,
- "search_fields": "customer, status, priority, is_active",
+ "search_fields": "project_name,customer, status, priority, is_active",
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
  "timeline_field": "customer",
  "title_field": "project_name",
  "track_seen": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index d21ac0f..2b0c3ab 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -37,7 +37,7 @@
 		emp = make_employee("test_employee_6@salary.com")
 
 		make_salary_structure_for_timesheet(emp)
-		timesheet = make_timesheet(emp, simulate=True, billable=1)
+		timesheet = make_timesheet(emp, simulate=True, is_billable=1)
 
 		self.assertEqual(timesheet.total_hours, 2)
 		self.assertEqual(timesheet.total_billable_hours, 2)
@@ -49,7 +49,7 @@
 		emp = make_employee("test_employee_6@salary.com")
 
 		make_salary_structure_for_timesheet(emp)
-		timesheet = make_timesheet(emp, simulate=True, billable=0)
+		timesheet = make_timesheet(emp, simulate=True, is_billable=0)
 
 		self.assertEqual(timesheet.total_hours, 2)
 		self.assertEqual(timesheet.total_billable_hours, 0)
@@ -61,7 +61,7 @@
 		emp = make_employee("test_employee_6@salary.com", company="_Test Company")
 
 		salary_structure = make_salary_structure_for_timesheet(emp)
-		timesheet = make_timesheet(emp, simulate = True, billable=1)
+		timesheet = make_timesheet(emp, simulate = True, is_billable=1)
 		salary_slip = make_salary_slip(timesheet.name)
 		salary_slip.submit()
 
@@ -82,7 +82,7 @@
 	def test_sales_invoice_from_timesheet(self):
 		emp = make_employee("test_employee_6@salary.com")
 
-		timesheet = make_timesheet(emp, simulate=True, billable=1)
+		timesheet = make_timesheet(emp, simulate=True, is_billable=1)
 		sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer')
 		sales_invoice.due_date = nowdate()
 		sales_invoice.submit()
@@ -100,7 +100,7 @@
 		emp = make_employee("test_employee_6@salary.com")
 		project = frappe.get_value("Project", {"project_name": "_Test Project"})
 
-		timesheet = make_timesheet(emp, simulate=True, billable=1, project=project, company='_Test Company')
+		timesheet = make_timesheet(emp, simulate=True, is_billable=1, project=project, company='_Test Company')
 		sales_invoice = create_sales_invoice(do_not_save=True)
 		sales_invoice.project = project
 		sales_invoice.submit()
@@ -171,13 +171,13 @@
 	return salary_structure
 
 
-def make_timesheet(employee, simulate=False, billable = 0, activity_type="_Test Activity Type", project=None, task=None, company=None):
+def make_timesheet(employee, simulate=False, is_billable = 0, activity_type="_Test Activity Type", project=None, task=None, company=None):
 	update_activity_type(activity_type)
 	timesheet = frappe.new_doc("Timesheet")
 	timesheet.employee = employee
 	timesheet.company = company or '_Test Company'
 	timesheet_detail = timesheet.append('time_logs', {})
-	timesheet_detail.billable = billable
+	timesheet_detail.is_billable = is_billable
 	timesheet_detail.activity_type = activity_type
 	timesheet_detail.from_time = now_datetime()
 	timesheet_detail.hours = 2
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 43a57e5..84c7b81 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -90,17 +90,99 @@
 		}
 		if(frm.doc.per_billed > 0) {
 			frm.fields_dict["time_logs"].grid.toggle_enable("billing_hours", false);
-			frm.fields_dict["time_logs"].grid.toggle_enable("billable", false);
+			frm.fields_dict["time_logs"].grid.toggle_enable("is_billable", false);
 		}
+		frm.trigger('setup_filters');
+		frm.trigger('set_dynamic_field_label');
+	},
+
+	customer: function(frm) {
+		frm.set_query('parent_project', function(doc) {
+			return {
+				filters: {
+					"customer": doc.customer
+				}
+			};
+		});
+		frm.set_query('project', 'time_logs', function(doc) {
+			return {
+				filters: {
+					"customer": doc.customer
+				}
+			};
+		});
+		frm.refresh();
+	},
+
+	currency: function(frm) {
+		let base_currency = frappe.defaults.get_global_default('currency');
+		if (base_currency != frm.doc.currency) {
+			frappe.call({
+				method: "erpnext.setup.utils.get_exchange_rate",
+				args: {
+					from_currency: frm.doc.currency,
+					to_currency: base_currency
+				},
+				callback: function(r) {
+					if (r.message) {
+						frm.set_value('exchange_rate', flt(r.message));
+						frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + " = [?] " + base_currency);
+					}
+				}
+			});
+		}
+		frm.trigger('set_dynamic_field_label');
+	},
+
+	exchange_rate: function(frm) {
+		$.each(frm.doc.time_logs, function(i, d) {
+			calculate_billing_costing_amount(frm, d.doctype, d.name);
+		});
+		calculate_time_and_amount(frm);
+	},
+
+	set_dynamic_field_label: function(frm) {
+		let base_currency = frappe.defaults.get_global_default('currency');
+		frm.set_currency_labels(["base_total_costing_amount", "base_total_billable_amount", "base_total_billed_amount"], base_currency);
+		frm.set_currency_labels(["total_costing_amount", "total_billable_amount", "total_billed_amount"], frm.doc.currency);
+
+		frm.toggle_display(["base_total_costing_amount", "base_total_billable_amount", "base_total_billed_amount"],
+			frm.doc.currency != base_currency);
+
+		if (frm.doc.time_logs.length > 0) {
+			frm.set_currency_labels(["base_billing_rate", "base_billing_amount", "base_costing_rate", "base_costing_amount"], base_currency, "time_logs");
+			frm.set_currency_labels(["billing_rate", "billing_amount", "costing_rate", "costing_amount"], frm.doc.currency, "time_logs");
+
+			let time_logs_grid = frm.fields_dict.time_logs.grid;
+			$.each(["base_billing_rate", "base_billing_amount", "base_costing_rate", "base_costing_amount"], function(i, d) {
+				if (frappe.meta.get_docfield(time_logs_grid.doctype, d))
+					time_logs_grid.set_column_disp(d, frm.doc.currency != base_currency);
+			});
+		}
+		frm.refresh_fields();
 	},
 
 	make_invoice: function(frm) {
+		let fields = [{
+			"fieldtype": "Link",
+			"label": __("Item Code"),
+			"fieldname": "item_code",
+			"options": "Item"
+		}];
+
+		if (!frm.doc.customer) {
+			fields.push({
+				"fieldtype": "Link",
+				"label": __("Customer"),
+				"fieldname": "customer",
+				"options": "Customer",
+				"default": frm.doc.customer
+			});
+		}
+
 		let dialog = new frappe.ui.Dialog({
-			title: __("Select Item (optional)"),
-			fields: [
-				{"fieldtype": "Link", "label": __("Item Code"), "fieldname": "item_code", "options":"Item"},
-				{"fieldtype": "Link", "label": __("Customer"), "fieldname": "customer", "options":"Customer"}
-			]
+			title: __("Create Sales Invoice"),
+			fields: fields
 		});
 
 		dialog.set_primary_action(__('Create Sales Invoice'), () => {
@@ -113,7 +195,8 @@
 				args: {
 					"source_name": frm.doc.name,
 					"item_code": args.item_code,
-					"customer": args.customer
+					"customer": frm.doc.customer || args.customer,
+					"currency": frm.doc.currency
 				},
 				freeze: true,
 				callback: function(r) {
@@ -136,8 +219,7 @@
 
 	parent_project: function(frm) {
 		set_project_in_timelog(frm);
-	},
-
+	}
 });
 
 frappe.ui.form.on("Timesheet Detail", {
@@ -171,36 +253,34 @@
 		if(frm.doc.parent_project) {
 			frappe.model.set_value(cdt, cdn, 'project', frm.doc.parent_project);
 		}
-
-		var $trigger_again = $('.form-grid').find('.grid-row').find('.btn-open-row');
-		$trigger_again.on('click', () => {
-			let $timer = $('.form-grid').find('[data-fieldname="timer"]');
-			if ($timer.get(0)) {
-				$timer.append(frappe.render_template("timesheet"));
-			}
-			frm.trigger("control_timer");
-		});
 	},
+
 	hours: function(frm, cdt, cdn) {
 		calculate_end_time(frm, cdt, cdn);
+		calculate_billing_costing_amount(frm, cdt, cdn);
+		calculate_time_and_amount(frm);
 	},
 
 	billing_hours: function(frm, cdt, cdn) {
 		calculate_billing_costing_amount(frm, cdt, cdn);
+		calculate_time_and_amount(frm);
 	},
 
 	billing_rate: function(frm, cdt, cdn) {
 		calculate_billing_costing_amount(frm, cdt, cdn);
+		calculate_time_and_amount(frm);
 	},
 
 	costing_rate: function(frm, cdt, cdn) {
 		calculate_billing_costing_amount(frm, cdt, cdn);
+		calculate_time_and_amount(frm);
 	},
 
-	billable: function(frm, cdt, cdn) {
+	is_billable: function(frm, cdt, cdn) {
 		update_billing_hours(frm, cdt, cdn);
 		update_time_rates(frm, cdt, cdn);
 		calculate_billing_costing_amount(frm, cdt, cdn);
+		calculate_time_and_amount(frm);
 	},
 
 	activity_type: function(frm, cdt, cdn) {
@@ -208,7 +288,8 @@
 			method: "erpnext.projects.doctype.timesheet.timesheet.get_activity_cost",
 			args: {
 				employee: frm.doc.employee,
-				activity_type: frm.selected_doc.activity_type
+				activity_type: frm.selected_doc.activity_type,
+				currency: frm.doc.currency
 			},
 			callback: function(r){
 				if(r.message){
@@ -240,9 +321,9 @@
 	}
 };
 
-var update_billing_hours = function(frm, cdt, cdn){
-	var child = locals[cdt][cdn];
-	if(!child.billable) {
+var update_billing_hours = function(frm, cdt, cdn) {
+	let child = frappe.get_doc(cdt, cdn);
+	if (!child.is_billable) {
 		frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0);
 	} else {
 		// bill all hours by default
@@ -250,40 +331,44 @@
 	}
 };
 
-var update_time_rates = function(frm, cdt, cdn){
-	var child = locals[cdt][cdn];
-	if(!child.billable){
+var update_time_rates = function(frm, cdt, cdn) {
+	let child = frappe.get_doc(cdt, cdn);
+	if (!child.is_billable) {
 		frappe.model.set_value(cdt, cdn, 'billing_rate', 0.0);
 	}
 };
 
-var calculate_billing_costing_amount = function(frm, cdt, cdn){
-	var child = locals[cdt][cdn];
-	var billing_amount = 0.0;
-	var costing_amount = 0.0;
-
-	if(child.billing_hours && child.billable){
-		billing_amount = (child.billing_hours * child.billing_rate);
+var calculate_billing_costing_amount = function(frm, cdt, cdn) {
+	let row = frappe.get_doc(cdt, cdn);
+	let billing_amount = 0.0;
+	let base_billing_amount = 0.0;
+	let exchange_rate = flt(frm.doc.exchange_rate);
+	frappe.model.set_value(cdt, cdn, 'base_billing_rate', flt(row.billing_rate) * exchange_rate);
+	frappe.model.set_value(cdt, cdn, 'base_costing_rate', flt(row.costing_rate) * exchange_rate);
+	if (row.billing_hours && row.is_billable) {
+		base_billing_amount = flt(row.billing_hours) * flt(row.base_billing_rate);
+		billing_amount = flt(row.billing_hours) * flt(row.billing_rate);
 	}
-	costing_amount = flt(child.costing_rate * child.hours);
+
+	frappe.model.set_value(cdt, cdn, 'base_billing_amount', base_billing_amount);
+	frappe.model.set_value(cdt, cdn, 'base_costing_amount', flt(row.base_costing_rate) * flt(row.hours));
 	frappe.model.set_value(cdt, cdn, 'billing_amount', billing_amount);
-	frappe.model.set_value(cdt, cdn, 'costing_amount', costing_amount);
-	calculate_time_and_amount(frm);
+	frappe.model.set_value(cdt, cdn, 'costing_amount', flt(row.costing_rate) * flt(row.hours));
 };
 
 var calculate_time_and_amount = function(frm) {
-	var tl = frm.doc.time_logs || [];
-	var total_working_hr = 0;
-	var total_billing_hr = 0;
-	var total_billable_amount = 0;
-	var total_costing_amount = 0;
+	let tl = frm.doc.time_logs || [];
+	let total_working_hr = 0;
+	let total_billing_hr = 0;
+	let total_billable_amount = 0;
+	let total_costing_amount = 0;
 	for(var i=0; i<tl.length; i++) {
 		if (tl[i].hours) {
 			total_working_hr += tl[i].hours;
 			total_billable_amount += tl[i].billing_amount;
 			total_costing_amount += tl[i].costing_amount;
 
-			if(tl[i].billable){
+			if (tl[i].is_billable) {
 				total_billing_hr += tl[i].billing_hours;
 			}
 		}
diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json
index b286821..75f7478 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.json
+++ b/erpnext/projects/doctype/timesheet/timesheet.json
@@ -11,6 +11,9 @@
   "title",
   "naming_series",
   "company",
+  "customer",
+  "currency",
+  "exchange_rate",
   "sales_invoice",
   "column_break_3",
   "salary_slip",
@@ -30,11 +33,14 @@
   "total_hours",
   "billing_details",
   "total_billable_hours",
-  "total_billed_hours",
-  "total_costing_amount",
+  "base_total_billable_amount",
+  "base_total_billed_amount",
+  "base_total_costing_amount",
   "column_break_10",
+  "total_billed_hours",
   "total_billable_amount",
   "total_billed_amount",
+  "total_costing_amount",
   "per_billed",
   "section_break_18",
   "note",
@@ -176,7 +182,6 @@
    "default": "0",
    "fieldname": "total_hours",
    "fieldtype": "Float",
-   "in_list_view": 1,
    "label": "Total Working Hours",
    "read_only": 1
   },
@@ -199,7 +204,6 @@
    "allow_on_submit": 1,
    "fieldname": "total_billed_hours",
    "fieldtype": "Float",
-   "in_list_view": 1,
    "label": "Total Billed Hours",
    "print_hide": 1,
    "read_only": 1
@@ -209,6 +213,7 @@
    "fieldname": "total_costing_amount",
    "fieldtype": "Currency",
    "label": "Total Costing Amount",
+   "options": "currency",
    "print_hide": 1,
    "read_only": 1
   },
@@ -222,6 +227,7 @@
    "fieldname": "total_billable_amount",
    "fieldtype": "Currency",
    "label": "Total Billable Amount",
+   "options": "currency",
    "read_only": 1
   },
   {
@@ -229,6 +235,7 @@
    "fieldname": "total_billed_amount",
    "fieldtype": "Currency",
    "label": "Total Billed Amount",
+   "options": "currency",
    "print_hide": 1,
    "read_only": 1
   },
@@ -236,6 +243,7 @@
    "allow_on_submit": 1,
    "fieldname": "per_billed",
    "fieldtype": "Percent",
+   "in_list_view": 1,
    "label": "% Amount Billed",
    "no_copy": 1,
    "print_hide": 1,
@@ -265,13 +273,53 @@
    "fieldtype": "Link",
    "label": "Project",
    "options": "Project"
+  },
+  {
+   "fieldname": "customer",
+   "fieldtype": "Link",
+   "label": "Customer",
+   "options": "Customer"
+  },
+  {
+   "fetch_from": "customer.default_currency",
+   "fetch_if_empty": 1,
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "label": "Currency",
+   "options": "Currency"
+  },
+  {
+   "fieldname": "base_total_costing_amount",
+   "fieldtype": "Currency",
+   "label": "Total Costing Amount",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_total_billable_amount",
+   "fieldtype": "Currency",
+   "label": "Total Billable Amount",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_total_billed_amount",
+   "fieldtype": "Currency",
+   "label": "Total Billed Amount",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "exchange_rate",
+   "fieldtype": "Float",
+   "label": "Exchange Rate"
   }
  ],
  "icon": "fa fa-clock-o",
  "idx": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-01-08 20:51:14.590080",
+ "modified": "2021-05-18 16:10:08.249619",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Timesheet",
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index ed02f79..a3e4577 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -14,6 +14,7 @@
 from erpnext.manufacturing.doctype.workstation.workstation import (check_if_within_operating_hours,
 	WorkstationHolidayError)
 from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
+from erpnext.setup.utils import get_exchange_rate
 
 class OverlapError(frappe.ValidationError): pass
 class OverWorkLoggedError(frappe.ValidationError): pass
@@ -37,9 +38,9 @@
 		self.total_hours = 0.0
 		self.total_billable_hours = 0.0
 		self.total_billed_hours = 0.0
-		self.total_billable_amount = 0.0
-		self.total_costing_amount = 0.0
-		self.total_billed_amount = 0.0
+		self.total_billable_amount = self.base_total_billable_amount = 0.0
+		self.total_costing_amount = self.base_total_costing_amount = 0.0
+		self.total_billed_amount = self.base_total_billed_amount = 0.0
 
 		for d in self.get("time_logs"):
 			self.update_billing_hours(d)
@@ -47,10 +48,13 @@
 
 			self.total_hours += flt(d.hours)
 			self.total_costing_amount += flt(d.costing_amount)
-			if d.billable:
+			self.base_total_costing_amount += flt(d.base_costing_amount)
+			if d.is_billable:
 				self.total_billable_hours += flt(d.billing_hours)
 				self.total_billable_amount += flt(d.billing_amount)
+				self.base_total_billable_amount += flt(d.base_billing_amount)
 				self.total_billed_amount += flt(d.billing_amount) if d.sales_invoice else 0.0
+				self.base_total_billed_amount += flt(d.base_billing_amount) if d.sales_invoice else 0.0
 				self.total_billed_hours += flt(d.billing_hours) if d.sales_invoice else 0.0
 
 	def calculate_percentage_billed(self):
@@ -59,7 +63,7 @@
 			self.per_billed = (self.total_billed_amount * 100) / self.total_billable_amount
 
 	def update_billing_hours(self, args):
-		if args.billable:
+		if args.is_billable:
 			if flt(args.billing_hours) == 0.0:
 				args.billing_hours = args.hours
 		else:
@@ -133,16 +137,20 @@
 	def validate_time_logs(self):
 		for data in self.get('time_logs'):
 			self.validate_overlap(data)
-			self.validate_task_project()
+			self.set_project(data)
+			self.validate_project(data)
 
 	def validate_overlap(self, data):
 		settings = frappe.get_single('Projects Settings')
 		self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap)
 		self.validate_overlap_for("employee", data, self.employee, settings.ignore_employee_time_overlap)
 
-	def validate_task_project(self):
-		for log in self.time_logs:
-			log.project = log.project or frappe.db.get_value("Task", log.task, "project")
+	def set_project(self, data):
+		data.project = data.project or frappe.db.get_value("Task", data.task, "project")
+
+	def validate_project(self, data):
+		if self.parent_project and self.parent_project != data.project:
+			frappe.throw(_("Row {0}: Project must be same as the one set in the Timesheet: {1}.").format(data.idx, self.parent_project))
 
 	def validate_overlap_for(self, fieldname, args, value, ignore_validation=False):
 		if not value or ignore_validation:
@@ -189,7 +197,7 @@
 
 	def update_cost(self):
 		for data in self.time_logs:
-			if data.activity_type or data.billable:
+			if data.activity_type or data.is_billable:
 				rate = get_activity_cost(self.employee, data.activity_type)
 				hours = data.billing_hours or 0
 				costing_hours = data.billing_hours or data.hours or 0
@@ -200,20 +208,29 @@
 					data.costing_amount = data.costing_rate * costing_hours
 
 	def update_time_rates(self, ts_detail):
-		if not ts_detail.billable:
+		if not ts_detail.is_billable:
 			ts_detail.billing_rate = 0.0
 
 @frappe.whitelist()
-def get_projectwise_timesheet_data(project, parent=None, from_time=None, to_time=None):
+def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to_time=None):
 	condition = ''
+	if project:
+		condition += "and tsd.project = %(project)s"
 	if parent:
-		condition = "AND parent = %(parent)s"
+		condition += "AND tsd.parent = %(parent)s"
 	if from_time and to_time:
-		condition += "AND from_time BETWEEN %(from_time)s AND %(to_time)s"
+		condition += "AND CAST(tsd.from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s"
 
-	return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt
-			from `tabTimesheet Detail` where parenttype = 'Timesheet' and docstatus=1 and project = %(project)s {0} and billable = 1
-			and sales_invoice is null""".format(condition), {'project': project, 'parent': parent, 'from_time': from_time, 'to_time': to_time}, as_dict=1)
+	return frappe.db.sql("""SELECT tsd.name as name,
+				tsd.parent as parent, tsd.billing_hours as billing_hours,
+				tsd.billing_amount as billing_amount, tsd.activity_type as activity_type,
+				tsd.description as description, ts.currency as currency
+			FROM `tabTimesheet Detail` tsd
+			INNER JOIN `tabTimesheet` ts ON ts.name = tsd.parent
+			WHERE tsd.parenttype = 'Timesheet'
+				and tsd.docstatus=1 {0}
+				and tsd.is_billable = 1
+				and tsd.sales_invoice is null""".format(condition), {'project': project, 'parent': parent, 'from_time': from_time, 'to_time': to_time}, as_dict=1)
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
@@ -250,7 +267,7 @@
 	}
 
 @frappe.whitelist()
-def make_sales_invoice(source_name, item_code=None, customer=None):
+def make_sales_invoice(source_name, item_code=None, customer=None, currency=None):
 	target = frappe.new_doc("Sales Invoice")
 	timesheet = frappe.get_doc('Timesheet', source_name)
 
@@ -268,6 +285,9 @@
 	if customer:
 		target.customer = customer
 
+	if currency:
+		target.currency = currency
+
 	if item_code:
 		target.append('items', {
 			'item_code': item_code,
@@ -275,11 +295,16 @@
 			'rate': billing_rate
 		})
 
-	target.append('timesheets', {
-		'time_sheet': timesheet.name,
-		'billing_hours': hours,
-		'billing_amount': billing_amount
-	})
+	for time_log in timesheet.time_logs:
+		if time_log.is_billable:
+			target.append('timesheets', {
+				'time_sheet': timesheet.name,
+				'billing_hours': time_log.billing_hours,
+				'billing_amount': time_log.billing_amount,
+				'timesheet_detail': time_log.name,
+				'activity_type': time_log.activity_type,
+				'description': time_log.description
+			})
 
 	target.run_method("calculate_billing_amount_for_timesheet")
 	target.run_method("set_missing_values")
@@ -309,12 +334,17 @@
 	})
 
 @frappe.whitelist()
-def get_activity_cost(employee=None, activity_type=None):
+def get_activity_cost(employee=None, activity_type=None, currency=None):
+	base_currency = frappe.defaults.get_global_default('currency')
 	rate = frappe.db.get_values("Activity Cost", {"employee": employee,
 		"activity_type": activity_type}, ["costing_rate", "billing_rate"], as_dict=True)
 	if not rate:
 		rate = frappe.db.get_values("Activity Type", {"activity_type": activity_type},
 			["costing_rate", "billing_rate"], as_dict=True)
+		if rate and currency and currency!=base_currency:
+			exchange_rate = get_exchange_rate(base_currency, currency)
+			rate[0]["costing_rate"] = rate[0]["costing_rate"] * exchange_rate
+			rate[0]["billing_rate"] = rate[0]["billing_rate"] * exchange_rate
 
 	return rate[0] if rate else {}
 
diff --git a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
index a9b3bfb..ee04c61 100644
--- a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
+++ b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
@@ -1,979 +1,279 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2013-03-05 09:11:06", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Document", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2013-03-05 09:11:06",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "activity_type",
+  "from_time",
+  "description",
+  "section_break_3",
+  "expected_hours",
+  "to_time",
+  "hours",
+  "completed",
+  "section_break_7",
+  "completed_qty",
+  "workstation",
+  "column_break_12",
+  "operation",
+  "operation_id",
+  "project_details",
+  "project",
+  "project_name",
+  "column_break_2",
+  "task",
+  "section_break_6",
+  "is_billable",
+  "sales_invoice",
+  "column_break_8",
+  "billing_hours",
+  "section_break_11",
+  "base_billing_rate",
+  "base_billing_amount",
+  "base_costing_rate",
+  "base_costing_amount",
+  "column_break_14",
+  "billing_rate",
+  "billing_amount",
+  "costing_rate",
+  "costing_amount"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "activity_type", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Activity Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Activity Type", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "activity_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Activity Type",
+   "options": "Activity Type"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "from_time", 
-   "fieldtype": "Datetime", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "From Time", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "columns": 2,
+   "fieldname": "from_time",
+   "fieldtype": "Datetime",
+   "in_list_view": 1,
+   "label": "From Time"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_3",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "expected_hours", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Expected Hrs", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "expected_hours",
+   "fieldtype": "Float",
+   "label": "Expected Hrs"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 1, 
-   "fieldname": "hours", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Hrs", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "columns": 1,
+   "fieldname": "hours",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Hrs"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "to_time", 
-   "fieldtype": "Datetime", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "To Time", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "to_time",
+   "fieldtype": "Datetime",
+   "label": "To Time"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "fieldname": "completed", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Completed", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "fieldname": "completed",
+   "fieldtype": "Check",
+   "label": "Completed"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_7", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_7",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:parent.work_order", 
-   "fieldname": "completed_qty", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Completed Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:parent.work_order",
+   "fieldname": "completed_qty",
+   "fieldtype": "Float",
+   "label": "Completed Qty"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:parent.work_order", 
-   "fieldname": "workstation", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Workstation", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Workstation", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:parent.work_order",
+   "fieldname": "workstation",
+   "fieldtype": "Link",
+   "label": "Workstation",
+   "options": "Workstation",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_12", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:parent.work_order", 
-   "fieldname": "operation", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Operation", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Operation", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:parent.work_order",
+   "fieldname": "operation",
+   "fieldtype": "Link",
+   "label": "Operation",
+   "options": "Operation",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:parent.work_order", 
-   "fieldname": "operation_id", 
-   "fieldtype": "Data", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Operation Id", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:parent.work_order",
+   "fieldname": "operation_id",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Operation Id"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "project_details", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "project_details",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 3, 
-   "fieldname": "project", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Project", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Project", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "columns": 3,
+   "fieldname": "project",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Project",
+   "options": "Project",
+   "read_only_depends_on": "eval: parent.parent_project"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "task", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Task", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Task", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "task",
+   "fieldtype": "Link",
+   "label": "Task",
+   "options": "Task",
+   "remember_last_selected_value": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_6", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 1, 
-   "depends_on": "", 
-   "fieldname": "billable", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Bill", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_8",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_8", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "depends_on": "is_billable",
+   "fieldname": "billing_hours",
+   "fieldtype": "Float",
+   "label": "Billing Hours",
+   "permlevel": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "billable", 
-   "fieldname": "billing_hours", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing Hours", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 1, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "is_billable",
+   "fieldname": "section_break_11",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "billable", 
-   "fieldname": "section_break_11", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_rate",
+   "fieldtype": "Currency",
+   "label": "Billing Rate",
+   "options": "currency",
+   "permlevel": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "billing_rate", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing Rate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 1, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "default": "0",
+   "fieldname": "billing_amount",
+   "fieldtype": "Currency",
+   "label": "Billing Amount",
+   "options": "currency",
+   "permlevel": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "depends_on": "", 
-   "description": "", 
-   "fieldname": "billing_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 1, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_14",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_14", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "costing_rate",
+   "fieldtype": "Currency",
+   "label": "Costing Rate",
+   "options": "currency",
+   "permlevel": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "costing_rate", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Costing Rate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 1, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "default": "0",
+   "fieldname": "costing_amount",
+   "fieldtype": "Currency",
+   "label": "Costing Amount",
+   "options": "currency",
+   "permlevel": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "description": "", 
-   "fieldname": "costing_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Costing Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 1, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "fieldname": "sales_invoice",
+   "fieldtype": "Link",
+   "label": "Sales Invoice",
+   "no_copy": 1,
+   "options": "Sales Invoice",
+   "print_hide": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reference", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Reference", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "columns": 1,
+   "default": "0",
+   "fieldname": "is_billable",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Is Billable",
+   "print_hide": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "sales_invoice", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Sales Invoice", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "Sales Invoice", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fetch_from": "project.project_name",
+   "fieldname": "project_name",
+   "fieldtype": "Data",
+   "label": "Project Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "label": "Description"
+  },
+  {
+   "fieldname": "base_billing_rate",
+   "fieldtype": "Currency",
+   "label": "Billing Rate",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_billing_amount",
+   "fieldtype": "Currency",
+   "label": "Billing Amount",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_costing_rate",
+   "fieldtype": "Currency",
+   "label": "Costing Rate",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_costing_amount",
+   "fieldtype": "Currency",
+   "label": "Costing Amount",
+   "print_hide": 1,
+   "read_only": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2019-02-18 18:55:53.190526", 
- "modified_by": "Administrator", 
- "module": "Projects", 
- "name": "Timesheet Detail", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_order": "ASC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "idx": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-18 12:19:33.205940",
+ "modified_by": "Administrator",
+ "module": "Projects",
+ "name": "Timesheet Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "ASC"
 }
\ No newline at end of file
diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py
index 6c3c05f..5efde41 100644
--- a/erpnext/projects/report/billing_summary.py
+++ b/erpnext/projects/report/billing_summary.py
@@ -126,7 +126,7 @@
 	timesheet_details = frappe.get_all(
 		"Timesheet Detail",
 		filters = timesheet_details_filter,
-		fields=["from_time", "to_time", "hours", "billable", "billing_hours", "billing_rate", "parent"]
+		fields=["from_time", "to_time", "hours", "is_billable", "billing_hours", "billing_rate", "parent"]
 	)
 
 	timesheet_details_map = frappe._dict()
@@ -139,7 +139,7 @@
 	precision = frappe.get_precision("Timesheet Detail", "hours")
 	activity_duration = time_diff_in_hours(end_time, start_time)
 	billing_duration = 0.0
-	if activity.billable:
+	if activity.is_billable:
 		billing_duration = activity.billing_hours
 		if activity_duration != activity.billing_hours:
 			billing_duration = activity_duration * activity.billing_hours / activity.hours
diff --git a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py
index 842fd4d..4d22f46 100644
--- a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py
+++ b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py
@@ -140,7 +140,7 @@
 					additional_filters += f"AND tt.{field} = '{self.filters.get(field)}'"
 
 		self.filtered_time_logs = frappe.db.sql('''
-			SELECT tt.employee AS employee, ttd.hours AS hours, ttd.billable AS billable, ttd.project AS project
+			SELECT tt.employee AS employee, ttd.hours AS hours, ttd.is_billable AS is_billable, ttd.project AS project
 			FROM `tabTimesheet Detail` AS ttd
 			JOIN `tabTimesheet` AS tt
 				ON ttd.parent = tt.name
@@ -153,14 +153,14 @@
 	def generate_stats_by_employee(self):
 		self.stats_by_employee = frappe._dict()
 
-		for emp, hours, billable, project in self.filtered_time_logs:
+		for emp, hours, is_billable, project in self.filtered_time_logs:
 			self.stats_by_employee.setdefault(
 				emp, frappe._dict()
 			).setdefault('billed_hours', 0.0)
 
 			self.stats_by_employee[emp].setdefault('non_billed_hours', 0.0)
 
-			if billable:
+			if is_billable:
 				self.stats_by_employee[emp]['billed_hours'] += flt(hours, 2)
 			else:
 				self.stats_by_employee[emp]['non_billed_hours'] += flt(hours, 2)
diff --git a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py
index fa87827..0e5a597 100644
--- a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py
+++ b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py
@@ -31,7 +31,7 @@
         timesheet1.append("time_logs", {
             "activity_type": get_random("Activity Type"),
             "hours": 5,
-            "billable": 1,
+            "is_billable": 1,
             "from_time": '2021-04-01 13:30:00.000000',
             "to_time": '2021-04-01 18:30:00.000000'
         })
@@ -46,7 +46,7 @@
         timesheet2.append("time_logs", {
             "activity_type": get_random("Activity Type"),
             "hours": 10,
-            "billable": 0,
+            "is_billable": 0,
             "from_time": '2021-04-01 13:30:00.000000',
             "to_time": '2021-04-01 23:30:00.000000',
             "project": cls.test_project.name
diff --git a/erpnext/projects/report/project_profitability/project_profitability.py b/erpnext/projects/report/project_profitability/project_profitability.py
index 5ad2d85..9139d84 100644
--- a/erpnext/projects/report/project_profitability/project_profitability.py
+++ b/erpnext/projects/report/project_profitability/project_profitability.py
@@ -4,6 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
+from frappe.utils import flt
 
 def execute(filters=None):
 	columns, data = [], []
@@ -52,8 +53,8 @@
 
 def calculate_cost_and_profit(data):
 	for row in data:
-		row.fractional_cost = row.base_gross_pay * row.utilization
-		row.profit = row.base_grand_total - row.base_gross_pay * row.utilization
+		row.fractional_cost = flt(row.base_gross_pay) * flt(row.utilization)
+		row.profit = flt(row.base_grand_total) - flt(row.base_gross_pay) * flt(row.utilization)
 	return data
 
 def get_conditions(filters):
diff --git a/erpnext/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py
index 7fe28b1..f2b9c25 100644
--- a/erpnext/projects/report/project_profitability/test_project_profitability.py
+++ b/erpnext/projects/report/project_profitability/test_project_profitability.py
@@ -14,7 +14,7 @@
 		if not frappe.db.exists('Salary Component', 'Timesheet Component'):
 			frappe.get_doc({'doctype': 'Salary Component', 'salary_component': 'Timesheet Component'}).insert()
 		make_salary_structure_for_timesheet(emp, company='_Test Company')
-		self.timesheet = make_timesheet(emp, simulate = True, billable=1)
+		self.timesheet = make_timesheet(emp, simulate = True, is_billable=1)
 		self.salary_slip = make_salary_slip(self.timesheet.name)
 		self.salary_slip.submit()
 		self.sales_invoice = make_sales_invoice(self.timesheet.name, '_Test Item', '_Test Customer')
diff --git a/erpnext/public/css/email.css b/erpnext/public/css/email.css
deleted file mode 100644
index 8cf1a31..0000000
--- a/erpnext/public/css/email.css
+++ /dev/null
@@ -1,29 +0,0 @@
-.panel-header {
-  background-color: #fafbfc;
-  border: 1px solid #d1d8dd;
-  border-radius: 3px 3px 0 0;
-}
-.panel-body {
-  background-color: #fff;
-  border: 1px solid #d1d8dd;
-  border-top: none;
-  border-radius: 0 0 3px 3px;
-  overflow-wrap: break-word;
-}
-.sender-avatar {
-  width: 24px;
-  height: 24px;
-  border-radius: 3px;
-  vertical-align: middle;
-}
-.sender-avatar-placeholder {
-  width: 24px;
-  height: 24px;
-  border-radius: 3px;
-  vertical-align: middle;
-  line-height: 24px;
-  text-align: center;
-  color: #d1d8dd;
-  border: 1px solid #d1d8dd;
-  background-color: #fff;
-}
diff --git a/erpnext/public/css/erpnext.css b/erpnext/public/css/erpnext.css
deleted file mode 100644
index 6e4efcb..0000000
--- a/erpnext/public/css/erpnext.css
+++ /dev/null
@@ -1,408 +0,0 @@
-.erpnext-footer {
-  margin: 11px auto;
-  text-align: center;
-}
-.show-all-reports {
-  margin-top: 5px;
-  font-size: 11px;
-}
-/* toolbar */
-.toolbar-splash {
-  width: 32px;
-  height: 32px;
-  margin: -10px auto;
-}
-.erpnext-icon {
-  width: 24px;
-  margin-right: 0px;
-  margin-top: -3px;
-}
-.dashboard-list-item {
-  background-color: inherit;
-  padding: 5px 0px;
-  border-bottom: 1px solid #d1d8dd;
-}
-#page-stock-balance .dashboard-list-item {
-  padding: 5px 15px;
-}
-.dashboard-list-item:last-child {
-  border-bottom: none;
-}
-.frappe-control[data-fieldname='result_html'] {
-  overflow: scroll;
-}
-.assessment-result-tool {
-  table-layout: fixed;
-}
-.assessment-result-tool input {
-  width: 100%;
-  border: 0;
-  outline: none;
-  text-align: right;
-}
-.assessment-result-tool th {
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-.assessment-result-tool .total-score,
-.assessment-result-tool .grade,
-.assessment-result-tool .score {
-  text-align: right;
-}
-/* pos */
-body[data-route="pos"] .pos-bill-toolbar {
-  padding: 10px 0px;
-  height: 51px;
-}
-body[data-route="pos"] .pos-bill-item:hover,
-body[data-route="pos"] .list-customers-table > .pos-list-row:hover {
-  background-color: #f5f7fa;
-  cursor: pointer;
-}
-body[data-route="pos"] .pos-item-qty {
-  display: inline-block;
-}
-body[data-route="pos"] .pos-qty-row > div {
-  padding: 5px 0px;
-}
-body[data-route="pos"] .pos-qty-btn {
-  margin-top: 3px;
-  cursor: pointer;
-  font-size: 120%;
-}
-body[data-route="pos"] .search-area .form-group {
-  max-width: 100% !important;
-}
-body[data-route="pos"] .tax-table {
-  margin-bottom: 10px;
-}
-body[data-route="pos"] .discount-field-col {
-  padding-left: 24px;
-}
-body[data-route="pos"] .discount-amount-area .input-group:first-child {
-  margin-bottom: 2px;
-}
-body[data-route="pos"] .payment-toolbar .row {
-  width: 323px;
-  margin: 0 auto;
-}
-body[data-route="pos"] .payment-mode {
-  cursor: pointer;
-  font-family: sans-serif;
-  font-size: 15px;
-}
-body[data-route="pos"] .pos-payment-row .col-xs-6 {
-  padding: 15px;
-}
-body[data-route="pos"] .pos-payment-row {
-  border-bottom: 1px solid #d1d8dd;
-  margin: 2px 0px 5px 0px;
-  height: 60px;
-  margin-top: 0px;
-  margin-bottom: 0px;
-}
-body[data-route="pos"] .pos-payment-row:hover,
-body[data-route="pos"] .pos-keyboard-key:hover {
-  background-color: #fafbfc;
-  cursor: pointer;
-}
-body[data-route="pos"] .pos-keyboard-key,
-body[data-route="pos"] .delete-btn {
-  border: 1px solid #d1d8dd;
-  height: 85px;
-  width: 85px;
-  margin: 10px 10px;
-  font-size: 24px;
-  font-weight: 200;
-  background-color: #FDFDFD;
-  border-color: #e8e8e8;
-}
-body[data-route="pos"] .numeric-keypad {
-  border: 1px solid #d1d8dd;
-  height: 69px;
-  width: 69px;
-  font-size: 20px;
-  font-weight: 200;
-  background-color: #FDFDFD;
-  border-color: #e8e8e8;
-  margin-left: -4px;
-}
-body[data-route="pos"] .pos-pay {
-  height: 69px;
-  width: 69px;
-  font-size: 17px;
-  font-weight: 200;
-  margin-left: -4px;
-}
-body[data-route="pos"] .numeric-keypad {
-  height: 60px;
-  width: 60px;
-  font-size: 20px;
-  font-weight: 200;
-  border-radius: 0;
-  background-color: #fff;
-  margin-left: -4px;
-}
-@media (max-width: 1199px) {
-  body[data-route="pos"] .numeric-keypad {
-    height: 45px;
-    width: 45px;
-    font-size: 14px;
-  }
-}
-@media (max-width: 991px) {
-  body[data-route="pos"] .numeric-keypad {
-    height: 40px;
-    width: 40px;
-  }
-}
-body[data-route="pos"] .numeric_keypad {
-  margin-left: -15px;
-}
-body[data-route="pos"] .numeric_keypad > .row > button {
-  border: none;
-  border-right: 1px solid #d1d8dd;
-  border-bottom: 1px solid #d1d8dd;
-}
-body[data-route="pos"] .numeric_keypad > .row > button:first-child {
-  border-left: 1px solid #d1d8dd;
-}
-body[data-route="pos"] .numeric_keypad > .row:first-child > button {
-  border-top: 1px solid #d1d8dd;
-}
-body[data-route="pos"] .pos-pay {
-  background-color: #5E64FF;
-  border: none;
-}
-body[data-route="pos"] .multimode-payments {
-  padding-left: 30px;
-}
-body[data-route="pos"] .payment-toolbar {
-  padding-right: 30px;
-}
-body[data-route="pos"] .list-row-head.pos-invoice-list {
-  border-top: 1px solid #d1d8dd;
-}
-body[data-route="pos"] .modal-dialog {
-  width: 750px;
-}
-@media (max-width: 767px) {
-  body[data-route="pos"] .modal-dialog {
-    width: auto;
-  }
-  body[data-route="pos"] .modal-dialog .modal-content {
-    height: auto;
-  }
-}
-@media (max-width: 767px) {
-  body[data-route="pos"] .amount-row h3 {
-    font-size: 15px;
-  }
-  body[data-route="pos"] .pos-keyboard-key,
-  body[data-route="pos"] .delete-btn {
-    height: 50px;
-  }
-  body[data-route="pos"] .multimode-payments {
-    padding-left: 15px;
-  }
-  body[data-route="pos"] .payment-toolbar {
-    padding-right: 15px;
-  }
-}
-body[data-route="pos"] .amount-label {
-  font-size: 16px;
-}
-body[data-route="pos"] .selected-payment-mode {
-  background-color: #fafbfc;
-  cursor: pointer;
-}
-body[data-route="pos"] .pos-invoice-list {
-  padding: 15px 10px;
-}
-body[data-route="pos"] .write_off_amount,
-body[data-route="pos"] .change_amount {
-  margin: 15px;
-  width: 130px;
-}
-body[data-route="pos"] .pos-list-row {
-  display: table;
-  table-layout: fixed;
-  width: 100%;
-  padding: 9px 15px;
-  font-size: 12px;
-  margin: 0px;
-  border-bottom: 1px solid #d1d8dd;
-}
-body[data-route="pos"] .pos-list-row .cell {
-  display: table-cell;
-  vertical-align: middle;
-}
-body[data-route="pos"] .pos-list-row .cell.price-cell {
-  width: 50%;
-}
-body[data-route="pos"] .pos-list-row .subject {
-  width: 40%;
-}
-body[data-route="pos"] .pos-list-row .list-row-checkbox,
-body[data-route="pos"] .pos-list-row .list-select-all {
-  margin-right: 7px;
-}
-body[data-route="pos"] .pos-bill-header {
-  background-color: #f5f7fa;
-  border: 1px solid #d1d8dd;
-  padding: 13px 15px;
-}
-body[data-route="pos"] .pos-list-row.active {
-  background-color: #fffce7;
-}
-body[data-route="pos"] .totals-area {
-  border-right: 1px solid #d1d8dd;
-  border-left: 1px solid #d1d8dd;
-  margin-bottom: 15px;
-}
-body[data-route="pos"] .tax-area .pos-list-row {
-  border: none;
-}
-body[data-route="pos"] .item-cart-items {
-  height: calc(100vh - 526px);
-  overflow: auto;
-  border: 1px solid #d1d8dd;
-  border-top: none;
-}
-@media (max-width: 767px) {
-  body[data-route="pos"] .item-cart-items {
-    height: 30vh;
-  }
-}
-body[data-route="pos"] .no-items-message {
-  min-height: 200px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  height: 100%;
-}
-body[data-route="pos"] .pos-list-row:last-child {
-  border-bottom: none;
-}
-body[data-route="pos"] .form-section-heading {
-  padding: 0;
-}
-body[data-route="pos"] .item-list {
-  border: 1px solid #d1d8dd;
-  border-top: none;
-  max-height: calc(100vh - 190px);
-  overflow: auto;
-}
-@media (max-width: 767px) {
-  body[data-route="pos"] .item-list {
-    max-height: initial;
-  }
-}
-body[data-route="pos"] .item-list .image-field {
-  height: 140px;
-}
-body[data-route="pos"] .item-list .image-field .placeholder-text {
-  font-size: 50px;
-}
-body[data-route="pos"] .item-list .pos-item-wrapper {
-  position: relative;
-}
-body[data-route="pos"] .pos-bill-toolbar {
-  margin-top: 10px;
-}
-body[data-route="pos"] .search-item .form-group {
-  margin: 0;
-}
-body[data-route="pos"] .item-list-area .pos-bill-header {
-  padding: 5px;
-  padding-left: 15px;
-}
-body[data-route="pos"] .pos-selected-item-action .pos-list-row:first-child {
-  padding-top: 0;
-}
-body[data-route="pos"] .pos-selected-item-action > .pos-list-row {
-  border: none;
-}
-@media (max-width: 1199px) {
-  body[data-route="pos"] .pos-selected-item-action > .pos-list-row {
-    padding: 5px 15px;
-  }
-}
-body[data-route="pos"] .edit-customer-btn {
-  position: absolute;
-  right: 57px;
-  top: 15px;
-  z-index: 100;
-}
-body[data-route="pos"] .btn-more {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  cursor: pointer;
-  background-color: #fafbfc;
-  min-height: 200px;
-}
-body[data-route="pos"] .collapse-btn {
-  cursor: pointer;
-}
-@media (max-width: 767px) {
-  body[data-route="pos"] .page-actions {
-    max-width: 110px;
-  }
-}
-.price-info {
-  position: absolute;
-  left: 0;
-  bottom: 0;
-  margin: 0 0 15px 15px;
-  background-color: rgba(141, 153, 166, 0.6);
-  padding: 5px 9px;
-  border-radius: 3px;
-  color: #fff;
-}
-.leaderboard .result {
-  border-top: 1px solid #d1d8dd;
-}
-.leaderboard .list-item {
-  padding-left: 45px;
-}
-.leaderboard .list-item_content {
-  padding-right: 45px;
-}
-.exercise-card {
-	box-shadow: 0 1px 3px rgba(0,0,0,0.30);
-	border-radius: 2px;
-	padding: 6px 6px 6px 8px;
-	margin-top: 10px;
-	height: 100% !important;
-}
-.exercise-card .card-img-top {
-	width: 100%;
-	height: 15vw;
-	object-fit: cover;
-}
-.exercise-card .btn-edit {
-	position: absolute;
-	bottom: 10px;
-	left: 20px;
-}
-.exercise-card .btn-del {
-	position: absolute;
-	bottom: 10px;
-	left: 50px;
-}
-.exercise-card .card-body {
-		margin-bottom: 10px;
-}
-.exercise-card .card-footer {
-		padding: 10px;
-}
-.exercise-row {
-	height: 100% !important;
-	display: flex;
-	flex-wrap: wrap;
-}
-.exercise-col {
-	padding: 10px;
-}
diff --git a/erpnext/public/css/leaflet/leaflet.css b/erpnext/public/css/leaflet/leaflet.css
deleted file mode 100755
index 979a8bd..0000000
--- a/erpnext/public/css/leaflet/leaflet.css
+++ /dev/null
@@ -1,611 +0,0 @@
-/* required styles */

-

-.leaflet-pane,

-.leaflet-tile,

-.leaflet-marker-icon,

-.leaflet-marker-shadow,

-.leaflet-tile-container,

-.leaflet-map-pane svg,

-.leaflet-map-pane canvas,

-.leaflet-zoom-box,

-.leaflet-image-layer,

-.leaflet-layer {

-    position: absolute;

-    left: 0;

-    top: 0;

-}

-

-.leaflet-container {

-    overflow: hidden;

-    -ms-touch-action: none;

-    touch-action: none;

-}

-

-.leaflet-tile,

-.leaflet-marker-icon,

-.leaflet-marker-shadow {

-    -webkit-user-select: none;

-    -moz-user-select: none;

-    user-select: none;

-    -webkit-user-drag: none;

-}

-

-

-/* Safari renders non-retina tile on retina better with this, but Chrome is worse */

-

-.leaflet-safari .leaflet-tile {

-    image-rendering: -webkit-optimize-contrast;

-}

-

-

-/* hack that prevents hw layers "stretching" when loading new tiles */

-

-.leaflet-safari .leaflet-tile-container {

-    width: 1600px;

-    height: 1600px;

-    -webkit-transform-origin: 0 0;

-}

-

-.leaflet-marker-icon,

-.leaflet-marker-shadow {

-    display: block;

-}

-

-

-/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */

-

-

-/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */

-

-.leaflet-container .leaflet-overlay-pane svg,

-.leaflet-container .leaflet-marker-pane img,

-.leaflet-container .leaflet-tile-pane img,

-.leaflet-container img.leaflet-image-layer {

-    max-width: none !important;

-}

-

-.leaflet-tile {

-    filter: inherit;

-    visibility: hidden;

-}

-

-.leaflet-tile-loaded {

-    visibility: inherit;

-}

-

-.leaflet-zoom-box {

-    width: 0;

-    height: 0;

-    -moz-box-sizing: border-box;

-    box-sizing: border-box;

-    z-index: 800;

-}

-

-

-/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */

-

-.leaflet-overlay-pane svg {

-    -moz-user-select: none;

-}

-

-.leaflet-pane {

-    z-index: 400;

-}

-

-.leaflet-tile-pane {

-    z-index: 200;

-}

-

-.leaflet-overlay-pane {

-    z-index: 400;

-}

-

-.leaflet-shadow-pane {

-    z-index: 500;

-}

-

-.leaflet-marker-pane {

-    z-index: 600;

-}

-

-.leaflet-popup-pane {

-    z-index: 700;

-}

-

-.leaflet-map-pane canvas {

-    z-index: 100;

-}

-

-.leaflet-map-pane svg {

-    z-index: 200;

-}

-

-.leaflet-vml-shape {

-    width: 1px;

-    height: 1px;

-}

-

-.lvml {

-    behavior: url(#default#VML);

-    display: inline-block;

-    position: absolute;

-}

-

-

-/* control positioning */

-

-.leaflet-control {

-    position: relative;

-    z-index: 800;

-    pointer-events: auto;

-}

-

-.leaflet-top,

-.leaflet-bottom {

-    position: absolute;

-    z-index: 1000;

-    pointer-events: none;

-}

-

-.leaflet-top {

-    top: 0;

-}

-

-.leaflet-right {

-    right: 0;

-}

-

-.leaflet-bottom {

-    bottom: 0;

-}

-

-.leaflet-left {

-    left: 0;

-}

-

-.leaflet-control {

-    float: left;

-    clear: both;

-}

-

-.leaflet-right .leaflet-control {

-    float: right;

-}

-

-.leaflet-top .leaflet-control {

-    margin-top: 10px;

-}

-

-.leaflet-bottom .leaflet-control {

-    margin-bottom: 10px;

-}

-

-.leaflet-left .leaflet-control {

-    margin-left: 10px;

-}

-

-.leaflet-right .leaflet-control {

-    margin-right: 10px;

-}

-

-

-/* zoom and fade animations */

-

-.leaflet-fade-anim .leaflet-tile {

-    will-change: opacity;

-}

-

-.leaflet-fade-anim .leaflet-popup {

-    opacity: 0;

-    -webkit-transition: opacity 0.2s linear;

-    -moz-transition: opacity 0.2s linear;

-    -o-transition: opacity 0.2s linear;

-    transition: opacity 0.2s linear;

-}

-

-.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {

-    opacity: 1;

-}

-

-.leaflet-zoom-animated {

-    -webkit-transform-origin: 0 0;

-    -ms-transform-origin: 0 0;

-    transform-origin: 0 0;

-}

-

-.leaflet-zoom-anim .leaflet-zoom-animated {

-    will-change: transform;

-}

-

-.leaflet-zoom-anim .leaflet-zoom-animated {

-    -webkit-transition: -webkit-transform 0.25s cubic-bezier(0, 0, 0.25, 1);

-    -moz-transition: -moz-transform 0.25s cubic-bezier(0, 0, 0.25, 1);

-    -o-transition: -o-transform 0.25s cubic-bezier(0, 0, 0.25, 1);

-    transition: transform 0.25s cubic-bezier(0, 0, 0.25, 1);

-}

-

-.leaflet-zoom-anim .leaflet-tile,

-.leaflet-pan-anim .leaflet-tile {

-    -webkit-transition: none;

-    -moz-transition: none;

-    -o-transition: none;

-    transition: none;

-}

-

-.leaflet-zoom-anim .leaflet-zoom-hide {

-    visibility: hidden;

-}

-

-

-/* cursors */

-

-.leaflet-interactive {

-    cursor: pointer;

-}

-

-.leaflet-grab {

-    cursor: -webkit-grab;

-    cursor: -moz-grab;

-}

-

-.leaflet-crosshair,

-.leaflet-crosshair .leaflet-interactive {

-    cursor: crosshair;

-}

-

-.leaflet-popup-pane,

-.leaflet-control {

-    cursor: auto;

-}

-

-.leaflet-dragging .leaflet-grab,

-.leaflet-dragging .leaflet-grab .leaflet-interactive,

-.leaflet-dragging .leaflet-marker-draggable {

-    cursor: move;

-    cursor: -webkit-grabbing;

-    cursor: -moz-grabbing;

-}

-

-

-/* visual tweaks */

-

-.leaflet-container {

-    background: #ddd;

-    outline: 0;

-}

-

-.leaflet-container a {

-    color: #0078A8;

-}

-

-.leaflet-container a.leaflet-active {

-    outline: 2px solid orange;

-}

-

-.leaflet-zoom-box {

-    border: 2px dotted #38f;

-    background: rgba(255, 255, 255, 0.5);

-}

-

-

-/* general typography */

-

-.leaflet-container {

-    font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;

-}

-

-

-/* general toolbar styles */

-

-.leaflet-bar {

-    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);

-    border-radius: 4px;

-}

-

-.leaflet-bar a,

-.leaflet-bar a:hover {

-    background-color: #fff;

-    border-bottom: 1px solid #ccc;

-    width: 26px;

-    height: 26px;

-    line-height: 26px;

-    display: block;

-    text-align: center;

-    text-decoration: none;

-    color: black;

-}

-

-.leaflet-bar a,

-.leaflet-control-layers-toggle {

-    background-position: 50% 50%;

-    background-repeat: no-repeat;

-    display: block;

-}

-

-.leaflet-bar a:hover {

-    background-color: #f4f4f4;

-}

-

-.leaflet-bar a:first-child {

-    border-top-left-radius: 4px;

-    border-top-right-radius: 4px;

-}

-

-.leaflet-bar a:last-child {

-    border-bottom-left-radius: 4px;

-    border-bottom-right-radius: 4px;

-    border-bottom: none;

-}

-

-.leaflet-bar a.leaflet-disabled {

-    cursor: default;

-    background-color: #f4f4f4;

-    color: #bbb;

-}

-

-.leaflet-touch .leaflet-bar a {

-    width: 30px;

-    height: 30px;

-    line-height: 30px;

-}

-

-

-/* zoom control */

-

-.leaflet-control-zoom-in,

-.leaflet-control-zoom-out {

-    font: bold 18px 'Lucida Console', Monaco, monospace;

-    text-indent: 1px;

-}

-

-.leaflet-control-zoom-out {

-    font-size: 20px;

-}

-

-.leaflet-touch .leaflet-control-zoom-in {

-    font-size: 22px;

-}

-

-.leaflet-touch .leaflet-control-zoom-out {

-    font-size: 24px;

-}

-

-

-/* layers control */

-

-.leaflet-control-layers {

-    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4);

-    background: #fff;

-    border-radius: 5px;

-}

-

-.leaflet-control-layers-toggle {

-    background-image: url('assets/erpnext/images/leaflet/layers.png');

-    width: 36px;

-    height: 36px;

-}

-

-.leaflet-retina .leaflet-control-layers-toggle {

-    background-image: url('assets/erpnext/images/leaflet/layers-2x.png');

-    background-size: 26px 26px;

-}

-

-.leaflet-touch .leaflet-control-layers-toggle {

-    width: 44px;

-    height: 44px;

-}

-

-.leaflet-control-layers .leaflet-control-layers-list,

-.leaflet-control-layers-expanded .leaflet-control-layers-toggle {

-    display: none;

-}

-

-.leaflet-control-layers-expanded .leaflet-control-layers-list {

-    display: block;

-    position: relative;

-}

-

-.leaflet-control-layers-expanded {

-    padding: 6px 10px 6px 6px;

-    color: #333;

-    background: #fff;

-}

-

-.leaflet-control-layers-scrollbar {

-    overflow-y: scroll;

-    padding-right: 5px;

-}

-

-.leaflet-control-layers-selector {

-    margin-top: 2px;

-    position: relative;

-    top: 1px;

-}

-

-.leaflet-control-layers label {

-    display: block;

-}

-

-.leaflet-control-layers-separator {

-    height: 0;

-    border-top: 1px solid #ddd;

-    margin: 5px -10px 5px -6px;

-}

-

-

-/* attribution and scale controls */

-

-.leaflet-container .leaflet-control-attribution {

-    background: #fff;

-    background: rgba(255, 255, 255, 0.7);

-    margin: 0;

-}

-

-.leaflet-control-attribution,

-.leaflet-control-scale-line {

-    padding: 0 5px;

-    color: #333;

-}

-

-.leaflet-control-attribution a {

-    text-decoration: none;

-}

-

-.leaflet-control-attribution a:hover {

-    text-decoration: underline;

-}

-

-.leaflet-container .leaflet-control-attribution,

-.leaflet-container .leaflet-control-scale {

-    font-size: 11px;

-}

-

-.leaflet-left .leaflet-control-scale {

-    margin-left: 5px;

-}

-

-.leaflet-bottom .leaflet-control-scale {

-    margin-bottom: 5px;

-}

-

-.leaflet-control-scale-line {

-    border: 2px solid #777;

-    border-top: none;

-    line-height: 1.1;

-    padding: 2px 5px 1px;

-    font-size: 11px;

-    white-space: nowrap;

-    overflow: hidden;

-    -moz-box-sizing: border-box;

-    box-sizing: border-box;

-    background: #fff;

-    background: rgba(255, 255, 255, 0.5);

-}

-

-.leaflet-control-scale-line:not(:first-child) {

-    border-top: 2px solid #777;

-    border-bottom: none;

-    margin-top: -2px;

-}

-

-.leaflet-control-scale-line:not(:first-child):not(:last-child) {

-    border-bottom: 2px solid #777;

-}

-

-.leaflet-touch .leaflet-control-attribution,

-.leaflet-touch .leaflet-control-layers,

-.leaflet-touch .leaflet-bar {

-    box-shadow: none;

-}

-

-.leaflet-touch .leaflet-control-layers,

-.leaflet-touch .leaflet-bar {

-    border: 2px solid rgba(0, 0, 0, 0.2);

-    background-clip: padding-box;

-}

-

-

-/* popup */

-

-.leaflet-popup {

-    position: absolute;

-    text-align: center;

-}

-

-.leaflet-popup-content-wrapper {

-    padding: 1px;

-    text-align: left;

-    border-radius: 12px;

-}

-

-.leaflet-popup-content {

-    margin: 13px 19px;

-    line-height: 1.4;

-}

-

-.leaflet-popup-content p {

-    margin: 18px 0;

-}

-

-.leaflet-popup-tip-container {

-    margin: 0 auto;

-    width: 40px;

-    height: 20px;

-    position: relative;

-    overflow: hidden;

-}

-

-.leaflet-popup-tip {

-    width: 17px;

-    height: 17px;

-    padding: 1px;

-    margin: -10px auto 0;

-    -webkit-transform: rotate(45deg);

-    -moz-transform: rotate(45deg);

-    -ms-transform: rotate(45deg);

-    -o-transform: rotate(45deg);

-    transform: rotate(45deg);

-}

-

-.leaflet-popup-content-wrapper,

-.leaflet-popup-tip {

-    background: white;

-    color: #333;

-    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.4);

-}

-

-.leaflet-container a.leaflet-popup-close-button {

-    position: absolute;

-    top: 0;

-    right: 0;

-    padding: 4px 4px 0 0;

-    border: none;

-    text-align: center;

-    width: 18px;

-    height: 14px;

-    font: 16px/14px Tahoma, Verdana, sans-serif;

-    color: #c3c3c3;

-    text-decoration: none;

-    font-weight: bold;

-    background: transparent;

-}

-

-.leaflet-container a.leaflet-popup-close-button:hover {

-    color: #999;

-}

-

-.leaflet-popup-scrolled {

-    overflow: auto;

-    border-bottom: 1px solid #ddd;

-    border-top: 1px solid #ddd;

-}

-

-.leaflet-oldie .leaflet-popup-content-wrapper {

-    zoom: 1;

-}

-

-.leaflet-oldie .leaflet-popup-tip {

-    width: 24px;

-    margin: 0 auto;

-    -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";

-    filter: progid: DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);

-}

-

-.leaflet-oldie .leaflet-popup-tip-container {

-    margin-top: -1px;

-}

-

-.leaflet-oldie .leaflet-control-zoom,

-.leaflet-oldie .leaflet-control-layers,

-.leaflet-oldie .leaflet-popup-content-wrapper,

-.leaflet-oldie .leaflet-popup-tip {

-    border: 1px solid #999;

-}

-

-

-/* div icon */

-

-.leaflet-div-icon {

-    background: #fff;

-    border: 1px solid #666;

-}
\ No newline at end of file
diff --git a/erpnext/public/css/leaflet/leaflet.draw.css b/erpnext/public/css/leaflet/leaflet.draw.css
deleted file mode 100755
index 6fb7db0..0000000
--- a/erpnext/public/css/leaflet/leaflet.draw.css
+++ /dev/null
@@ -1,316 +0,0 @@
-/* ================================================================== */
-
-
-/* Toolbars
-/* ================================================================== */
-
-.leaflet-draw-section {
-    position: relative;
-}
-
-.leaflet-draw-toolbar {
-    margin-top: 12px;
-}
-
-.leaflet-draw-toolbar-top {
-    margin-top: 0;
-}
-
-.leaflet-draw-toolbar-notop a:first-child {
-    border-top-right-radius: 0;
-}
-
-.leaflet-draw-toolbar-nobottom a:last-child {
-    border-bottom-right-radius: 0;
-}
-
-.leaflet-draw-toolbar a {
-    background-image: url('assets/erpnext/images/leaflet/spritesheet.png');
-    background-repeat: no-repeat;
-}
-
-.leaflet-retina .leaflet-draw-toolbar a {
-    background-image: url('assets/erpnext/images/leaflet/spritesheet-2x.png');
-    background-size: 270px 30px;
-}
-
-.leaflet-draw a {
-    display: block;
-    text-align: center;
-    text-decoration: none;
-}
-
-
-/* ================================================================== */
-
-
-/* Toolbar actions menu
-/* ================================================================== */
-
-.leaflet-draw-actions {
-    display: none;
-    list-style: none;
-    margin: 0;
-    padding: 0;
-    position: absolute;
-    left: 26px;
-    /* leaflet-draw-toolbar.left + leaflet-draw-toolbar.width */
-    top: 0;
-    white-space: nowrap;
-}
-
-.leaflet-right .leaflet-draw-actions {
-    right: 26px;
-    left: auto;
-}
-
-.leaflet-draw-actions li {
-    display: inline-block;
-}
-
-.leaflet-draw-actions li:first-child a {
-    border-left: none;
-}
-
-.leaflet-draw-actions li:last-child a {
-    -webkit-border-radius: 0 4px 4px 0;
-    border-radius: 0 4px 4px 0;
-}
-
-.leaflet-right .leaflet-draw-actions li:last-child a {
-    -webkit-border-radius: 0;
-    border-radius: 0;
-}
-
-.leaflet-right .leaflet-draw-actions li:first-child a {
-    -webkit-border-radius: 4px 0 0 4px;
-    border-radius: 4px 0 0 4px;
-}
-
-.leaflet-draw-actions a {
-    background-color: #919187;
-    border-left: 1px solid #AAA;
-    color: #FFF;
-    font: 11px/19px "Helvetica Neue", Arial, Helvetica, sans-serif;
-    line-height: 28px;
-    text-decoration: none;
-    padding-left: 10px;
-    padding-right: 10px;
-    height: 28px;
-}
-
-.leaflet-draw-actions-bottom {
-    margin-top: 0;
-}
-
-.leaflet-draw-actions-top {
-    margin-top: 1px;
-}
-
-.leaflet-draw-actions-top a,
-.leaflet-draw-actions-bottom a {
-    height: 27px;
-    line-height: 27px;
-}
-
-.leaflet-draw-actions a:hover {
-    background-color: #A0A098;
-}
-
-.leaflet-draw-actions-top.leaflet-draw-actions-bottom a {
-    height: 26px;
-    line-height: 26px;
-}
-
-
-/* ================================================================== */
-
-
-/* Draw toolbar
-/* ================================================================== */
-
-.leaflet-draw-toolbar .leaflet-draw-draw-polyline {
-    background-position: -2px -2px;
-}
-
-.leaflet-draw-toolbar .leaflet-draw-draw-polygon {
-    background-position: -31px -2px;
-}
-
-.leaflet-draw-toolbar .leaflet-draw-draw-rectangle {
-    background-position: -62px -2px;
-}
-
-.leaflet-draw-toolbar .leaflet-draw-draw-circle {
-    background-position: -92px -2px;
-}
-
-.leaflet-draw-toolbar .leaflet-draw-draw-marker {
-    background-position: -122px -2px;
-}
-
-
-/* ================================================================== */
-
-
-/* Edit toolbar
-/* ================================================================== */
-
-.leaflet-draw-toolbar .leaflet-draw-edit-edit {
-    background-position: -152px -2px;
-}
-
-.leaflet-draw-toolbar .leaflet-draw-edit-remove {
-    background-position: -182px -2px;
-}
-
-.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled {
-    background-position: -212px -2px;
-}
-
-.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled {
-    background-position: -242px -2px;
-}
-
-
-/* ================================================================== */
-
-
-/* Drawing styles
-/* ================================================================== */
-
-.leaflet-mouse-marker {
-    background-color: #fff;
-    cursor: crosshair;
-}
-
-.leaflet-draw-tooltip {
-    background: rgb(54, 54, 54);
-    background: rgba(0, 0, 0, 0.5);
-    border: 1px solid transparent;
-    -webkit-border-radius: 4px;
-    border-radius: 4px;
-    color: #fff;
-    font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif;
-    margin-left: 20px;
-    margin-top: -21px;
-    padding: 4px 8px;
-    position: absolute;
-    visibility: hidden;
-    white-space: nowrap;
-    z-index: 6;
-}
-
-.leaflet-draw-tooltip:before {
-    border-right: 6px solid black;
-    border-right-color: rgba(0, 0, 0, 0.5);
-    border-top: 6px solid transparent;
-    border-bottom: 6px solid transparent;
-    content: "";
-    position: absolute;
-    top: 7px;
-    left: -7px;
-}
-
-.leaflet-error-draw-tooltip {
-    background-color: #F2DEDE;
-    border: 1px solid #E6B6BD;
-    color: #B94A48;
-}
-
-.leaflet-error-draw-tooltip:before {
-    border-right-color: #E6B6BD;
-}
-
-.leaflet-draw-tooltip-single {
-    margin-top: -12px
-}
-
-.leaflet-draw-tooltip-subtext {
-    color: #f8d5e4;
-}
-
-.leaflet-draw-guide-dash {
-    font-size: 1%;
-    opacity: 0.6;
-    position: absolute;
-    width: 5px;
-    height: 5px;
-}
-
-
-/* ================================================================== */
-
-
-/* Edit styles
-/* ================================================================== */
-
-.leaflet-edit-marker-selected {
-    background: rgba(254, 87, 161, 0.1);
-    border: 4px dashed rgba(254, 87, 161, 0.6);
-    -webkit-border-radius: 4px;
-    border-radius: 4px;
-}
-
-.leaflet-edit-move {
-    cursor: move;
-}
-
-.leaflet-edit-resize {
-    cursor: pointer;
-}
-
-
-/* ================================================================== */
-
-
-/* Old IE styles
-/* ================================================================== */
-
-.leaflet-oldie .leaflet-draw-toolbar {
-    border: 3px solid #999;
-}
-
-.leaflet-oldie .leaflet-draw-toolbar a {
-    background-color: #eee;
-}
-
-.leaflet-oldie .leaflet-draw-toolbar a:hover {
-    background-color: #fff;
-}
-
-.leaflet-oldie .leaflet-draw-actions {
-    left: 32px;
-    margin-top: 3px;
-}
-
-.leaflet-oldie .leaflet-draw-actions li {
-    display: inline;
-    zoom: 1;
-}
-
-.leaflet-oldie .leaflet-edit-marker-selected {
-    border: 4px dashed #fe93c2;
-}
-
-.leaflet-oldie .leaflet-draw-actions a {
-    background-color: #999;
-}
-
-.leaflet-oldie .leaflet-draw-actions a:hover {
-    background-color: #a5a5a5;
-}
-
-.leaflet-oldie .leaflet-draw-actions-top a {
-    margin-top: 1px;
-}
-
-.leaflet-oldie .leaflet-draw-actions-bottom a {
-    height: 28px;
-    line-height: 28px;
-}
-
-.leaflet-oldie .leaflet-draw-actions-top.leaflet-draw-actions-bottom a {
-    height: 27px;
-    line-height: 27px;
-}
\ No newline at end of file
diff --git a/erpnext/public/js/account_tree_grid.js b/erpnext/public/js/account_tree_grid.js
index 757f33e..413a5ee 100644
--- a/erpnext/public/js/account_tree_grid.js
+++ b/erpnext/public/js/account_tree_grid.js
@@ -14,9 +14,9 @@
 // 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 = frappe.views.TreeGridReport.extend({
-	init: function(wrapper, title) {
-		this._super({
+erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridReport {
+	constructor(wrapper, title) {
+		super({
 			title: title,
 			parent: $(wrapper).find('.layout-main'),
 			page: wrapper.page,
@@ -33,8 +33,24 @@
 				}
 			},
 		});
-	},
-	setup_columns: function() {
+
+		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,
@@ -50,25 +66,10 @@
 			{id: "closing_cr", name: __("Closing (Cr)"), field: "closing_cr", width: 100,
 				formatter: this.currency_formatter}
 		];
+	}
 
-	},
-	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_filters: function() {
-		this._super();
+	setup_filters() {
+		super.setup_filters();
 		var me = this;
 		// default filters
 		this.filter_inputs.fiscal_year.change(function() {
@@ -83,8 +84,8 @@
 		});
 		me.show_zero_check()
 		if(me.ignore_closing_entry) me.ignore_closing_entry();
-	},
-	prepare_data: function() {
+	}
+	prepare_data() {
 		var me = this;
 		if(!this.primary_data) {
 			// make accounts list
@@ -113,12 +114,12 @@
 		this.set_indent();
 		this.prepare_balances();
 
-	},
-	init_account: function(d) {
+	}
+	init_account(d) {
 		this.reset_item_values(d);
-	},
+	}
 
-	prepare_balances: function() {
+	prepare_balances() {
 		var gl = frappe.report_dump.data['GL Entry'];
 		var me = this;
 
@@ -139,8 +140,8 @@
 		});
 
 		this.update_groups();
-	},
-	update_balances: function(account, posting_date, v) {
+	}
+	update_balances(account, posting_date, v) {
 		// opening
 		if (posting_date < this.opening_date || v.is_opening === "Yes") {
 			if (account.report_type === "Profit and Loss" &&
@@ -161,8 +162,8 @@
 		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: function(account, field, balance) {
+	}
+	set_debit_or_credit(account, field, balance) {
 		if(balance > 0) {
 			account[field+"_dr"] = balance;
 			account[field+"_cr"] = 0;
@@ -170,8 +171,8 @@
 			account[field+"_cr"] = Math.abs(balance);
 			account[field+"_dr"] = 0;
 		}
-	},
-	update_groups: function() {
+	}
+	update_groups() {
 		// update groups
 		var me= this;
 		$.each(this.data, function(i, account) {
@@ -202,9 +203,9 @@
 				}
 			}
 		});
-	},
+	}
 
-	set_fiscal_year: function() {
+	set_fiscal_year() {
 		if (this.opening_date > this.closing_date) {
 			frappe.msgprint(__("Opening Date should be before Closing Date"));
 			return;
@@ -223,9 +224,9 @@
 			frappe.msgprint(__("Opening Date and Closing Date should be within same Fiscal Year"));
 			return;
 		}
-	},
+	}
 
-	show_general_ledger: function(account) {
+	show_general_ledger(account) {
 		frappe.route_options = {
 			account: account,
 			company: this.company,
@@ -234,4 +235,4 @@
 		};
 		frappe.set_route("query-report", "General Ledger");
 	}
-});
+};
diff --git a/erpnext/public/js/bank-reconciliation-tool.bundle.js b/erpnext/public/js/bank-reconciliation-tool.bundle.js
new file mode 100644
index 0000000..636ef18
--- /dev/null
+++ b/erpnext/public/js/bank-reconciliation-tool.bundle.js
@@ -0,0 +1,3 @@
+import "./bank_reconciliation_tool/data_table_manager";
+import "./bank_reconciliation_tool/number_card";
+import "./bank_reconciliation_tool/dialog_manager";
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index cdfd909..8b4a497 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -9,14 +9,14 @@
 
 cur_frm.email_field = "contact_email";
 
-erpnext.buying.BuyingController = erpnext.TransactionController.extend({
-	setup: function() {
-		this._super();
-	},
+erpnext.buying.BuyingController = class BuyingController extends erpnext.TransactionController {
+	setup() {
+		super.setup();
+	}
 
-	onload: function(doc, cdt, cdn) {
+	onload(doc, cdt, cdn) {
 		this.setup_queries(doc, cdt, cdn);
-		this._super();
+		super.onload();
 
 		this.frm.set_query('shipping_rule', function() {
 			return {
@@ -48,9 +48,9 @@
 			});
 		}
 		/* eslint-enable */
-	},
+	}
 
-	setup_queries: function(doc, cdt, cdn) {
+	setup_queries(doc, cdt, cdn) {
 		var me = this;
 
 		if(this.frm.fields_dict.buying_price_list) {
@@ -109,9 +109,9 @@
 				return me.set_query_for_item_tax_template(doc, cdt, cdn)
 			});
 		}
-	},
+	}
 
-	refresh: function(doc) {
+	refresh(doc) {
 		frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier'};
 
 		this.frm.toggle_display("supplier_name",
@@ -122,38 +122,38 @@
 			this.set_from_product_bundle();
 		}
 
-		this._super();
-	},
+		super.refresh();
+	}
 
-	supplier: function() {
+	supplier() {
 		var me = this;
 		erpnext.utils.get_party_details(this.frm, null, null, function(){
 			me.apply_price_list();
 		});
-	},
+	}
 
-	supplier_address: function() {
+	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: function() {
+	buying_price_list() {
 		this.apply_price_list();
-	},
+	}
 
-	discount_percentage: function(doc, cdt, cdn) {
+	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: function(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: function(doc, cdt, cdn) {
+	qty(doc, cdt, cdn) {
 		var item = frappe.get_doc(cdt, cdn);
 		if ((doc.doctype == "Purchase Receipt") || (doc.doctype == "Purchase Invoice" && (doc.update_stock || doc.is_return))) {
 			frappe.model.round_floats_in(item, ["qty", "received_qty"]);
@@ -168,22 +168,22 @@
 			item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
 			item.received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(item.received_qty);
 		}
-		this._super(doc, cdt, cdn);
-	},
+		super.qty(doc, cdt, cdn);
+	}
 
-	batch_no: function(doc, cdt, cdn) {
-		this._super(doc, cdt, cdn);
-	},
+	batch_no(doc, cdt, cdn) {
+		super.batch_no(doc, cdt, cdn);
+	}
 
-	received_qty: function(doc, cdt, cdn) {
+	received_qty(doc, cdt, cdn) {
 		this.calculate_accepted_qty(doc, cdt, cdn)
-	},
+	}
 
-	rejected_qty: function(doc, cdt, cdn) {
+	rejected_qty(doc, cdt, cdn) {
 		this.calculate_accepted_qty(doc, cdt, cdn)
-	},
+	}
 
-	calculate_accepted_qty: function(doc, cdt, cdn){
+	calculate_accepted_qty(doc, cdt, cdn){
 		var item = frappe.get_doc(cdt, cdn);
 		frappe.model.round_floats_in(item, ["received_qty", "rejected_qty"]);
 
@@ -191,9 +191,9 @@
 
 		item.qty = flt(item.received_qty - item.rejected_qty, precision("qty", item));
 		this.qty(doc, cdt, cdn);
-	},
+	}
 
-	validate_negative_quantity: function(cdt, cdn, item, fieldnames){
+	validate_negative_quantity(cdt, cdn, item, fieldnames){
 		if(!item || !fieldnames) { return }
 
 		var is_negative_qty = false;
@@ -206,9 +206,9 @@
 		}
 
 		return is_negative_qty
-	},
+	}
 
-	warehouse: function(doc, cdt, cdn) {
+	warehouse(doc, cdt, cdn) {
 		var item = frappe.get_doc(cdt, cdn);
 		if(item.item_code && item.warehouse) {
 			return this.frm.call({
@@ -221,9 +221,9 @@
 				}
 			});
 		}
-	},
+	}
 
-	project: function(doc, cdt, cdn) {
+	project(doc, cdt, cdn) {
 		var item = frappe.get_doc(cdt, cdn);
 		if(item.project) {
 			$.each(this.frm.doc["items"] || [],
@@ -234,48 +234,48 @@
 					}
 				});
 		}
-	},
+	}
 
-	rejected_warehouse: function(doc, cdt) {
+	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: function(doc, cdt, cdn) {
+	category(doc, cdt, cdn) {
 		// should be the category field of tax table
 		if(cdt != doc.doctype) {
 			this.calculate_taxes_and_totals();
 		}
-	},
-	add_deduct_tax: function(doc, cdt, cdn) {
+	}
+	add_deduct_tax(doc, cdt, cdn) {
 		this.calculate_taxes_and_totals();
-	},
+	}
 
-	set_from_product_bundle: function() {
+	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: function(){
+	shipping_address(){
 		var me = this;
 		erpnext.utils.get_address_display(this.frm, "shipping_address",
 			"shipping_address_display", true);
-	},
+	}
 
-	billing_address: function() {
+	billing_address() {
 		erpnext.utils.get_address_display(this.frm, "billing_address",
 			"billing_address_display", true);
-	},
+	}
 
-	tc_name: function() {
+	tc_name() {
 		this.get_terms();
-	},
+	}
 
-	update_auto_repeat_reference: function(doc) {
+	update_auto_repeat_reference(doc) {
 		if (doc.auto_repeat) {
 			frappe.call({
 				method:"frappe.automation.doctype.auto_repeat.auto_repeat.update_reference",
@@ -292,9 +292,9 @@
 				}
 			})
 		}
-	},
+	}
 
-	manufacturer: function(doc, cdt, cdn) {
+	manufacturer(doc, cdt, cdn) {
 		const row = locals[cdt][cdn];
 
 		if(row.manufacturer) {
@@ -311,9 +311,9 @@
 				}
 			});
 		}
-	},
+	}
 
-	manufacturer_part_no: function(doc, cdt, cdn) {
+	manufacturer_part_no(doc, cdt, cdn) {
 		const row = locals[cdt][cdn];
 
 		if (row.manufacturer_part_no) {
@@ -336,7 +336,7 @@
 
 			}
 		}
-});
+};
 
 cur_frm.add_fetch('project', 'cost_center', 'cost_center');
 
@@ -497,4 +497,4 @@
 	});
 
 	dialog.show();
-}
\ No newline at end of file
+}
diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js
index 87b21b7..d346357 100644
--- a/erpnext/public/js/controllers/stock_controller.js
+++ b/erpnext/public/js/controllers/stock_controller.js
@@ -3,22 +3,22 @@
 
 frappe.provide("erpnext.stock");
 
-erpnext.stock.StockController = frappe.ui.form.Controller.extend({
-	onload: function() {
+erpnext.stock.StockController = class StockController extends frappe.ui.form.Controller {
+	onload() {
 		// warehouse query if company
 		if (this.frm.fields_dict.company) {
 			this.setup_warehouse_query();
 		}
-	},
+	}
 
-	setup_warehouse_query: function() {
+	setup_warehouse_query() {
 		var me = this;
 		erpnext.queries.setup_queries(this.frm, "Warehouse", function() {
 			return erpnext.queries.warehouse(me.frm.doc);
 		});
-	},
+	}
 
-	setup_posting_date_time_check: function() {
+	setup_posting_date_time_check() {
 		// make posting date default and read only unless explictly checked
 		frappe.ui.form.on(this.frm.doctype, 'set_posting_date_and_time_read_only', function(frm) {
 			if(frm.doc.docstatus == 0 && frm.doc.set_posting_time) {
@@ -46,9 +46,9 @@
 				frm.trigger('set_posting_date_and_time_read_only');
 			}
 		});
-	},
+	}
 
-	show_stock_ledger: function() {
+	show_stock_ledger() {
 		var me = this;
 		if(this.frm.doc.docstatus > 0) {
 			cur_frm.add_custom_button(__("Stock Ledger"), function() {
@@ -63,9 +63,9 @@
 			}, __("View"));
 		}
 
-	},
+	}
 
-	show_general_ledger: function() {
+	show_general_ledger() {
 		var me = this;
 		if(this.frm.doc.docstatus > 0) {
 			cur_frm.add_custom_button(__('Accounting Ledger'), function() {
@@ -81,4 +81,4 @@
 			}, __("View"));
 		}
 	}
-});
+};
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 2e133be..32439b6 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -1,12 +1,12 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-erpnext.taxes_and_totals = erpnext.payments.extend({
-	setup: function() {
+erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
+	setup() {
 		this.fetch_round_off_accounts();
-	},
+	}
 
-	apply_pricing_rule_on_item: function(item) {
+	apply_pricing_rule_on_item(item) {
 		let effective_item_rate = item.price_list_rate;
 		let item_rate = item.rate;
 		if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) {
@@ -32,9 +32,9 @@
 		}
 
 		frappe.model.set_value(item.doctype, item.name, "rate", item_rate);
-	},
+	}
 
-	calculate_taxes_and_totals: function(update_paid_amount) {
+	calculate_taxes_and_totals(update_paid_amount) {
 		this.discount_amount_applied = false;
 		this._calculate_taxes_and_totals();
 		this.calculate_discount_amount();
@@ -63,16 +63,16 @@
 		}
 
 		this.frm.refresh_fields();
-	},
+	}
 
-	calculate_discount_amount: function(){
+	calculate_discount_amount(){
 		if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount")) {
 			this.set_discount_amount();
 			this.apply_discount_amount();
 		}
-	},
+	}
 
-	_calculate_taxes_and_totals: function() {
+	_calculate_taxes_and_totals() {
 		this.validate_conversion_rate();
 		this.calculate_item_values();
 		this.initialize_taxes();
@@ -82,9 +82,9 @@
 		this.manipulate_grand_total_for_inclusive_tax();
 		this.calculate_totals();
 		this._cleanup();
-	},
+	}
 
-	validate_conversion_rate: function() {
+	validate_conversion_rate() {
 		this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, (cur_frm) ? precision("conversion_rate") : 9);
 		var conversion_rate_label = frappe.meta.get_label(this.frm.doc.doctype, "conversion_rate",
 			this.frm.doc.name);
@@ -99,9 +99,9 @@
 				frappe.throw(err_message);
 			}
 		}
-	},
+	}
 
-	calculate_item_values: function() {
+	calculate_item_values() {
 		var me = this;
 		if (!this.discount_amount_applied) {
 			$.each(this.frm.doc["items"] || [], function(i, item) {
@@ -121,16 +121,16 @@
 				me.set_in_company_currency(item, ["price_list_rate", "rate", "amount", "net_rate", "net_amount"]);
 			});
 		}
-	},
+	}
 
-	set_in_company_currency: function(doc, fields) {
+	set_in_company_currency(doc, fields) {
 		var me = this;
 		$.each(fields, function(i, f) {
 			doc["base_"+f] = flt(flt(doc[f], precision(f, doc)) * me.frm.doc.conversion_rate, precision("base_" + f, doc));
 		});
-	},
+	}
 
-	initialize_taxes: function() {
+	initialize_taxes() {
 		var me = this;
 
 		$.each(this.frm.doc["taxes"] || [], function(i, tax) {
@@ -152,9 +152,9 @@
 			}
 			frappe.model.round_floats_in(tax);
 		});
-	},
+	}
 
-	fetch_round_off_accounts: function() {
+	fetch_round_off_accounts() {
 		let me = this;
 		frappe.flags.round_off_applicable_accounts = [];
 
@@ -165,14 +165,16 @@
 					"company": me.frm.doc.company,
 					"account_list": frappe.flags.round_off_applicable_accounts
 				},
-				callback: function(r) {
-					frappe.flags.round_off_applicable_accounts.push(...r.message);
+				callback(r) {
+					if (r.message) {
+						frappe.flags.round_off_applicable_accounts.push(...r.message);
+					}
 				}
 			});
 		}
-	},
+	}
 
-	determine_exclusive_rate: function() {
+	determine_exclusive_rate() {
 		var me = this;
 
 		var has_inclusive_tax = false;
@@ -210,9 +212,9 @@
 				me.set_in_company_currency(item, ["net_rate", "net_amount"]);
 			}
 		});
-	},
+	}
 
-	get_current_tax_fraction: function(tax, item_tax_map) {
+	get_current_tax_fraction(tax, item_tax_map) {
 		// Get tax fraction for calculating tax exclusive amount
 		// from tax inclusive amount
 		var current_tax_fraction = 0.0;
@@ -241,14 +243,14 @@
 			inclusive_tax_amount_per_qty *= -1;
 		}
 		return [current_tax_fraction, inclusive_tax_amount_per_qty];
-	},
+	}
 
-	_get_tax_rate: function(tax, item_tax_map) {
+	_get_tax_rate(tax, item_tax_map) {
 		return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ?
 			flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate;
-	},
+	}
 
-	calculate_net_total: function() {
+	calculate_net_total() {
 		var me = this;
 		this.frm.doc.total_qty = this.frm.doc.total = this.frm.doc.base_total = this.frm.doc.net_total = this.frm.doc.base_net_total = 0.0;
 
@@ -261,9 +263,9 @@
 			});
 
 		frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
-	},
+	}
 
-	calculate_taxes: function() {
+	calculate_taxes() {
 		var me = this;
 		this.frm.doc.rounding_adjustment = 0;
 		var actual_tax_dict = {};
@@ -342,9 +344,9 @@
 				}
 			});
 		});
-	},
+	}
 
-	set_cumulative_total: function(row_idx, tax) {
+	set_cumulative_total(row_idx, tax) {
 		var tax_amount = tax.tax_amount_after_discount_amount;
 		if (tax.category == 'Valuation') {
 			tax_amount = 0;
@@ -357,13 +359,13 @@
 		} else {
 			tax.total = flt(this.frm.doc["taxes"][row_idx-1].total + tax_amount, precision("total", tax));
 		}
-	},
+	}
 
-	_load_item_tax_rate: function(item_tax_rate) {
+	_load_item_tax_rate(item_tax_rate) {
 		return item_tax_rate ? JSON.parse(item_tax_rate) : {};
-	},
+	}
 
-	get_current_tax_amount: function(item, tax, item_tax_map) {
+	get_current_tax_amount(item, tax, item_tax_map) {
 		var tax_rate = this._get_tax_rate(tax, item_tax_map);
 		var current_tax_amount = 0.0;
 
@@ -399,9 +401,9 @@
 		this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
 
 		return current_tax_amount;
-	},
+	}
 
-	set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) {
+	set_item_wise_tax(item, tax, tax_rate, current_tax_amount) {
 		// store tax breakup for each item
 		let tax_detail = tax.item_wise_tax_detail;
 		let key = item.item_code || item.item_name;
@@ -411,9 +413,9 @@
 			item_wise_tax_amount += tax_detail[key][1];
 
 		tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
-	},
+	}
 
-	round_off_totals: function(tax) {
+	round_off_totals(tax) {
 		if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
 			tax.tax_amount= Math.round(tax.tax_amount);
 			tax.tax_amount_after_discount_amount = Math.round(tax.tax_amount_after_discount_amount);
@@ -421,16 +423,16 @@
 
 		tax.tax_amount = flt(tax.tax_amount, precision("tax_amount", tax));
 		tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, precision("tax_amount", tax));
-	},
+	}
 
-	round_off_base_values: function(tax) {
+	round_off_base_values(tax) {
 		if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
 			tax.base_tax_amount= Math.round(tax.base_tax_amount);
 			tax.base_tax_amount_after_discount_amount = Math.round(tax.base_tax_amount_after_discount_amount);
 		}
-	},
+	}
 
-	manipulate_grand_total_for_inclusive_tax: function() {
+	manipulate_grand_total_for_inclusive_tax() {
 		var me = this;
 		// if fully inclusive taxes and diff
 		if (this.frm.doc["taxes"] && this.frm.doc["taxes"].length) {
@@ -461,9 +463,9 @@
 				}
 			}
 		}
-	},
+	}
 
-	calculate_totals: function() {
+	calculate_totals() {
 		// Changing sequence can cause rounding_adjustmentng issue and on-screen discrepency
 		var me = this;
 		var tax_count = this.frm.doc["taxes"] ? this.frm.doc["taxes"].length : 0;
@@ -509,9 +511,9 @@
 
 		// rounded totals
 		this.set_rounded_total();
-	},
+	}
 
-	set_rounded_total: function() {
+	set_rounded_total() {
 		var disable_rounded_total = 0;
 		if(frappe.meta.get_docfield(this.frm.doc.doctype, "disable_rounded_total", this.frm.doc.name)) {
 			disable_rounded_total = this.frm.doc.disable_rounded_total;
@@ -533,9 +535,9 @@
 
 			this.set_in_company_currency(this.frm.doc, ["rounding_adjustment", "rounded_total"]);
 		}
-	},
+	}
 
-	_cleanup: function() {
+	_cleanup() {
 		this.frm.doc.base_in_words = this.frm.doc.in_words = "";
 
 		if(this.frm.doc["items"] && this.frm.doc["items"].length) {
@@ -562,16 +564,16 @@
 				tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail);
 			});
 		}
-	},
+	}
 
-	set_discount_amount: function() {
+	set_discount_amount() {
 		if(this.frm.doc.additional_discount_percentage) {
 			this.frm.doc.discount_amount = flt(flt(this.frm.doc[frappe.scrub(this.frm.doc.apply_discount_on)])
 				* this.frm.doc.additional_discount_percentage / 100, precision("discount_amount"));
 		}
-	},
+	}
 
-	apply_discount_amount: function() {
+	apply_discount_amount() {
 		var me = this;
 		var distributed_amount = 0.0;
 		this.frm.doc.base_discount_amount = 0.0;
@@ -609,9 +611,9 @@
 				this._calculate_taxes_and_totals();
 			}
 		}
-	},
+	}
 
-	get_total_for_discount_amount: function() {
+	get_total_for_discount_amount() {
 		if(this.frm.doc.apply_discount_on == "Net Total") {
 			return this.frm.doc.net_total;
 		} else {
@@ -635,27 +637,27 @@
 
 			return flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total"));
 		}
-	},
+	}
 
-	calculate_total_advance: function(update_paid_amount) {
+	calculate_total_advance(update_paid_amount) {
 		var total_allocated_amount = frappe.utils.sum($.map(this.frm.doc["advances"] || [], function(adv) {
 			return flt(adv.allocated_amount, precision("allocated_amount", adv));
 		}));
 		this.frm.doc.total_advance = flt(total_allocated_amount, precision("total_advance"));
 
 		this.calculate_outstanding_amount(update_paid_amount);
-	},
+	}
 
-	is_internal_invoice: function() {
+	is_internal_invoice() {
 		if (['Sales Invoice', 'Purchase Invoice'].includes(this.frm.doc.doctype)) {
 			if (this.frm.doc.company === this.frm.doc.represents_company) {
 				return true;
 			}
 		}
 		return false;
-	},
+	}
 
-	calculate_outstanding_amount: function(update_paid_amount) {
+	calculate_outstanding_amount(update_paid_amount) {
 		// NOTE:
 		// paid_amount and write_off_amount is only for POS/Loyalty Point Redemption Invoice
 		// total_advance is only for non POS Invoice
@@ -703,9 +705,9 @@
 			this.frm.doc.outstanding_amount =  flt(total_amount_to_pay - flt(paid_amount) +
 				flt(this.frm.doc.change_amount * this.frm.doc.conversion_rate), precision("outstanding_amount"));
 		}
-	},
+	}
 
-	update_paid_amount_for_return: function() {
+	update_paid_amount_for_return() {
 		var grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total;
 
 		if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
@@ -729,9 +731,9 @@
 		this.frm.refresh_fields();
 
 		this.calculate_paid_amount();
-	},
+	}
 
-	set_default_payment: function(total_amount_to_pay, update_paid_amount) {
+	set_default_payment(total_amount_to_pay, update_paid_amount) {
 		var me = this;
 		var payment_status = true;
 		if(this.frm.doc.is_pos && (update_paid_amount===undefined || update_paid_amount)) {
@@ -747,9 +749,9 @@
 				}
 			});
 		}
-	},
+	}
 
-	calculate_paid_amount: function() {
+	calculate_paid_amount() {
 		var me = this;
 		var paid_amount = 0.0;
 		var base_paid_amount = 0.0;
@@ -769,9 +771,9 @@
 
 		this.frm.set_value('paid_amount', flt(paid_amount, precision("paid_amount")));
 		this.frm.set_value('base_paid_amount', flt(base_paid_amount, precision("base_paid_amount")));
-	},
+	}
 
-	calculate_change_amount: function(){
+	calculate_change_amount(){
 		this.frm.doc.change_amount = 0.0;
 		this.frm.doc.base_change_amount = 0.0;
 		if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype)
@@ -790,9 +792,9 @@
 					precision("base_change_amount"));
 			}
 		}
-	},
+	}
 
-	calculate_write_off_amount: function(){
+	calculate_write_off_amount(){
 		if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
 			this.frm.doc.write_off_amount = flt(this.frm.doc.grand_total - this.frm.doc.paid_amount
 				+ this.frm.doc.change_amount, precision("write_off_amount"));
@@ -804,4 +806,4 @@
 		}
 		this.calculate_outstanding_amount(false);
 	}
-});
+};
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 0af8da7..0ffda07 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -3,9 +3,9 @@
 
 frappe.provide('erpnext.accounts.dimensions');
 
-erpnext.TransactionController = erpnext.taxes_and_totals.extend({
-	setup: function() {
-		this._super();
+erpnext.TransactionController = class TransactionController extends erpnext.taxes_and_totals {
+	setup() {
+		super.setup();
 		frappe.flags.hide_serial_batch_dialog = true;
 		frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
 			var item = frappe.get_doc(cdt, cdn);
@@ -222,8 +222,8 @@
 			});
 		}
 
-	},
-	onload: function() {
+	}
+	onload() {
 		var me = this;
 
 		if(this.frm.doc.__islocal) {
@@ -249,15 +249,15 @@
 				}
 			]);
 		}
-	},
+	}
 
-	is_return: function() {
+	is_return() {
 		if(!this.frm.doc.is_return && this.frm.doc.return_against) {
 			this.frm.set_value('return_against', '');
 		}
-	},
+	}
 
-	setup_quality_inspection: function() {
+	setup_quality_inspection() {
 		if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)) {
 			return;
 		}
@@ -290,9 +290,9 @@
 				}
 			}
 		});
-	},
+	}
 
-	make_payment_request: function() {
+	make_payment_request() {
 		var me = this;
 		const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], this.frm.doc.doctype))
 			? "Inward" : "Outward";
@@ -314,9 +314,9 @@
 				}
 			}
 		})
-	},
+	}
 
-	onload_post_render: function() {
+	onload_post_render() {
 		if(this.frm.doc.__islocal && !(this.frm.doc.taxes || []).length
 			&& !(this.frm.doc.__onload ? this.frm.doc.__onload.load_after_mapping : false)) {
 			frappe.after_ajax(() => this.apply_default_taxes());
@@ -328,9 +328,9 @@
 			this.setup_item_selector();
 			this.frm.get_field("items").grid.set_multiple_add("item_code", "qty");
 		}
-	},
+	}
 
-	refresh: function() {
+	refresh() {
 		erpnext.toggle_naming_series();
 		erpnext.hide_company();
 		this.set_dynamic_labels();
@@ -360,9 +360,9 @@
 					.appendTo($input_group);
 			}
 		}
-	},
+	}
 
-	scan_barcode: function() {
+	scan_barcode() {
 		let scan_barcode_field = this.frm.fields_dict["scan_barcode"];
 
 		let show_description = function(idx, exist = null) {
@@ -434,9 +434,9 @@
 			});
 		}
 		return false;
-	},
+	}
 
-	apply_default_taxes: function() {
+	apply_default_taxes() {
 		var me = this;
 		var taxes_and_charges_field = frappe.meta.get_docfield(me.frm.doc.doctype, "taxes_and_charges",
 			me.frm.doc.name);
@@ -475,22 +475,22 @@
 				}
 			});
 		}
-	},
+	}
 
-	setup_sms: function() {
+	setup_sms() {
 		var me = this;
 		let blacklist = ['Purchase Invoice', 'BOM'];
 		if(this.frm.doc.docstatus===1 && !in_list(["Lost", "Stopped", "Closed"], this.frm.doc.status)
 			&& !blacklist.includes(this.frm.doctype)) {
 			this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); });
 		}
-	},
+	}
 
-	send_sms: function() {
+	send_sms() {
 		var sms_man = new erpnext.SMSManager(this.frm.doc);
-	},
+	}
 
-	barcode: function(doc, cdt, cdn) {
+	barcode(doc, cdt, cdn) {
 		var d = locals[cdt][cdn];
 		if(d.barcode=="" || d.barcode==null) {
 			// barcode cleared, remove item
@@ -499,9 +499,9 @@
 
 		this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1;
 		this.item_code(doc, cdt, cdn);
-	},
+	}
 
-	item_code: function(doc, cdt, cdn) {
+	item_code(doc, cdt, cdn) {
 		var me = this;
 		var item = frappe.get_doc(cdt, cdn);
 		var update_stock = 0, show_batch_dialog = 0;
@@ -651,9 +651,9 @@
 				});
 			}
 		}
-	},
+	}
 
-	price_list_rate: function(doc, cdt, cdn) {
+	price_list_rate(doc, cdt, cdn) {
 		var item = frappe.get_doc(cdt, cdn);
 		frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]);
 
@@ -665,17 +665,17 @@
 				precision("rate", item));
 
 		this.calculate_taxes_and_totals();
-	},
+	}
 
-	margin_rate_or_amount: function(doc, cdt, cdn) {
+	margin_rate_or_amount(doc, cdt, cdn) {
 		// calculated the revised total margin and rate on margin rate changes
 		let item = frappe.get_doc(cdt, cdn);
 		this.apply_pricing_rule_on_item(item);
 		this.calculate_taxes_and_totals();
 		cur_frm.refresh_fields();
-	},
+	}
 
-	margin_type: function(doc, cdt, cdn) {
+	margin_type(doc, cdt, cdn) {
 		// calculate the revised total margin and rate on margin type changes
 		let item = frappe.get_doc(cdt, cdn);
 		if (!item.margin_type) {
@@ -685,9 +685,9 @@
 			this.calculate_taxes_and_totals();
 			cur_frm.refresh_fields();
 		}
-	},
+	}
 
-	get_incoming_rate: function(item, posting_date, posting_time, voucher_type, company) {
+	get_incoming_rate(item, posting_date, posting_time, voucher_type, company) {
 
 		let item_args = {
 			'item_code': item.item_code,
@@ -710,9 +710,9 @@
 				frappe.model.set_value(item.doctype, item.name, 'rate', r.message * item.conversion_factor);
 			}
 		});
-	},
+	}
 
-	add_taxes_from_item_tax_template: function(item_tax_map) {
+	add_taxes_from_item_tax_template(item_tax_map) {
 		let me = this;
 
 		if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) {
@@ -730,9 +730,9 @@
 				}
 			});
 		}
-	},
+	}
 
-	serial_no: function(doc, cdt, cdn) {
+	serial_no(doc, cdt, cdn) {
 		var me = this;
 		var item = frappe.get_doc(cdt, cdn);
 
@@ -752,9 +752,9 @@
 				}
 			}
 		}
-	},
+	}
 
-	update_qty: function(cdt, cdn) {
+	update_qty(cdt, cdn) {
 		var valid_serial_nos = [];
 		var serialnos = [];
 		var item = frappe.get_doc(cdt, cdn);
@@ -767,17 +767,17 @@
 		frappe.model.set_value(item.doctype, item.name,
 			"qty", valid_serial_nos.length / item.conversion_factor);
 		frappe.model.set_value(item.doctype, item.name, "stock_qty", valid_serial_nos.length);
-	},
+	}
 
-	validate: function() {
+	validate() {
 		this.calculate_taxes_and_totals(false);
-	},
+	}
 
-	update_stock: function() {
+	update_stock() {
 		this.frm.trigger('set_default_internal_warehouse');
-	},
+	}
 
-	set_default_internal_warehouse: function() {
+	set_default_internal_warehouse() {
 		let me = this;
 		if ((this.frm.doc.doctype === 'Sales Invoice' && me.frm.doc.update_stock)
 			|| this.frm.doc.doctype == 'Delivery Note') {
@@ -796,9 +796,9 @@
 				});
 			}
 		}
-	},
+	}
 
-	company: function() {
+	company() {
 		var me = this;
 		var set_pricing = function() {
 			if(me.frm.doc.company && me.frm.fields_dict.currency) {
@@ -905,16 +905,16 @@
 		if(this.frm.doc.company) {
 			erpnext.last_selected_company = this.frm.doc.company;
 		}
-	},
+	}
 
-	transaction_date: function() {
+	transaction_date() {
 		if (this.frm.doc.transaction_date) {
 			this.frm.transaction_date = this.frm.doc.transaction_date;
 			frappe.ui.form.trigger(this.frm.doc.doctype, "currency");
 		}
-	},
+	}
 
-	posting_date: function() {
+	posting_date() {
 		var me = this;
 		if (this.frm.doc.posting_date) {
 			this.frm.posting_date = this.frm.doc.posting_date;
@@ -943,9 +943,9 @@
 				frappe.ui.form.trigger(me.frm.doc.doctype, "currency");
 			}
 		}
-	},
+	}
 
-	due_date: function() {
+	due_date() {
 		// due_date is to be changed, payment terms template and/or payment schedule must
 		// be removed as due_date is automatically changed based on payment terms
 		if (this.frm.doc.due_date && !this.frm.updating_party_details && !this.frm.doc.is_pos) {
@@ -968,13 +968,13 @@
 				frappe.msgprint(final_message);
 			}
 		}
-	},
+	}
 
-	bill_date: function() {
+	bill_date() {
 		this.posting_date();
-	},
+	}
 
-	recalculate_terms: function() {
+	recalculate_terms() {
 		const doc = this.frm.doc;
 		if (doc.payment_terms_template) {
 			this.payment_terms_template();
@@ -993,17 +993,17 @@
 				}
 			);
 		}
-	},
+	}
 
-	get_company_currency: function() {
+	get_company_currency() {
 		return erpnext.get_currency(this.frm.doc.company);
-	},
+	}
 
-	contact_person: function() {
+	contact_person() {
 		erpnext.utils.get_contact_details(this.frm);
-	},
+	}
 
-	currency: function() {
+	currency() {
 		/* manqala 19/09/2016: let the translation date be whichever of the transaction_date or posting_date is available */
 		var transaction_date = this.frm.doc.transaction_date || this.frm.doc.posting_date;
 		/* end manqala */
@@ -1025,9 +1025,9 @@
 		} else {
 			this.conversion_rate();
 		}
-	},
+	}
 
-	conversion_rate: function() {
+	conversion_rate() {
 		const me = this.frm;
 		if(this.frm.doc.currency === this.get_company_currency()) {
 			this.frm.set_value("conversion_rate", 1.0);
@@ -1047,9 +1047,9 @@
 		}
 		// Make read only if Accounts Settings doesn't allow stale rates
 		this.frm.set_df_property("conversion_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1);
-	},
+	}
 
-	shipping_rule: function() {
+	shipping_rule() {
 		var me = this;
 		if(this.frm.doc.shipping_rule) {
 			return this.frm.call({
@@ -1065,9 +1065,9 @@
 		else {
 			me.calculate_taxes_and_totals();
 		}
-	},
+	}
 
-	set_margin_amount_based_on_currency: function(exchange_rate) {
+	set_margin_amount_based_on_currency(exchange_rate) {
 		if (in_list(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "Purchase Invoice", "Purchase Order", "Purchase Receipt"]), this.frm.doc.doctype) {
 			var me = this;
 			$.each(this.frm.doc.items || [], function(i, d) {
@@ -1077,9 +1077,9 @@
 				}
 			});
 		}
-	},
+	}
 
-	set_actual_charges_based_on_currency: function(exchange_rate) {
+	set_actual_charges_based_on_currency(exchange_rate) {
 		var me = this;
 		$.each(this.frm.doc.taxes || [], function(i, d) {
 			if(d.charge_type == "Actual") {
@@ -1087,9 +1087,9 @@
 					flt(d.tax_amount) / flt(exchange_rate));
 			}
 		});
-	},
+	}
 
-	get_exchange_rate: function(transaction_date, from_currency, to_currency, callback) {
+	get_exchange_rate(transaction_date, from_currency, to_currency, callback) {
 		var args;
 		if (["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"].includes(this.frm.doctype)) {
 			args = "for_selling";
@@ -1113,9 +1113,9 @@
 				callback(flt(r.message));
 			}
 		});
-	},
+	}
 
-	price_list_currency: function() {
+	price_list_currency() {
 		var me=this;
 		this.set_dynamic_labels();
 
@@ -1129,9 +1129,9 @@
 		} else {
 			this.plc_conversion_rate();
 		}
-	},
+	}
 
-	plc_conversion_rate: function() {
+	plc_conversion_rate() {
 		if(this.frm.doc.price_list_currency === this.get_company_currency()) {
 			this.frm.set_value("plc_conversion_rate", 1.0);
 		} else if(this.frm.doc.price_list_currency === this.frm.doc.currency
@@ -1143,9 +1143,9 @@
 		if(!this.in_apply_price_list) {
 			this.apply_price_list(null, true);
 		}
-	},
+	}
 
-	uom: function(doc, cdt, cdn) {
+	uom(doc, cdt, cdn) {
 		var me = this;
 		var item = frappe.get_doc(cdt, cdn);
 		if(item.item_code && item.uom) {
@@ -1163,9 +1163,9 @@
 			});
 		}
 		me.calculate_stock_uom_rate(doc, cdt, cdn);
-	},
+	}
 
-	conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) {
+	conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate) {
 		if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
 			var item = frappe.get_doc(cdt, cdn);
 			frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
@@ -1190,35 +1190,35 @@
 			}
 			this.calculate_stock_uom_rate(doc, cdt, cdn);
 		}
-	},
+	}
 
-	batch_no: function(doc, cdt, cdn) {
+	batch_no(doc, cdt, cdn) {
 		let item = frappe.get_doc(cdt, cdn);
 		this.apply_price_list(item, true);
-	},
+	}
 
-	toggle_conversion_factor: function(item) {
+	toggle_conversion_factor(item) {
 		// toggle read only property for conversion factor field if the uom and stock uom are same
 		if(this.frm.get_field('items').grid.fields_map.conversion_factor) {
 			this.frm.fields_dict.items.grid.toggle_enable("conversion_factor",
 				((item.uom != item.stock_uom) && !frappe.meta.get_docfield(cur_frm.fields_dict.items.grid.doctype, "conversion_factor").read_only)? true: false);
 		}
 
-	},
+	}
 
-	qty: function(doc, cdt, cdn) {
+	qty(doc, cdt, cdn) {
 		let item = frappe.get_doc(cdt, cdn);
 		this.conversion_factor(doc, cdt, cdn, true);
 		this.calculate_stock_uom_rate(doc, cdt, cdn);
 		this.apply_pricing_rule(item, true);
-	},
+	}
 
-	calculate_stock_uom_rate: function(doc, cdt, cdn) {
+	calculate_stock_uom_rate(doc, cdt, cdn) {
 		let item = frappe.get_doc(cdt, cdn);
 		item.stock_uom_rate = flt(item.rate)/flt(item.conversion_factor);
 		refresh_field("stock_uom_rate", item.name, item.parentfield);
-	},
-	service_stop_date: function(frm, cdt, cdn) {
+	}
+	service_stop_date(frm, cdt, cdn) {
 		var child = locals[cdt][cdn];
 
 		if(child.service_stop_date) {
@@ -1234,9 +1234,9 @@
 				frappe.throw(__("Service Stop Date cannot be after Service End Date"));
 			}
 		}
-	},
+	}
 
-	service_start_date: function(frm, cdt, cdn) {
+	service_start_date(frm, cdt, cdn) {
 		var child = locals[cdt][cdn];
 
 		if(child.service_start_date) {
@@ -1248,9 +1248,9 @@
 				}
 			})
 		}
-	},
+	}
 
-	calculate_net_weight: function(){
+	calculate_net_weight(){
 		/* Calculate Total Net Weight then further applied shipping rule to calculate shipping charges.*/
 		var me = this;
 		this.frm.doc.total_net_weight= 0.0;
@@ -1260,9 +1260,9 @@
 		});
 		refresh_field("total_net_weight");
 		this.shipping_rule();
-	},
+	}
 
-	set_dynamic_labels: function() {
+	set_dynamic_labels() {
 		// What TODO? should we make price list system non-mandatory?
 		this.frm.toggle_reqd("plc_conversion_rate",
 			!!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency));
@@ -1271,9 +1271,9 @@
 		this.change_form_labels(company_currency);
 		this.change_grid_labels(company_currency);
 		this.frm.refresh_fields();
-	},
+	}
 
-	change_form_labels: function(company_currency) {
+	change_form_labels(company_currency) {
 		var me = this;
 
 		this.frm.set_currency_labels(["base_total", "base_net_total", "base_total_taxes_and_charges",
@@ -1320,16 +1320,16 @@
 		if(frappe.meta.get_docfield(cur_frm.doctype, "base_net_total"))
 			cur_frm.toggle_display("base_net_total", (show && (me.frm.doc.currency != company_currency)));
 
-	},
+	}
 
-	change_grid_labels: function(company_currency) {
+	change_grid_labels(company_currency) {
 		var me = this;
 
 		this.update_item_grid_labels(company_currency);
 
 		this.toggle_item_grid_columns(company_currency);
 
-		if(this.frm.fields_dict["operations"]) {
+		if (this.frm.doc.operations && this.frm.doc.operations.length > 0) {
 			this.frm.set_currency_labels(["operating_cost", "hour_rate"], this.frm.doc.currency, "operations");
 			this.frm.set_currency_labels(["base_operating_cost", "base_hour_rate"], company_currency, "operations");
 
@@ -1340,7 +1340,7 @@
 			});
 		}
 
-		if(this.frm.fields_dict["scrap_items"]) {
+		if (this.frm.doc.scrap_items && this.frm.doc.scrap_items.length > 0) {
 			this.frm.set_currency_labels(["rate", "amount"], this.frm.doc.currency, "scrap_items");
 			this.frm.set_currency_labels(["base_rate", "base_amount"], company_currency, "scrap_items");
 
@@ -1351,21 +1351,21 @@
 			});
 		}
 
-		if(this.frm.fields_dict["taxes"]) {
+		if (this.frm.doc.taxes && this.frm.doc.taxes.length > 0) {
 			this.frm.set_currency_labels(["tax_amount", "total", "tax_amount_after_discount"], this.frm.doc.currency, "taxes");
 
 			this.frm.set_currency_labels(["base_tax_amount", "base_total", "base_tax_amount_after_discount"], company_currency, "taxes");
 		}
 
-		if(this.frm.fields_dict["advances"]) {
+		if (this.frm.doc.advances && this.frm.doc.advances.length > 0) {
 			this.frm.set_currency_labels(["advance_amount", "allocated_amount"],
 				this.frm.doc.party_account_currency, "advances");
 		}
 
 		this.update_payment_schedule_grid_labels(company_currency);
-	},
+	}
 
-	update_item_grid_labels: function(company_currency) {
+	update_item_grid_labels(company_currency) {
 		this.frm.set_currency_labels([
 			"base_rate", "base_net_rate", "base_price_list_rate",
 			"base_amount", "base_net_amount", "base_rate_with_margin"
@@ -1375,9 +1375,9 @@
 			"rate", "net_rate", "price_list_rate", "amount",
 			"net_amount", "stock_uom_rate", "rate_with_margin"
 		], this.frm.doc.currency, "items");
-	},
+	}
 
-	update_payment_schedule_grid_labels: function(company_currency) {
+	update_payment_schedule_grid_labels(company_currency) {
 		const me = this;
 		if (this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length > 0) {
 			this.frm.set_currency_labels(["base_payment_amount", "base_outstanding", "base_paid_amount"],
@@ -1391,9 +1391,9 @@
 					schedule_grid.set_column_disp(fname, me.frm.doc.currency != company_currency);
 			});
 		}
-	},
+	}
 
-	toggle_item_grid_columns: function(company_currency) {
+	toggle_item_grid_columns(company_currency) {
 		const me = this;
 		// toggle columns
 		var item_grid = this.frm.fields_dict["items"].grid;
@@ -1414,21 +1414,21 @@
 			if(frappe.meta.get_docfield(item_grid.doctype, fname))
 				item_grid.set_column_disp(fname, (show && (me.frm.doc.currency != company_currency)));
 		});
-	},
+	}
 
-	recalculate: function() {
+	recalculate() {
 		this.calculate_taxes_and_totals();
-	},
+	}
 
-	recalculate_values: function() {
+	recalculate_values() {
 		this.calculate_taxes_and_totals();
-	},
+	}
 
-	calculate_charges: function() {
+	calculate_charges() {
 		this.calculate_taxes_and_totals();
-	},
+	}
 
-	ignore_pricing_rule: function() {
+	ignore_pricing_rule() {
 		if(this.frm.doc.ignore_pricing_rule) {
 			var me = this;
 			var item_list = [];
@@ -1462,9 +1462,9 @@
 		} else {
 			this.apply_pricing_rule();
 		}
-	},
+	}
 
-	apply_pricing_rule: function(item, calculate_taxes_and_totals) {
+	apply_pricing_rule(item, calculate_taxes_and_totals) {
 		var me = this;
 		var args = this._get_args(item);
 		if (!(args.items && args.items.length)) {
@@ -1483,9 +1483,9 @@
 				}
 			}
 		});
-	},
+	}
 
-	_get_args: function(item) {
+	_get_args(item) {
 		var me = this;
 		return {
 			"items": this._get_item_list(item),
@@ -1513,9 +1513,9 @@
 			"pos_profile": me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '',
 			"coupon_code": me.frm.doc.coupon_code
 		};
-	},
+	}
 
-	_get_item_list: function(item) {
+	_get_item_list(item) {
 		var item_list = [];
 		var append_item = function(d) {
 			if (d.item_code) {
@@ -1556,9 +1556,9 @@
 			});
 		}
 		return item_list;
-	},
+	}
 
-	_set_values_for_item_list: function(children) {
+	_set_values_for_item_list(children) {
 		var me = this;
 		var price_list_rate_changed = false;
 		var items_rule_dict = {};
@@ -1598,9 +1598,9 @@
 		me.apply_rule_on_other_items(items_rule_dict);
 
 		if(!price_list_rate_changed) me.calculate_taxes_and_totals();
-	},
+	}
 
-	apply_rule_on_other_items: function(args) {
+	apply_rule_on_other_items(args) {
 		const me = this;
 		const fields = ["discount_percentage", "pricing_rules", "discount_amount", "rate"];
 
@@ -1619,9 +1619,9 @@
 				});
 			}
 		}
-	},
+	}
 
-	apply_product_discount: function(args) {
+	apply_product_discount(args) {
 		const items = this.frm.doc.items.filter(d => (d.is_free_item)) || [];
 
 		const exist_items = items.map(row => (row.item_code, row.pricing_rules));
@@ -1646,9 +1646,9 @@
 		// free_item_data is a temporary variable
 		args.free_item_data = '';
 		refresh_field('items');
-	},
+	}
 
-	apply_price_list: function(item, reset_plc_conversion) {
+	apply_price_list(item, reset_plc_conversion) {
 		// We need to reset plc_conversion_rate sometimes because the call to
 		// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
 		if (!reset_plc_conversion) {
@@ -1687,9 +1687,9 @@
 		}).always(() => {
 			me.in_apply_price_list = false;
 		});
-	},
+	}
 
-	remove_pricing_rule: function(item) {
+	remove_pricing_rule(item) {
 		let me = this;
 		const fields = ["discount_percentage",
 			"discount_amount", "margin_rate_or_amount", "rate_with_margin"];
@@ -1723,18 +1723,18 @@
 
 			me.trigger_price_list_rate();
 		}
-	},
+	}
 
-	trigger_price_list_rate: function() {
+	trigger_price_list_rate() {
 		var me  = this;
 
 		this.frm.doc.items.forEach(child_row => {
 			me.frm.script_manager.trigger("price_list_rate",
 				child_row.doctype, child_row.name);
 		})
-	},
+	}
 
-	validate_company_and_party: function() {
+	validate_company_and_party() {
 		var me = this;
 		var valid = true;
 
@@ -1749,9 +1749,9 @@
 			}
 		});
 		return valid;
-	},
+	}
 
-	get_terms: function() {
+	get_terms() {
 		var me = this;
 
 		erpnext.utils.get_terms(this.frm.doc.tc_name, this.frm.doc, function(r) {
@@ -1759,9 +1759,9 @@
 				me.frm.set_value("terms", r.message);
 			}
 		});
-	},
+	}
 
-	taxes_and_charges: function() {
+	taxes_and_charges() {
 		var me = this;
 		if(this.frm.doc.taxes_and_charges) {
 			return this.frm.call({
@@ -1787,9 +1787,9 @@
 				}
 			});
 		}
-	},
+	}
 
-	tax_category: function() {
+	tax_category() {
 		var me = this;
 		if(me.frm.updating_party_details) return;
 
@@ -1797,9 +1797,9 @@
 			() => this.update_item_tax_map(),
 			() => erpnext.utils.set_taxes(this.frm, "tax_category"),
 		]);
-	},
+	}
 
-	item_tax_template: function(doc, cdt, cdn) {
+	item_tax_template(doc, cdt, cdn) {
 		var me = this;
 		if(me.frm.updating_party_details) return;
 
@@ -1825,9 +1825,9 @@
 			item.item_tax_rate = "{}";
 			me.calculate_taxes_and_totals();
 		}
-	},
+	}
 
-	update_item_tax_map: function() {
+	update_item_tax_map() {
 		var me = this;
 		var item_codes = [];
 		$.each(this.frm.doc.items || [], function(i, item) {
@@ -1863,9 +1863,9 @@
 				}
 			});
 		}
-	},
+	}
 
-	is_recurring: function() {
+	is_recurring() {
 		// set default values for recurring documents
 		if(this.frm.doc.is_recurring && this.frm.doc.__islocal) {
 			frappe.msgprint(__("Please set recurring after saving"));
@@ -1888,9 +1888,9 @@
 		}
 
 		refresh_many(["notification_email_address", "repeat_on_day_of_month"]);
-	},
+	}
 
-	from_date: function() {
+	from_date() {
 		// set to_date
 		if(this.frm.doc.from_date) {
 			var recurring_type_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6,
@@ -1904,25 +1904,25 @@
 				refresh_field('to_date');
 			}
 		}
-	},
+	}
 
-	set_gross_profit: function(item) {
+	set_gross_profit(item) {
 		if (["Sales Order", "Quotation"].includes(this.frm.doc.doctype) && item.valuation_rate) {
 			var rate = flt(item.rate) * flt(this.frm.doc.conversion_rate || 1);
 			item.gross_profit = flt(((rate - item.valuation_rate) * item.stock_qty), precision("amount", item));
 		}
-	},
+	}
 
-	setup_item_selector: function() {
+	setup_item_selector() {
 		// TODO: remove item selector
 
 		return;
 		// if(!this.item_selector) {
 		// 	this.item_selector = new erpnext.ItemSelector({frm: this.frm});
 		// }
-	},
+	}
 
-	get_advances: function() {
+	get_advances() {
 		if(!this.frm.is_return) {
 			return this.frm.call({
 				method: "set_advances",
@@ -1932,9 +1932,9 @@
 				}
 			})
 		}
-	},
+	}
 
-	make_payment_entry: function() {
+	make_payment_entry() {
 		return frappe.call({
 			method: cur_frm.cscript.get_method_for_payment(),
 			args: {
@@ -1947,9 +1947,9 @@
 				// cur_frm.refresh_fields()
 			}
 		});
-	},
+	}
 
-	get_method_for_payment: function(){
+	get_method_for_payment(){
 		var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
 		if(cur_frm.doc.__onload && cur_frm.doc.__onload.make_payment_via_journal_entry){
 			if(in_list(['Sales Invoice', 'Purchase Invoice'],  cur_frm.doc.doctype)){
@@ -1960,9 +1960,9 @@
 		}
 
 		return method
-	},
+	}
 
-	set_query_for_batch: function(doc, cdt, cdn) {
+	set_query_for_batch(doc, cdt, cdn) {
 		// Show item's batches in the dropdown of batch no
 
 		var me = this;
@@ -1992,9 +1992,9 @@
 				filters: filters
 			}
 		}
-	},
+	}
 
-	set_query_for_item_tax_template: function(doc, cdt, cdn) {
+	set_query_for_item_tax_template(doc, cdt, cdn) {
 		var item = frappe.get_doc(cdt, cdn);
 		if(!item.item_code) {
 			return doc.company ? {filters: {company: doc.company}} : {};
@@ -2014,9 +2014,9 @@
 				filters: filters
 			}
 		}
-	},
+	}
 
-	payment_terms_template: function() {
+	payment_terms_template() {
 		var me = this;
 		const doc = this.frm.doc;
 		if(doc.payment_terms_template && doc.doctype !== 'Delivery Note') {
@@ -2039,9 +2039,9 @@
 				}
 			})
 		}
-	},
+	}
 
-	payment_term: function(doc, cdt, cdn) {
+	payment_term(doc, cdt, cdn) {
 		const me = this;
 		var row = locals[cdt][cdn];
 		if(row.payment_term) {
@@ -2065,17 +2065,17 @@
 				}
 			})
 		}
-	},
+	}
 
-	against_blanket_order: function(doc, cdt, cdn) {
+	against_blanket_order(doc, cdt, cdn) {
 		var item = locals[cdt][cdn];
 		if(!item.against_blanket_order) {
 			frappe.model.set_value(this.frm.doctype + " Item", item.name, "blanket_order", null);
 			frappe.model.set_value(this.frm.doctype + " Item", item.name, "blanket_order_rate", 0.00);
 		}
-	},
+	}
 
-	blanket_order: function(doc, cdt, cdn) {
+	blanket_order(doc, cdt, cdn) {
 		var me = this;
 		var item = locals[cdt][cdn];
 		if (item.blanket_order && (item.parenttype=="Sales Order" || item.parenttype=="Purchase Order")) {
@@ -2103,34 +2103,34 @@
 				}
 			})
 		}
-	},
+	}
 
-	set_reserve_warehouse: function() {
+	set_reserve_warehouse() {
 		this.autofill_warehouse(this.frm.doc.supplied_items, "reserve_warehouse", this.frm.doc.set_reserve_warehouse);
-	},
+	}
 
-	set_warehouse: function() {
+	set_warehouse() {
 		this.autofill_warehouse(this.frm.doc.items, "warehouse", this.frm.doc.set_warehouse);
-	},
+	}
 
-	set_target_warehouse: function() {
+	set_target_warehouse() {
 		this.autofill_warehouse(this.frm.doc.items, "target_warehouse", this.frm.doc.set_target_warehouse);
-	},
+	}
 
-	set_from_warehouse: function() {
+	set_from_warehouse() {
 		this.autofill_warehouse(this.frm.doc.items, "from_warehouse", this.frm.doc.set_from_warehouse);
-	},
+	}
 
-	autofill_warehouse : function (child_table, warehouse_field, warehouse) {
+	autofill_warehouse(child_table, warehouse_field, warehouse) {
 		if (warehouse && child_table && child_table.length) {
 			let doctype = child_table[0].doctype;
 			$.each(child_table || [], function(i, item) {
 				frappe.model.set_value(doctype, item.name, warehouse_field, warehouse);
 			});
 		}
-	},
+	}
 
-	coupon_code: function() {
+	coupon_code() {
 		var me = this;
 		frappe.run_serially([
 			() => this.frm.doc.ignore_pricing_rule=1,
@@ -2139,7 +2139,7 @@
 			() => me.apply_pricing_rule()
 		]);
 	}
-});
+};
 
 erpnext.show_serial_batch_selector = function (frm, d, callback, on_close, show_dialog) {
 	let warehouse, receiving_stock, existing_stock;
diff --git a/erpnext/public/js/erpnext-web.bundle.js b/erpnext/public/js/erpnext-web.bundle.js
new file mode 100644
index 0000000..7db6967
--- /dev/null
+++ b/erpnext/public/js/erpnext-web.bundle.js
@@ -0,0 +1,2 @@
+import "./website_utils";
+import "./shopping_cart";
diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js
new file mode 100644
index 0000000..519cfca
--- /dev/null
+++ b/erpnext/public/js/erpnext.bundle.js
@@ -0,0 +1,27 @@
+import "./conf";
+import "./utils";
+import "./queries";
+import "./sms_manager";
+import "./utils/party";
+import "./controllers/stock_controller";
+import "./payment/payments";
+import "./controllers/taxes_and_totals";
+import "./controllers/transaction";
+import "./templates/item_selector.html";
+import "./templates/employees_to_mark_attendance.html";
+import "./utils/item_selector";
+import "./help_links";
+import "./agriculture/ternary_plot";
+import "./templates/item_quick_entry.html";
+import "./utils/item_quick_entry";
+import "./utils/customer_quick_entry";
+import "./education/student_button.html";
+import "./education/assessment_result_tool.html";
+import "./hub/hub_factory";
+import "./call_popup/call_popup";
+import "./utils/dimension_tree_filter";
+import "./telephony";
+import "./templates/call_link.html";
+
+// import { sum } from 'frappe/public/utils/util.js'
+
diff --git a/erpnext/public/js/hub/hub_factory.js b/erpnext/public/js/hub/hub_factory.js
index 8dab2d6..9c67c1c 100644
--- a/erpnext/public/js/hub/hub_factory.js
+++ b/erpnext/public/js/hub/hub_factory.js
@@ -19,11 +19,7 @@
 	}
 
 	make(page_name) {
-		const assets = [
-			'/assets/js/marketplace.min.js'
-		];
-
-		frappe.require(assets, () => {
+		frappe.require('marketplace.bundle.js', () => {
 			erpnext.hub.marketplace = new erpnext.hub.Marketplace({
 				parent: this.make_page(true, page_name)
 			});
diff --git a/erpnext/public/js/hub/marketplace.js b/erpnext/public/js/hub/marketplace.bundle.js
similarity index 100%
rename from erpnext/public/js/hub/marketplace.js
rename to erpnext/public/js/hub/marketplace.bundle.js
diff --git a/erpnext/public/js/item-dashboard.bundle.js b/erpnext/public/js/item-dashboard.bundle.js
new file mode 100644
index 0000000..2d329e2
--- /dev/null
+++ b/erpnext/public/js/item-dashboard.bundle.js
@@ -0,0 +1,5 @@
+import "../../stock/dashboard/item_dashboard.html";
+import "../../stock/dashboard/item_dashboard_list.html";
+import "../../stock/dashboard/item_dashboard.js";
+import "../../stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html";
+import "../../stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html";
diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js
index 0d656bc..7df976c 100644
--- a/erpnext/public/js/payment/payments.js
+++ b/erpnext/public/js/payment/payments.js
@@ -1,31 +1,31 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-erpnext.payments = erpnext.stock.StockController.extend({
-	make_payment: function() {
+erpnext.payments = class payments extends erpnext.stock.StockController {
+	make_payment() {
 		var me = this;
 
 		this.dialog = new frappe.ui.Dialog({
 			title: 'Payment'
 		});
-	
+
 		this.dialog.show();
 		this.$body = this.dialog.body;
 		this.set_payment_primary_action();
 		this.make_keyboard();
 		this.select_text()
-	},
+	}
 
-	select_text: function(){
+	select_text(){
 		var me = this;
 		$(this.$body).find('.form-control').click(function(){
 			$(this).select();
 		})
-	},
+	}
 
-	set_payment_primary_action: function(){
+	set_payment_primary_action(){
 		var me = this;
-	
+
 		this.dialog.set_primary_action(__("Submit"), function() {
 			// Allow no ZERO payment
 			$.each(me.frm.doc.payments, function (index, data) {
@@ -36,18 +36,18 @@
 				}
 			});
 		})
-	},
+	}
 
-	make_keyboard: function(){
+	make_keyboard(){
 		var me = this;
 		$(this.$body).empty();
 		$(this.$body).html(frappe.render_template('pos_payment', this.frm.doc))
 		this.show_payment_details();
 		this.bind_keyboard_event()
 		this.clear_amount()
-	},
+	}
 
-	make_multimode_payment: function(){
+	make_multimode_payment(){
 		var me = this;
 
 		if(this.frm.doc.change_amount > 0){
@@ -57,9 +57,9 @@
 		this.payments = frappe.model.add_child(this.frm.doc, 'Multi Mode Payment', "payments");
 		this.payments.mode_of_payment = this.dialog.fields_dict.mode_of_payment.get_value();
 		this.payments.amount = flt(this.payment_val);
-	},
+	}
 
-	show_payment_details: function(){
+	show_payment_details(){
 		var me = this;
 		var multimode_payments = $(this.$body).find('.multimode-payments').empty();
 		if(this.frm.doc.payments.length){
@@ -82,9 +82,9 @@
 		}else{
 			$("<p>No payment mode selected in pos profile</p>").appendTo(multimode_payments)
 		}
-	},
+	}
 
-	set_outstanding_amount: function(){
+	set_outstanding_amount(){
 		this.selected_mode = $(this.$body).find(repl("input[idx='%(idx)s']",{'idx': this.idx}));
 		this.highlight_selected_row()
 		this.payment_val = 0.0
@@ -99,47 +99,47 @@
 		}
 		this.selected_mode.select()
 		this.bind_amount_change_event();
-	},
-	
-	bind_keyboard_event: function(){
+	}
+
+	bind_keyboard_event(){
 		var me = this;
 		this.payment_val = '';
 		this.bind_form_control_event();
 		this.bind_numeric_keys_event();
-	},
+	}
 
-	bind_form_control_event: function(){
+	bind_form_control_event(){
 		var me = this;
 		$(this.$body).find('.pos-payment-row').click(function(){
 			me.idx = $(this).attr("idx");
 			me.set_outstanding_amount()
 		})
-		
+
 		$(this.$body).find('.form-control').click(function(){
 			me.idx = $(this).attr("idx");
 			me.set_outstanding_amount();
 			me.update_paid_amount(true);
 		})
-		
+
 		$(this.$body).find('.write_off_amount').change(function(){
 			me.write_off_amount(flt($(this).val()), precision("write_off_amount"));
 		})
-		
+
 		$(this.$body).find('.change_amount').change(function(){
 			me.change_amount(flt($(this).val()), precision("change_amount"));
 		})
-	},
+	}
 
-	highlight_selected_row: function(){
+	highlight_selected_row(){
 		var me = this;
 		var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']",{'idx': this.idx}));
 		$(this.$body).find('.pos-payment-row').removeClass('selected-payment-mode')
 		selected_row.addClass('selected-payment-mode')
 		$(this.$body).find('.amount').attr('disabled', true);
 		this.selected_mode.attr('disabled', false);
-	},
-	
-	bind_numeric_keys_event: function(){
+	}
+
+	bind_numeric_keys_event(){
 		var me = this;
 		$(this.$body).find('.pos-keyboard-key').click(function(){
 			me.payment_val += $(this).text();
@@ -147,7 +147,7 @@
 			me.idx = me.selected_mode.attr("idx")
 			me.update_paid_amount()
 		})
-		
+
 		$(this.$body).find('.delete-btn').click(function(){
 			me.payment_val =  cstr(flt(me.selected_mode.val())).slice(0, -1);
 			me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency));
@@ -155,9 +155,9 @@
 			me.update_paid_amount();
 		})
 
-	},
-	
-	bind_amount_change_event: function(){
+	}
+
+	bind_amount_change_event(){
 		var me = this;
 		this.selected_mode.change(function(){
 			me.payment_val =  flt($(this).val()) || 0.0;
@@ -165,9 +165,9 @@
 			me.idx = me.selected_mode.attr("idx")
 			me.update_payment_amount()
 		})
-	},
+	}
 
-	clear_amount: function() {
+	clear_amount() {
 		var me = this;
 		$(this.$body).find('.clr').click(function(e){
 			e.stopPropagation();
@@ -178,9 +178,9 @@
 			me.highlight_selected_row();
 			me.update_payment_amount();
 		})
-	},
+	}
 
-	write_off_amount: function(write_off_amount) {
+	write_off_amount(write_off_amount) {
 		var me = this;
 
 		this.frm.doc.write_off_amount = flt(write_off_amount, precision("write_off_amount"));
@@ -188,17 +188,17 @@
 			precision("base_write_off_amount"));
 		this.calculate_outstanding_amount(false)
 		this.show_amounts()
-	},
+	}
 
-	change_amount: function(change_amount) {
+	change_amount(change_amount) {
 		var me = this;
 
 		this.frm.doc.change_amount = flt(change_amount, precision("change_amount"));
 		this.calculate_write_off_amount()
 		this.show_amounts()
-	},
+	}
 
-	update_paid_amount: function(update_write_off) {
+	update_paid_amount(update_write_off) {
 		var me = this;
 		if(in_list(['change_amount', 'write_off_amount'], this.idx)){
 			var value = me.selected_mode.val();
@@ -213,9 +213,9 @@
 		}else{
 			this.update_payment_amount()
 		}
-	},
+	}
 
-	update_payment_amount: function(){
+	update_payment_amount(){
 		var me = this;
 
 		$.each(this.frm.doc.payments, function(index, data){
@@ -226,9 +226,9 @@
 
 		this.calculate_outstanding_amount(false);
 		this.show_amounts();
-	},
+	}
 
-	show_amounts: function(){
+	show_amounts(){
 		var me = this;
 		$(this.$body).find(".write_off_amount").val(format_currency(this.frm.doc.write_off_amount, this.frm.doc.currency));
 		$(this.$body).find('.paid_amount').text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency));
@@ -236,4 +236,4 @@
 		$(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, frappe.get_doc(":Company", this.frm.doc.company).default_currency))
 		this.update_invoice();
 	}
-})
\ No newline at end of file
+}
diff --git a/erpnext/public/js/point-of-sale.bundle.js b/erpnext/public/js/point-of-sale.bundle.js
new file mode 100644
index 0000000..fbc4bbb
--- /dev/null
+++ b/erpnext/public/js/point-of-sale.bundle.js
@@ -0,0 +1,8 @@
+import "../../selling/page/point_of_sale/pos_item_selector.js";
+import "../../selling/page/point_of_sale/pos_item_cart.js";
+import "../../selling/page/point_of_sale/pos_item_details.js";
+import "../../selling/page/point_of_sale/pos_number_pad.js";
+import "../../selling/page/point_of_sale/pos_payment.js";
+import "../../selling/page/point_of_sale/pos_past_order_list.js";
+import "../../selling/page/point_of_sale/pos_past_order_summary.js";
+import "../../selling/page/point_of_sale/pos_controller.js";
diff --git a/erpnext/public/js/stock_analytics.js b/erpnext/public/js/stock_analytics.js
index 140c9dc..dfe2c88 100644
--- a/erpnext/public/js/stock_analytics.js
+++ b/erpnext/public/js/stock_analytics.js
@@ -2,8 +2,8 @@
 // License: GNU General Public License v3. See license.txt
 
 
-erpnext.StockAnalytics = erpnext.StockGridReport.extend({
-	init: function(wrapper, opts) {
+erpnext.StockAnalytics = class StockAnalytics extends erpnext.StockGridReport {
+	constructor(wrapper, opts) {
 		var args = {
 			title: __("Stock Analytics"),
 			parent: $(wrapper).find('.layout-main'),
@@ -30,9 +30,33 @@
 
 		if(opts) $.extend(args, opts);
 
-		this._super(args);
-	},
-	setup_columns: function() {
+		super(args);
+
+		this.filters = [
+			{fieldtype:"Select", label: __("Value or Qty"), fieldname: "value_or_qty",
+				options:[{label:__("Value"), value:"Value"}, {label:__("Quantity"), value:"Quantity"}],
+				filter: function(val, item, opts, me) {
+					return me.apply_zero_filter(val, item, opts, me);
+				}},
+			{fieldtype:"Select", label: __("Brand"), link:"Brand", fieldname: "brand",
+				default_value: __("Select Brand..."), filter: function(val, item, opts) {
+					return val == opts.default_value || item.brand == val || item._show;
+				}, link_formatter: {filter_input: "brand"}},
+			{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse", fieldname: "warehouse",
+				default_value: __("Select Warehouse...")},
+			{fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
+			{fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
+			{fieldtype:"Select", label: __("Range"), fieldname: "range",
+				options:[
+					{label:__("Daily"), value:"Daily"},
+					{label:__("Weekly"), value:"Weekly"},
+					{label:__("Monthly"), value:"Monthly"},
+					{label:__("Quarterly"), value:"Quarterly"},
+					{label:__("Yearly"), value:"Yearly"},
+				]}
+		];
+	}
+	setup_columns() {
 		var std_columns = [
 			{id: "name", name: __("Item"), field: "name", width: 300},
 			{id: "brand", name: __("Brand"), field: "brand", width: 100},
@@ -43,43 +67,21 @@
 
 		this.make_date_range_columns();
 		this.columns = std_columns.concat(this.columns);
-	},
-	filters: [
-		{fieldtype:"Select", label: __("Value or Qty"), fieldname: "value_or_qty",
-			options:[{label:__("Value"), value:"Value"}, {label:__("Quantity"), value:"Quantity"}],
-			filter: function(val, item, opts, me) {
-				return me.apply_zero_filter(val, item, opts, me);
-			}},
-		{fieldtype:"Select", label: __("Brand"), link:"Brand", fieldname: "brand",
-			default_value: __("Select Brand..."), filter: function(val, item, opts) {
-				return val == opts.default_value || item.brand == val || item._show;
-			}, link_formatter: {filter_input: "brand"}},
-		{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse", fieldname: "warehouse",
-			default_value: __("Select Warehouse...")},
-		{fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
-		{fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
-		{fieldtype:"Select", label: __("Range"), fieldname: "range",
-			options:[
-				{label:__("Daily"), value:"Daily"},
-				{label:__("Weekly"), value:"Weekly"},
-				{label:__("Monthly"), value:"Monthly"},
-				{label:__("Quarterly"), value:"Quarterly"},
-				{label:__("Yearly"), value:"Yearly"},
-			]}
-	],
-	setup_filters: function() {
+	}
+
+	setup_filters() {
 		var me = this;
-		this._super();
+		super.setup_filters();
 
 		this.trigger_refresh_on_change(["value_or_qty", "brand", "warehouse", "range"]);
 
 		this.show_zero_check();
-	},
-	init_filter_values: function() {
-		this._super();
+	}
+	init_filter_values() {
+		super.init_filter_values();
 		this.filter_inputs.range && this.filter_inputs.range.val('Monthly');
-	},
-	prepare_data: function() {
+	}
+	prepare_data() {
 		var me = this;
 
 		if(!this.data) {
@@ -112,8 +114,8 @@
 		this.prepare_balances();
 		this.update_groups();
 
-	},
-	prepare_balances: function() {
+	}
+	prepare_balances() {
 		var me = this;
 		var from_date = frappe.datetime.str_to_obj(this.from_date);
 		var to_date = frappe.datetime.str_to_obj(this.to_date);
@@ -164,8 +166,8 @@
 				item.closing_qty_value += diff;
 			}
 		}
-	},
-	update_groups: function() {
+	}
+	update_groups() {
 		var me = this;
 		$.each(this.data, function(i, item) {
 			// update groups
@@ -192,8 +194,8 @@
 				}
 			}
 		});
-	},
-	show_stock_ledger: function(item_code) {
+	}
+	show_stock_ledger(item_code) {
 		frappe.route_options = {
 			item_code: item_code,
 			from_date: this.from_date,
@@ -201,5 +203,5 @@
 		};
 		frappe.set_route("query-report", "Stock Ledger");
 	}
-});
+};
 
diff --git a/erpnext/public/js/stock_grid_report.js b/erpnext/public/js/stock_grid_report.js
index 832fd3e..752fafd 100644
--- a/erpnext/public/js/stock_grid_report.js
+++ b/erpnext/public/js/stock_grid_report.js
@@ -1,16 +1,16 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
-	get_item_warehouse: function(warehouse, item) {
+erpnext.StockGridReport = class StockGridReport extends frappe.views.TreeGridReport {
+	get_item_warehouse(warehouse, item) {
 		if(!this.item_warehouse[item]) this.item_warehouse[item] = {};
 		if(!this.item_warehouse[item][warehouse]) this.item_warehouse[item][warehouse] = {
 			balance_qty: 0.0, balance_value: 0.0, fifo_stack: []
 		};
 		return this.item_warehouse[item][warehouse];
-	},
+	}
 
-	get_value_diff: function(wh, sl, is_fifo) {
+	get_value_diff(wh, sl, is_fifo) {
 		// value
 		if(sl.qty > 0) {
 			// incoming - rate is given
@@ -59,8 +59,8 @@
 		wh.balance_qty += sl.qty;
 		wh.balance_value += value_diff;
 		return value_diff;
-	},
-	get_fifo_value_diff: function(wh, sl) {
+	}
+	get_fifo_value_diff(wh, sl) {
 		// get exact rate from fifo stack
 		var fifo_stack = (wh.fifo_stack || []).reverse();
 		var fifo_value_diff = 0.0;
@@ -89,9 +89,9 @@
 		// reset the updated stack
 		wh.fifo_stack = fifo_stack.reverse();
 		return -fifo_value_diff;
-	},
+	}
 
-	get_serialized_value_diff: function(sl) {
+	get_serialized_value_diff(sl) {
 		var me = this;
 
 		var value_diff = 0.0;
@@ -103,9 +103,9 @@
 		});
 
 		return value_diff;
-	},
+	}
 
-	get_serialized_buying_rates: function() {
+	get_serialized_buying_rates() {
 		var serialized_buying_rates = {};
 
 		if (frappe.report_dump.data["Serial No"]) {
@@ -115,5 +115,5 @@
 		}
 
 		return serialized_buying_rates;
-	},
-});
+	}
+};
diff --git a/erpnext/public/js/telephony.js b/erpnext/public/js/telephony.js
index 9548d6c..1c3e314 100644
--- a/erpnext/public/js/telephony.js
+++ b/erpnext/public/js/telephony.js
@@ -1,19 +1,19 @@
-frappe.ui.form.ControlData = frappe.ui.form.ControlData.extend( {
+frappe.ui.form.ControlData = class ControlData extends frappe.ui.form.ControlData  {
 	make_input() {
-		this._super();
+		super.make_input();
 		if (this.df.options == 'Phone') {
 			this.setup_phone();
 		}
 		if (this.frm && this.frm.fields_dict) {
 			Object.values(this.frm.fields_dict).forEach(function(field) {
-				if (field.df.read_only === 1 && field.df.options === 'Phone' 
+				if (field.df.read_only === 1 && field.df.options === 'Phone'
 					&& field.disp_area.style[0] != 'display' && !field.has_icon) {
 					field.setup_phone();
 					field.has_icon = true;
 				}
 			});
 		}
-	},
+	}
 	setup_phone() {
 		if (frappe.phone_call.handler) {
 			let control = this.df.read_only ? '.control-value' : '.control-input';
@@ -30,4 +30,4 @@
 				});
 		}
 	}
-});
+};
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 19c9073..ce40ced 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -48,31 +48,24 @@
 		return cint(frappe.boot.sysdefaults.allow_stale);
 	},
 
-	setup_serial_no: function() {
-		var grid_row = cur_frm.open_grid_row();
-		if(!grid_row || !grid_row.grid_form.fields_dict.serial_no ||
-			grid_row.grid_form.fields_dict.serial_no.get_status()!=="Write") return;
+	setup_serial_or_batch_no: function() {
+		let grid_row = cur_frm.open_grid_row();
+		if (!grid_row || !grid_row.grid_form.fields_dict.serial_no ||
+			grid_row.grid_form.fields_dict.serial_no.get_status() !== "Write") return;
 
-		var $btn = $('<button class="btn btn-sm btn-default">'+__("Add Serial No")+'</button>')
-			.appendTo($("<div>")
-				.css({"margin-bottom": "10px", "margin-top": "10px"})
-				.appendTo(grid_row.grid_form.fields_dict.serial_no.$wrapper));
+		frappe.model.get_value('Item', {'name': grid_row.doc.item_code},
+			['has_serial_no', 'has_batch_no'], ({has_serial_no, has_batch_no}) => {
+				Object.assign(grid_row.doc, {has_serial_no, has_batch_no});
 
-		var me = this;
-		$btn.on("click", function() {
-			let callback = '';
-			let on_close = '';
-
-			frappe.model.get_value('Item', {'name':grid_row.doc.item_code}, 'has_serial_no',
-				(data) => {
-					if(data) {
-						grid_row.doc.has_serial_no = data.has_serial_no;
-						me.show_serial_batch_selector(grid_row.frm, grid_row.doc,
-							callback, on_close, true);
-					}
+				if (has_serial_no) {
+					attach_selector_button(__("Add Serial No"),
+						grid_row.grid_form.fields_dict.serial_no.$wrapper, this, grid_row);
+				} else if (has_batch_no) {
+					attach_selector_button(__("Pick Batch No"),
+						grid_row.grid_form.fields_dict.batch_no.$wrapper, this, grid_row);
 				}
-			);
-		});
+			}
+		);
 	},
 
 	route_to_adjustment_jv: (args) => {
@@ -731,6 +724,18 @@
 	}
 }
 
+frappe.form.link_formatters['Project'] = function(value, doc) {
+	if (doc && value && doc.project_name && doc.project_name !== value && doc.project === value) {
+		return value + ': ' + doc.project_name;
+	} else if (!value && doc.doctype && doc.project_name) {
+		// format blank value in child table
+		return doc.project;
+	} else {
+		// if value is blank in report view or project name and name are the same, return as is
+		return value;
+	}
+};
+
 // add description on posting time
 $(document).on('app_ready', function() {
 	if(!frappe.datetime.is_timezone_same()) {
@@ -743,3 +748,14 @@
 		});
 	}
 });
+
+function attach_selector_button(inner_text, append_loction, context, grid_row) {
+	let $btn_div = $("<div>").css({"margin-bottom": "10px", "margin-top": "10px"})
+		.appendTo(append_loction);
+	let $btn = $(`<button class="btn btn-sm btn-default">${inner_text}</button>`)
+		.appendTo($btn_div);
+
+	$btn.on("click", function() {
+		context.show_serial_batch_selector(grid_row.frm, grid_row.doc, "", "", true);
+	});
+}
diff --git a/erpnext/public/js/utils/customer_quick_entry.js b/erpnext/public/js/utils/customer_quick_entry.js
index ebe6cd9..efb8dd9 100644
--- a/erpnext/public/js/utils/customer_quick_entry.js
+++ b/erpnext/public/js/utils/customer_quick_entry.js
@@ -1,17 +1,17 @@
 frappe.provide('frappe.ui.form');
 
-frappe.ui.form.CustomerQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({
-	init: function(doctype, after_insert) {
+frappe.ui.form.CustomerQuickEntryForm = class CustomerQuickEntryForm extends frappe.ui.form.QuickEntryForm {
+	constructor(doctype, after_insert) {
+		super(doctype, after_insert);
 		this.skip_redirect_on_error = true;
-		this._super(doctype, after_insert);
-	},
+	}
 
-	render_dialog: function() {
+	render_dialog() {
 		this.mandatory = this.mandatory.concat(this.get_variant_fields());
-		this._super();
-	},
+		super.render_dialog();
+	}
 
-	get_variant_fields: function() {
+	get_variant_fields() {
 		var variant_fields = [{
 			fieldtype: "Section Break",
 			label: __("Primary Contact Details"),
@@ -77,5 +77,5 @@
 		}];
 
 		return variant_fields;
-	},
-})
\ No newline at end of file
+	}
+}
diff --git a/erpnext/public/js/utils/item_quick_entry.js b/erpnext/public/js/utils/item_quick_entry.js
index 27ef107..7e0198d 100644
--- a/erpnext/public/js/utils/item_quick_entry.js
+++ b/erpnext/public/js/utils/item_quick_entry.js
@@ -1,27 +1,27 @@
 frappe.provide('frappe.ui.form');
 
-frappe.ui.form.ItemQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({
-	init: function(doctype, after_insert) {
-		this._super(doctype, after_insert);
-	},
+frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.form.QuickEntryForm {
+	constructor(doctype, after_insert) {
+		super(doctype, after_insert);
+	}
 
-	render_dialog: function() {
+	render_dialog() {
 		this.mandatory = this.get_variant_fields().concat(this.mandatory);
 		this.mandatory = this.mandatory.concat(this.get_attributes_fields());
 		this.check_naming_series_based_on();
-		this._super();
+		super.render_dialog();
 		this.init_post_render_dialog_operations();
 		this.preset_fields_for_template();
 		this.dialog.$wrapper.find('.edit-full').text(__('Edit in full page for more options like assets, serial nos, batches etc.'))
-	},
+	}
 
-	check_naming_series_based_on: function() {
+	check_naming_series_based_on() {
 		if (frappe.defaults.get_default("item_naming_by") === "Naming Series") {
 			this.mandatory = this.mandatory.filter(d => d.fieldname !== "item_code");
 		}
-	},
+	}
 
-	init_post_render_dialog_operations: function() {
+	init_post_render_dialog_operations() {
 		this.dialog.fields_dict.attribute_html.$wrapper.append(frappe.render_template("item_quick_entry"));
 		this.init_for_create_variant_trigger();
 		this.init_for_item_template_trigger();
@@ -29,9 +29,9 @@
 		this.toggle_manufacturer_fields();
 		this.dialog.get_field("item_template").df.hidden = 1;
 		this.dialog.get_field("item_template").refresh();
-	},
+	}
 
-	register_primary_action: function() {
+	register_primary_action() {
 		var me = this;
 		this.dialog.set_primary_action(__('Save'), function() {
 			if (me.dialog.working) return;
@@ -59,9 +59,9 @@
 				me.insert(variant_values);
 			}
 		});
-	},
+	}
 
-	insert: function(variant_values) {
+	insert(variant_values) {
 		let me = this;
 		return new Promise(resolve => {
 			frappe.call({
@@ -94,9 +94,9 @@
 				freeze: true
 			});
 		});
-	},
+	}
 
-	open_doc: function() {
+	open_doc() {
 		this.dialog.hide();
 		this.update_doc();
 		if (this.dialog.fields_dict.create_variant.$input.prop("checked")) {
@@ -106,9 +106,9 @@
 		} else {
 			frappe.set_route('Form', this.doctype, this.doc.name);
 		}
-	},
+	}
 
-	get_variant_fields: function() {
+	get_variant_fields() {
 		var variant_fields = [{
 			fieldname: "create_variant",
 			fieldtype: "Check",
@@ -130,9 +130,9 @@
 		}];
 
 		return variant_fields;
-	},
+	}
 
-	get_manufacturing_fields: function() {
+	get_manufacturing_fields() {
 		this.manufacturer_fields = [{
 			fieldtype: 'Link',
 			options: 'Manufacturer',
@@ -148,9 +148,9 @@
 			reqd: 0
 		}];
 		return this.manufacturer_fields;
-	},
+	}
 
-	get_attributes_fields: function() {
+	get_attributes_fields() {
 		var attribute_fields = [{
 			fieldname: 'attribute_html',
 			fieldtype: 'HTML'
@@ -158,18 +158,18 @@
 
 		attribute_fields = attribute_fields.concat(this.get_manufacturing_fields());
 		return attribute_fields;
-	},
+	}
 
-	init_for_create_variant_trigger: function() {
+	init_for_create_variant_trigger() {
 		var me = this;
 
 		this.dialog.fields_dict.create_variant.$input.on("click", function() {
 			me.preset_fields_for_template();
 			me.init_post_template_trigger_operations(false, [], true);
 		});
-	},
+	}
 
-	preset_fields_for_template: function() {
+	preset_fields_for_template() {
 		var for_variant = this.dialog.get_value('create_variant');
 
 		// setup template field, seen and mandatory if variant
@@ -195,9 +195,9 @@
 			f.refresh();
 		});
 
-	},
+	}
 
-	init_for_item_template_trigger: function() {
+	init_for_item_template_trigger() {
 		var me = this;
 
 		me.dialog.fields_dict["item_template"].df.onchange = () => {
@@ -228,9 +228,9 @@
 				me.init_post_template_trigger_operations(false, [], true);
 			}
 		}
-	},
+	}
 
-	init_post_template_trigger_operations: function(is_manufacturer, attributes, attributes_flag) {
+	init_post_template_trigger_operations(is_manufacturer, attributes, attributes_flag) {
 		this.attributes = attributes;
 		this.attribute_values = {};
 		this.attributes_count = attributes.length;
@@ -240,23 +240,23 @@
 		this.toggle_manufacturer_fields();
 		this.dialog.fields_dict.attribute_html.$wrapper.find(".attributes").toggleClass("hide-control", attributes_flag);
 		this.dialog.fields_dict.attribute_html.$wrapper.find(".attributes-header").toggleClass("hide-control", attributes_flag);
-	},
+	}
 
-	toggle_manufacturer_fields: function() {
+	toggle_manufacturer_fields() {
 		var me = this;
 		$.each(this.manufacturer_fields, function(i, dialog_field) {
 			me.dialog.get_field(dialog_field.fieldname).df.hidden = !me.is_manufacturer;
 			me.dialog.get_field(dialog_field.fieldname).df.reqd = dialog_field.fieldname == 'manufacturer' ? me.is_manufacturer : false;
 			me.dialog.get_field(dialog_field.fieldname).refresh();
 		});
-	},
+	}
 
-	initiate_render_attributes: function() {
+	initiate_render_attributes() {
 		this.dialog.fields_dict.attribute_html.$wrapper.find(".attributes").empty();
 		this.render_attributes(this.attributes);
-	},
+	}
 
-	render_attributes: function(attributes) {
+	render_attributes(attributes) {
 		var me = this;
 
 		this.dialog.get_field('attribute_html').toggle(true);
@@ -291,9 +291,9 @@
 				});
 			}
 		});
-	},
+	}
 
-	init_make_control: function(fieldtype, row) {
+	init_make_control(fieldtype, row) {
 		this[row.attribute] = frappe.ui.form.make_control({
 			df: {
 				"fieldtype": fieldtype,
@@ -305,9 +305,9 @@
 			only_input: false
 		});
 		this[row.attribute].make_input();
-	},
+	}
 
-	init_awesomplete_for_attribute: function(row) {
+	init_awesomplete_for_attribute(row) {
 		var me = this;
 
 		this[row.attribute].input.awesomplete = new Awesomplete(this[row.attribute].input, {
@@ -343,9 +343,9 @@
 			me.attribute_values[$(e.target).attr("data-fieldname")] = e.target.value;
 			$(e.target).closest(".frappe-control").toggleClass("has-error", e.target.value ? false : true);
 		});
-	},
+	}
 
-	get_variant_doc: function() {
+	get_variant_doc() {
 		var me = this;
 		var variant_doc = {};
 		var attribute = this.validate_mandatory_attributes();
@@ -381,9 +381,9 @@
 			})
 		}
 		return variant_doc;
-	},
+	}
 
-	validate_mandatory_attributes: function() {
+	validate_mandatory_attributes() {
 		var me = this;
 		var attribute = {};
 		var mandatory = [];
@@ -404,4 +404,4 @@
 		}
 		return attribute;
 	}
-});
\ No newline at end of file
+};
diff --git a/erpnext/public/js/utils/item_selector.js b/erpnext/public/js/utils/item_selector.js
index d04c488..9fc2640 100644
--- a/erpnext/public/js/utils/item_selector.js
+++ b/erpnext/public/js/utils/item_selector.js
@@ -1,5 +1,5 @@
-erpnext.ItemSelector = Class.extend({
-	init: function(opts) {
+erpnext.ItemSelector = class ItemSelector {
+	constructor(opts) {
 		$.extend(this, opts);
 
 		if (!this.item_field) {
@@ -12,9 +12,9 @@
 
 		this.grid = this.frm.get_field("items").grid;
 		this.setup();
-	},
+	}
 
-	setup: function() {
+	setup() {
 		var me = this;
 		if(!this.grid.add_items_button) {
 			this.grid.add_items_button = this.grid.add_custom_button(__('Add Items'), function() {
@@ -26,9 +26,9 @@
 				setTimeout(function() { me.dialog.input.focus(); }, 1000);
 			});
 		}
-	},
+	}
 
-	make_dialog: function() {
+	make_dialog() {
 		this.dialog = new frappe.ui.Dialog({
 			title: __('Add Items')
 		});
@@ -53,9 +53,9 @@
 				me.timeout_id = undefined;
 			}, 500);
 		});
-	},
+	}
 
-	add_item: function(item_code) {
+	add_item(item_code) {
 		// add row or update qty
 		var added = false;
 
@@ -82,9 +82,9 @@
 			]);
 		}
 
-	},
+	}
 
-	render_items: function() {
+	render_items() {
 		let args = {
 			query: this.item_query,
 			filters: {}
@@ -107,4 +107,4 @@
 			me.dialog.results.html(frappe.render_template('item_selector', {'data':r.values}));
 		});
 	}
-});
\ 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 3333d56..79b6220 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -1,6 +1,6 @@
 
-erpnext.SerialNoBatchSelector = Class.extend({
-	init: function(opts, show_dialog) {
+erpnext.SerialNoBatchSelector = class SerialNoBatchSelector {
+	constructor(opts, show_dialog) {
 		$.extend(this, opts);
 		this.show_dialog = show_dialog;
 		// frm, item, warehouse_details, has_batch, oldest
@@ -12,16 +12,16 @@
 		if(d && d.has_serial_no && !(this.show_dialog == false)) this.has_serial_no = 1;
 
 		this.setup();
-	},
+	}
 
-	setup: function() {
+	setup() {
 		this.item_code = this.item.item_code;
 		this.qty = this.item.qty;
 		this.make_dialog();
 		this.on_close_dialog();
-	},
+	}
 
-	make_dialog: function() {
+	make_dialog() {
 		var me = this;
 
 		this.data = this.oldest ? this.oldest : [];
@@ -176,15 +176,15 @@
 		}
 
 		this.dialog.show();
-	},
+	}
 
-	on_close_dialog: function() {
+	on_close_dialog() {
 		this.dialog.get_close_btn().on('click', () => {
 			this.on_close && this.on_close(this.item);
 		});
-	},
+	}
 
-	validate: function() {
+	validate() {
 		let values = this.values;
 		if(!values.warehouse) {
 			frappe.throw(__("Please select a warehouse"));
@@ -210,7 +210,7 @@
 			}
 			return true;
 		}
-	},
+	}
 
 	update_batch_items() {
 		// clones an items if muliple batches are selected.
@@ -233,14 +233,14 @@
 					'selected_qty', this.values.warehouse);
 			});
 		}
-	},
+	}
 
 	update_serial_no_item() {
 		// just updates serial no for the item
 		if(this.has_serial_no && !this.has_batch) {
 			this.map_row_values(this.item, this.values, 'serial_no', 'qty');
 		}
-	},
+	}
 
 	update_batch_serial_no_items() {
 		// if serial no selected is from different batches, adds new rows for each batch.
@@ -281,14 +281,14 @@
 				});
 			})
 		}
-	},
+	}
 
-	batch_exists: function(batch) {
+	batch_exists(batch) {
 		const batches = this.frm.doc.items.map(data => data.batch_no);
 		return (batches && in_list(batches, batch)) ? true : false;
-	},
+	}
 
-	map_row_values: function(row, values, number, qty_field, warehouse) {
+	map_row_values(row, values, number, qty_field, warehouse) {
 		row.qty = values[qty_field];
 		row.transfer_qty = flt(values[qty_field]) * flt(row.conversion_factor);
 		row[number] = values[number];
@@ -301,9 +301,9 @@
 		}
 
 		this.frm.dirty();
-	},
+	}
 
-	update_total_qty: function() {
+	update_total_qty() {
 		let qty_field = this.dialog.fields_dict.qty;
 		let total_qty = 0;
 
@@ -312,9 +312,9 @@
 		});
 
 		qty_field.set_input(total_qty);
-	},
+	}
 
-	get_batch_fields: function() {
+	get_batch_fields() {
 		var me = this;
 
 		return [
@@ -425,9 +425,9 @@
 				},
 			}
 		];
-	},
+	}
 
-	get_serial_no_fields: function() {
+	get_serial_no_fields() {
 		var me = this;
 		this.serial_list = [];
 
@@ -510,4 +510,4 @@
 			}
 		];
 	}
-});
+};
diff --git a/erpnext/public/less/email.less b/erpnext/public/less/email.less
deleted file mode 100644
index 4077c49..0000000
--- a/erpnext/public/less/email.less
+++ /dev/null
@@ -1,32 +0,0 @@
-@import "../../../../frappe/frappe/public/less/variables.less";
-
-.panel-header {
-	background-color: @light-bg;
-	border: 1px solid @border-color;
-	border-radius: 3px 3px 0 0;
-}
-
-.panel-body {
-	background-color: #fff;
-	border: 1px solid @border-color;
-	border-top: none;
-	border-radius: 0 0 3px 3px;
-	overflow-wrap: break-word;
-}
-
-.sender-avatar {
-	width: 24px;
-	height: 24px;
-	border-radius: 3px;
-	vertical-align: middle;
-}
-
-.sender-avatar-placeholder {
-	.sender-avatar;
-
-	line-height: 24px;
-	text-align: center;
-	color: @border-color;
-	border: 1px solid @border-color;
-	background-color: #fff;
-}
\ No newline at end of file
diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less
deleted file mode 100644
index 29deada..0000000
--- a/erpnext/public/less/hub.less
+++ /dev/null
@@ -1,375 +0,0 @@
-@import "variables.less";
-@import (reference) "desk.less";
-
-body[data-route*="marketplace"] {
-	.layout-side-section {
-		padding-top: 25px;
-		padding-left: 5px;
-		padding-right: 25px;
-	}
-
-	[data-route], [data-action] {
-		cursor: pointer;
-	}
-
-	.layout-main-section {
-		border: none;
-		font-size: @text-medium;
-		padding-top: 25px;
-
-		@media (max-width: @screen-xs) {
-			padding-left: 20px;
-			padding-right: 20px;
-		}
-	}
-
-	input, textarea {
-		font-size: @text-medium;
-	}
-
-	.hub-image {
-		height: 200px;
-	}
-
-	.hub-image-loading, .hub-image-broken {
-		content: " ";
-		position: absolute;
-		left: 0;
-		height: 100%;
-		width: 100%;
-		background-color: var(--bg-light-gray);
-		display: flex;
-		align-items: center;
-		justify-content: center;
-
-		span {
-			font-size: 32px;
-			color: @text-extra-muted;
-		}
-	}
-
-	.progress-bar {
-		background-color: #89da28;
-	}
-
-	.subpage-title.flex {
-		align-items: flex-start;
-		justify-content: space-between;
-	}
-
-	.hub-card {
-		margin-bottom: 25px;
-		position: relative;
-		border: 1px solid @border-color;
-		border-radius: 4px;
-		overflow: hidden;
-
-		&:hover .hub-card-overlay {
-			display: block;
-		}
-	}
-
-	.hub-card.is-local {
-		&.active {
-			.hub-card-header {
-				background-color: #f4ffe5;
-			}
-
-			.octicon-check {
-				display: inline;
-			}
-		}
-
-		.octicon-check {
-			display: none;
-			position: absolute;
-			font-size: 20px;
-			right: 15px;
-			top: 50%;
-			transform: translateY(-50%);
-		}
-	}
-
-	.hub-card-header {
-		position: relative;
-		padding: 12px 15px;
-		height: 60px;
-		border-bottom: 1px solid @border-color;
-	}
-
-	.hub-card-body {
-		position: relative;
-		height: 200px;
-	}
-
-	.hub-card-overlay {
-		display: none;
-		position: absolute;
-		top: 0;
-		width: 100%;
-		height: 100%;
-		background-color: rgba(0, 0, 0, 0.05);
-	}
-
-	.hub-card-overlay-body {
-		position: relative;
-		height: 100%;
-	}
-
-	.hub-card-overlay-button {
-		position: absolute;
-		right: 15px;
-		bottom: 15px;
-	}
-
-	.hub-card-image {
-		position: relative;
-		width: 100%;
-		height: 100%;
-		object-fit: contain;
-	}
-
-	.hub-search-container {
-		margin-bottom: 20px;
-
-		input {
-			height: 32px;
-		}
-	}
-
-	.hub-sidebar {
-		padding-top: 25px;
-		padding-right: 15px;
-	}
-
-	.hub-sidebar-group {
-		margin-bottom: 10px;
-	}
-
-	.hub-sidebar-item {
-		padding: 5px 8px;
-		margin-bottom: 3px;
-		border-radius: 4px;
-		border: 1px solid transparent;
-
-		&.active, &:hover:not(.is-title) {
-			border-color: @border-color;
-		}
-	}
-
-	.hub-item-image {
-		position: relative;
-		border: 1px solid @border-color;
-		border-radius: 4px;
-		overflow: hidden;
-		height: 200px;
-		width: 200px;
-		display: flex;
-		align-items: center;
-	}
-
-	.hub-item-skeleton-image {
-		border-radius: 4px;
-		background-color: @light-bg;
-		overflow: hidden;
-		height: 200px;
-		width: 200px;
-	}
-
-	.hub-skeleton {
-		background-color: @light-bg;
-		color: @light-bg;
-		max-width: 500px;
-	}
-
-	.hub-item-seller img {
-		width: 50px;
-		height: 50px;
-		border-radius: 4px;
-		border: 1px solid @border-color;
-	}
-
-	.register-title {
-		font-size: @text-regular;
-	}
-
-	.register-form {
-		border: 1px solid @border-color;
-		border-radius: 4px;
-		padding: 15px 25px;
-	}
-
-	.publish-area.filled {
-		.empty-items-container {
-			display: none;
-		}
-	}
-
-	.publish-area.empty {
-		.hub-items-container {
-			display: none;
-		}
-	}
-
-	.publish-area-head {
-		display: flex;
-		justify-content: space-between;
-		margin-bottom: 20px;
-	}
-
-	.hub-list-item {
-		display: flex;
-		justify-content: space-between;
-		align-items: center;
-		border: 1px solid @border-color;
-		margin-bottom: -1px;
-		overflow: hidden;
-	}
-
-	.hub-list-item:first-child {
-		border-top-left-radius: 4px;
-		border-top-right-radius: 4px;
-	}
-	.hub-list-item:last-child {
-		border-bottom-left-radius: 4px;
-		border-bottom-right-radius: 4px;
-	}
-
-	.hub-list-left {
-		display: flex;
-		align-items: center;
-		max-width: 90%;
-	}
-
-	.hub-list-right {
-		padding-right: 15px;
-	}
-
-	.hub-list-image {
-		position: relative;
-		width: 58px;
-		height: 58px;
-		border-right: 1px solid @border-color;
-
-		&::after {
-			font-size: 12px;
-		}
-	}
-
-	.hub-list-body {
-		padding: 12px 15px;
-	}
-
-	.hub-list-title {
-		font-weight: bold;
-	}
-
-	.hub-list-subtitle {
-		color: @text-muted;
-	}
-
-	.selling-item-message-card {
-		max-width: 500px;
-		margin-bottom: 15px;
-		border-radius: 3px;
-		border: 1px solid @border-color;
-		.selling-item-detail {
-			overflow: auto;
-			.item-image {
-				float: left;
-				height: 80px;
-				width: 80px;
-				object-fit: contain;
-				margin: 5px;
-			}
-			.item-name {
-				margin-left: 10px;
-			}
-		}
-		.received-message-container {
-			clear: left;
-			background-color: @light-bg;
-			.received-message {
-				border-top: 1px solid @border-color;
-				padding: 10px;
-			}
-			.frappe-timestamp {
-				float: right;
-			}
-		}
-	}
-
-	.form-container {
-		.frappe-control {
-			max-width: 100% !important;
-		}
-	}
-
-	.form-message {
-		padding-top: 0;
-		padding-bottom: 0;
-		border-bottom: none;
-	}
-
-	.hub-items-container {
-		.hub-items-header {
-			justify-content: space-between;
-			align-items: baseline;
-		}
-	}
-
-	.hub-item-container {
-		overflow: hidden;
-	}
-
-	.hub-item-review-container {
-		margin-top: calc(30vh);
-	}
-
-	.hub-item-dropdown {
-		margin-top: 20px;
-	}
-
-	/* messages page */
-
-	.message-list-item {
-		display: flex;
-		align-items: center;
-		padding: 8px 12px;
-
-		&:not(.active) {
-			filter: grayscale(1);
-			color: @text-muted;
-		}
-
-		&:hover {
-			background-color: @light-bg;
-		}
-
-		.list-item-left {
-			width: 30px;
-			border-radius: 4px;
-			overflow: hidden;
-			margin-right: 15px;
-		}
-
-		.list-item-body {
-			font-weight: bold;
-			padding-bottom: 1px;
-		}
-	}
-
-	.message-container {
-		display: flex;
-		flex-direction: column;
-		border: 1px solid @border-color;
-		border-radius: 3px;
-		height: calc(100vh - 300px);
-		justify-content: space-between;
-		padding: 15px;
-	}
-
-	.message-list {
-		overflow: scroll;
-	}
-}
diff --git a/erpnext/public/less/pos.less b/erpnext/public/less/pos.less
deleted file mode 100644
index b081ed4..0000000
--- a/erpnext/public/less/pos.less
+++ /dev/null
@@ -1,229 +0,0 @@
-@import "../../../../frappe/frappe/public/less/variables.less";
-
-[data-route="point-of-sale"] {
-	.layout-main-section-wrapper {
-		margin-bottom: 0;
-	}
-
-	.pos-items-wrapper {
-		max-height: ~"calc(100vh - 210px)";
-	}
-}
-
-.pos {
-	// display: flex;
-	padding: 15px;
-}
-
-.list-item {
-	min-height: 40px;
-	height: auto;
-}
-
-.cart-container {
-	padding: 0 15px;
-	// flex: 2;
-	display: inline-block;
-	width: 39%;
-	vertical-align: top;
-}
-
-.item-container {
-	padding: 0 15px;
-	// flex: 3;
-	display: inline-block;
-	width: 60%;
-	vertical-align: top;
-}
-
-.search-field {
-	width: 60%;
-
-	input::placeholder {
-		font-size: @text-medium;
-	}
-}
-
-.item-group-field {
-	width: 40%;
-	margin-left: 15px;
-}
-
-.cart-wrapper {
-	margin-bottom: 12px;
-	.list-item__content:not(:first-child) {
-		justify-content: flex-end;
-	}
-
-	.list-item--head .list-item__content:nth-child(2) {
-		flex: 1.5;
-	}
-}
-
-.cart-items {
-	height: 150px;
-	overflow: auto;
-
-	.list-item.current-item {
-		background-color: @light-yellow;
-	}
-
-	.list-item.current-item.qty input {
-		border: 1px solid @brand-primary;
-		font-weight: bold;
-	}
-
-	.list-item.current-item.disc .discount {
-		font-weight: bold;
-	}
-
-	.list-item.current-item.rate .rate {
-		font-weight: bold;
-	}
-
-	.list-item .quantity {
-		flex: 1.5;
-	}
-
-	input {
-		text-align: right;
-		height: 22px;
-		font-size: @text-medium;
-	}
-}
-
-.fields {
-	display: flex;
-}
-
-.pos-items-wrapper {
-	max-height: 480px;
-	overflow-y: auto;
-}
-
-.pos-items {
-	overflow: hidden;
-}
-
-.pos-item-wrapper {
-	display: flex;
-	flex-direction: column;
-	position: relative;
-	width: 25%;
-}
-
-.image-view-container {
-	display: block;
-}
-
-.image-view-container .image-field {
-	height: auto;
-}
-
-.empty-state {
-	height: 100%;
-	position: relative;
-
-	span {
-		position: absolute;
-		color: @text-muted;
-		font-size: @text-medium;
-		top: 50%;
-		left: 50%;
-		transform: translate(-50%, -50%);
-	}
-}
-
-@keyframes yellow-fade {
-	0% {background-color: @light-yellow;}
-	100% {background-color: transparent;}
-}
-
-.highlight {
-	animation: yellow-fade 1s ease-in 1;
-}
-
-input[type=number]::-webkit-inner-spin-button,
-input[type=number]::-webkit-outer-spin-button {
-	-webkit-appearance: none;
-	margin: 0;
-}
-
-// number pad
-
-.number-pad {
-	border-collapse: collapse;
-	cursor: pointer;
-	display: table;
-}
-.num-row {
-	display: table-row;
-}
-.num-col {
-	display: table-cell;
-	border: 1px solid @border-color;
-
-	& > div {
-		width: 50px;
-		height: 50px;
-		text-align: center;
-		line-height: 50px;
-	}
-
-	&.active {
-		background-color: @light-yellow;
-	}
-
-	&.brand-primary {
-		background-color: @brand-primary;
-		color: #ffffff;
-	}
-}
-
-// taxes, totals and discount area
-.discount-amount {
-	.discount-inputs {
-		display: flex;
-		flex-direction: column;
-		padding: 15px 0;
-	}
-
-	input:first-child {
-		margin-bottom: 10px;
-	}
-}
-
-.taxes-and-totals {
-	border-top: 1px solid @border-color;
-
-	.taxes {
-		display: flex;
-		flex-direction: column;
-		padding: 15px 0;
-		align-items: flex-end;
-
-		& > div:first-child {
-			margin-bottom: 10px;
-		}
-	}
-}
-
-.grand-total {
-	border-top: 1px solid @border-color;
-
-	.list-item {
-		height: 60px;
-	}
-
-	.grand-total-value {
-		font-size: 18px;
-	}
-}
-
-.rounded-total-value {
-  font-size: 18px;
-}
-
-.quantity-total {
-  font-size: 18px;
-}
\ No newline at end of file
diff --git a/erpnext/public/less/products.less b/erpnext/public/less/products.less
deleted file mode 100644
index 5e744ce..0000000
--- a/erpnext/public/less/products.less
+++ /dev/null
@@ -1,71 +0,0 @@
-@import "variables.less";
-
-.products-list .product-image {
-	display: inline-block;
-	width: 160px;
-	height: 160px;
-	object-fit: contain;
-	margin-right: 1rem;
-}
-
-.product-image.no-image {
-	display: flex;
-	justify-content: center;
-	align-items: center;
-	font-size: 3rem;
-	color: var(--gray);
-	background: var(--light);
-}
-
-.product-image a {
-	text-decoration: none;
-}
-
-.filter-options {
-	margin-left: -5px;
-	padding-left: 5px;
-	max-height: 300px;
-	overflow: auto;
-}
-
-.item-slideshow-image {
-	height: 3rem;
-	width: 3rem;
-	object-fit: contain;
-	padding: 0.5rem;
-	border: 1px solid @border-color;
-	border-radius: 4px;
-	cursor: pointer;
-
-	&:hover, &.active {
-		border-color: var(--primary);
-	}
-}
-
-.address-card {
-	cursor: pointer;
-	position: relative;
-
-	.check {
-		display: none;
-	}
-
-	&.active {
-		border-color: var(--primary);
-
-		.check {
-			display: inline-flex;
-		}
-	}
-}
-
-.check {
-	display: inline-flex;
-    padding: 0.25rem;
-    background: var(--primary);
-    color: white;
-    border-radius: 50%;
-	font-size: 12px;
-	width: 24px;
-	height: 24px;
-}
\ No newline at end of file
diff --git a/erpnext/public/less/website.less b/erpnext/public/less/website.less
deleted file mode 100644
index ac878de..0000000
--- a/erpnext/public/less/website.less
+++ /dev/null
@@ -1,388 +0,0 @@
-@import "variables.less";
-
-.web-long-description {
-	font-size: 18px;
-	line-height: 200%;
-}
-
-.web-page-content {
-	margin-bottom: 30px;
-}
-
-.item-stock {
-	margin-bottom: 10px !important;
-}
-
-.product-link {
-	display: block;
-	text-align: center;
-}
-
-
-.product-image img {
-	max-height: 500px;
-	margin: 0 auto;
-}
-
-@media (max-width: 767px) {
-	.product-image {
-		height: 0px;
-		padding: 0px 0px 100%;
-		overflow: hidden;
-	}
-}
-
-.product-image-square {
-	width: 100%;
-	height: 0;
-	padding: 50% 0px;
-	background-size: cover;
-	background-repeat: no-repeat;
-	background-position: center top;
-}
-
-.product-image.missing-image {
-	.product-image-square;
-	position: relative;
-	background-color: @light-border-color;
-}
-
-.product-image.missing-image .octicon {
-	font-size: 32px;
-	color: @border-color;
-}
-
-.product-search {
-	margin-bottom: 15px;
-}
-
-
-@media (max-width: 767px) {
-	.product-search {
-		width: 100%;
-	}
-}
-
-.borderless td, .borderless th {
-  border-bottom: 1px solid @light-border-color;
-  padding-left:0px !important;
-  line-height: 1.8em !important;
-}
-
-.item-desc {
-	 border-top: 2px solid @light-border-color;
-	 padding-top:10px;
-}
-
-.featured-products {
-	border-top: 1px solid @light-border-color;
-}
-
-.transaction-list-item {
-	.indicator {
-		font-weight: inherit;
-		color: @text-muted;
-	}
-
-	.transaction-time {
-		margin-top: 5px;
-	}
-
-}
-
-// order.html
-.transaction-subheading {
-	.indicator {
-		font-weight: inherit;
-		color: @text-muted;
-	}
-}
-
-.order-container {
-	margin: 50px 0px;
-
-	.order-item-header .h6 {
-		padding: 7px 15px;
-	}
-
-	.order-items {
-		margin: 30px 0px 0px;
-	}
-
-	.order-item-table {
-		margin: 0px -15px;
-	}
-
-	.order-item-header {
-		border-bottom: 1px solid #d1d8dd;
-	}
-
-	.order-image-col {
-		padding-right: 0px;
-	}
-
-	.order-image {
-		max-width: 55px;
-		max-height: 55px;
-		margin-top: -5px;
-	}
-
-	.order-taxes {
-		margin-top: 30px;
-
-		.row {
-			margin-top: 15px;
-		}
-	}
-
-	.tax-grand-total-row {
-		padding-top: 15px;
-		padding-bottom: 30px;
-	}
-
-	.tax-grand-total {
-		display: inline-block;
-		font-size: 16px;
-		font-weight: bold;
-		margin-top: 5px;
-	}
-}
-
-.cart-container {
-	margin: 50px 0px;
-
-	.checkout {
-		margin-bottom:15px;
-	}
-
-	.cart-item-header .h6 {
-		padding: 7px 15px;
-	}
-
-	.cart-items {
-		margin: 30px 0px 0px;
-	}
-
-	.cart-item-table {
-		margin: 0px -15px;
-	}
-
-	.cart-item-header {
-		border-bottom: 1px solid #d1d8dd;
-	}
-
-	.cart-image-col {
-		padding-right: 0px;
-	}
-
-	.cart-image {
-		max-width: 55px;
-		max-height: 55px;
-		margin-top: -5px;
-	}
-
-	.cart-taxes {
-		margin-top: 30px;
-
-		.row {
-			margin-top: 15px;
-		}
-	}
-
-	.tax-grand-total-row {
-		border-top: 1px solid @border-color;
-		padding-top: 15px;
-	}
-
-	.cart-addresses {
-		margin-top: 50px;
-	}
-}
-
-.cart-items-dropdown .cart-dropdown,
-.item_name_dropdown {
-	display: none;
-
-}
-
-.cart-dropdown-container {
-	width: 400px;
-	padding: 15px;
-
-	.item-price {
-		display: block !important;
-		padding-bottom: 10px;
-	}
-
-	.cart-item-header {
-		border-bottom: 1px solid #d1d8dd;
-	}
-
-	.cart-items-dropdown {
-		max-height: 350px;
-	}
-
-	.cart-items-dropdown .cart-dropdown {
-		display:block;
-		margin-top:15px;
-	}
-
-	.item_name_dropdown {
-		display:block;
-	}
-
-	.item-description,
-	.cart-items .checkout,
-	.item_name_and_description {
-		display: none;
-	}
-
-	.checkout-btn {
-		padding-bottom:25px;
-	}
-	.col-name-description {
-		margin-bottom:8px;
-	}
-}
-
-// .number-spinner {
-// 	width:100px;
-// 	margin-top:5px;
-// }
-
-.cart-btn {
-	border-color: #ccc;
-}
-.cart-qty {
-	text-align:center;
-}
-
-.product-list-link {
-	.row {
-		border-bottom: 1px solid @light-border-color;
-	}
-
-	.row:hover {
-		background-color: @light-bg;
-	}
-
-	.row > div {
-		padding-top: 15px;
-		padding-bottom: 15px;
-	}
-}
-
-.product-list-link:first-child .row {
-	border-top: 1px solid @light-border-color;
-}
-
-.item-group-nav-buttons {
-	margin-top: 15px;
-}
-
-.footer-subscribe {
-	.btn-default {
-		background-color: transparent;
-		border: 1px solid @border-color;
-	}
-}
-
-@media (min-width: 992px) {
-	.footer-subscribe {
-		max-width: 350px;
-	}
-}
-
-.item-group-content {
-	margin-top: 30px;
-}
-
-.item-group-slideshow {
-	margin-bottom: 1rem;
-}
-
-.product-image-img {
-	border: 1px solid @light-border-color;
-	border-radius: 3px;
-}
-
-.product-text {
-	word-wrap: break-word;
-	height: 75px;
-	display: block; /* Fallback for non-webkit */
-	display: -webkit-box;
-	max-width: 100%;
-	margin: 0 auto;
-	-webkit-line-clamp: 3;
-	-webkit-box-orient: vertical;
-	overflow: hidden;
-	text-overflow: ellipsis;
-}
-
-.product-image-wrapper {
-	padding-bottom: 40px;
-}
-
-.duration-bar {
-  display: inline-block;
-  color: white;
-  background: #8FD288;
-  padding: 3px;
-}
-
-.duration-invisible {
-  visibility: hidden;
-}
-
-.duration-value {
-  float: right;
-}
-
-.bar-outer-text {
-  color: #8FD288;
-  background: none;
-  float: none;
-  border: none;
-}
-
-.bom-spec {
-	margin-bottom: 20px;
-}
-
-.modal-title {
-	margin-top: 5px;
-}
-
-.modal-header {
-	padding: 10px 15px;
-}
-// For Item Alternate Image
-.item-alternative-image {
-	padding: 5px;
-	margin-bottom: 5px;
-
-	&:hover {
-		border-color: @brand-primary;
-	}
-}
-
-.item-slideshow-image {
-	height: 3rem;
-	width: 3rem;
-	object-fit: contain;
-	padding: 0.5rem;
-	border: 1px solid @border-color;
-	border-radius: 4px;
-	cursor: pointer;
-
-	&:hover, &.active {
-		border-color: @brand-primary;
-	}
-}
-
-.section-products {
-	.card-img-top {
-		max-height: 300px;
-		object-fit: contain;
-	}
-}
\ No newline at end of file
diff --git a/erpnext/public/scss/erpnext-web.bundle.scss b/erpnext/public/scss/erpnext-web.bundle.scss
new file mode 100644
index 0000000..6ef1892
--- /dev/null
+++ b/erpnext/public/scss/erpnext-web.bundle.scss
@@ -0,0 +1,2 @@
+@import "./shopping_cart";
+@import "./website";
diff --git a/erpnext/public/scss/erpnext.bundle.scss b/erpnext/public/scss/erpnext.bundle.scss
new file mode 100644
index 0000000..d3313c7
--- /dev/null
+++ b/erpnext/public/scss/erpnext.bundle.scss
@@ -0,0 +1,3 @@
+@import "./erpnext";
+@import "./call_popup";
+@import "./point-of-sale";
diff --git a/erpnext/public/less/erpnext.less b/erpnext/public/scss/erpnext.scss
similarity index 81%
rename from erpnext/public/less/erpnext.less
rename to erpnext/public/scss/erpnext.scss
index 4076ebe..0e61861 100644
--- a/erpnext/public/less/erpnext.less
+++ b/erpnext/public/scss/erpnext.scss
@@ -1,4 +1,4 @@
-@import "variables.less";
+@import "frappe/public/scss/desk/variables";
 
 .erpnext-footer {
 	margin: 11px auto;
@@ -141,7 +141,7 @@
 	}
 
 	.pos-payment-row {
-		border-bottom:1px solid @border-color;
+		border-bottom:1px solid $border-color;
 		margin: 2px 0px 5px 0px;
 		height: 60px;
 		margin-top: 0px;
@@ -149,12 +149,12 @@
 	}
 
 	.pos-payment-row:hover, .pos-keyboard-key:hover{
-		background-color: @light-bg;
+		background-color: var(--bg-color);
 		cursor: pointer;
 	}
 
 	.pos-keyboard-key, .delete-btn {
-		border: 1px solid @border-color;
+		border: 1px solid $border-color;
 		height:85px;
 		width:85px;
 		margin:10px 10px;
@@ -165,7 +165,7 @@
 	}
 
 	.numeric-keypad {
-		border: 1px solid @border-color;
+		border: 1px solid $border-color;
 		height:69px;
 		width:69px;
 		font-size:20px;
@@ -192,13 +192,13 @@
 		background-color: #fff;
 		margin-left:-4px;
 
-		@media (max-width: @screen-md) {
+		@media (max-width: map-get($grid-breakpoints, "xl")) {
 			height: 45px;
 			width: 45px;
 			font-size: 14px;
 		}
 
-		@media (max-width: @screen-sm) {
+		@media (max-width: map-get($grid-breakpoints, "lg")) {
 			height: 40px;
 			width: 40px;
 		}
@@ -209,21 +209,21 @@
 
 		& > .row > button {
 			border: none;
-			border-right: 1px solid @border-color;
-			border-bottom: 1px solid @border-color;
+			border-right: 1px solid $border-color;
+			border-bottom: 1px solid $border-color;
 
 			&:first-child {
-				border-left: 1px solid @border-color;
+				border-left: 1px solid $border-color;
 			}
 		}
 
 		& > .row:first-child > button {
-			border-top: 1px solid @border-color;
+			border-top: 1px solid $border-color;
 		}
 	}
 
 	.pos-pay {
-		background-color: @brand-primary;
+		background-color: var(--primary);
 		border: none;
 	}
 
@@ -236,13 +236,13 @@
 	}
 
 	.list-row-head.pos-invoice-list {
-		border-top: 1px solid @border-color;
+		border-top: 1px solid $border-color;
 	}
 
 	.modal-dialog {
 		width: 750px;
 
-		@media (max-width: @screen-xs) {
+		@media (max-width: map-get($grid-breakpoints, 'md')) {
 			width: auto;
 
 			.modal-content {
@@ -251,7 +251,7 @@
 		}
 	}
 
-	@media (max-width: @screen-xs) {
+	@media (max-width: map-get($grid-breakpoints, 'md')) {
 		.amount-row h3 {
 			font-size: 15px;
 		}
@@ -271,7 +271,7 @@
 	}
 
 	.selected-payment-mode {
-		background-color: @light-bg;
+		background-color: var(--bg-color);
 		cursor: pointer;
 	}
 
@@ -291,7 +291,7 @@
 		padding: 9px 15px;
 		font-size: 12px;
 		margin: 0px;
-		border-bottom: 1px solid @border-color;
+		border-bottom: 1px solid $border-color;
 
 		.cell {
 			display: table-cell;
@@ -313,17 +313,17 @@
 
 	.pos-bill-header {
 		background-color: #f5f7fa;
-		border: 1px solid @border-color;
+		border: 1px solid $border-color;
 		padding: 13px 15px;
 	}
 
 	.pos-list-row.active {
-		background-color: @light-yellow;
+		background-color: var(--fg-hover-color);
 	}
 
 	.totals-area {
-		border-right: 1px solid @border-color;
-		border-left: 1px solid @border-color;
+		border-right: 1px solid $border-color;
+		border-left: 1px solid $border-color;
 		margin-bottom: 15px;
 	}
 
@@ -332,12 +332,12 @@
 	}
 
 	.item-cart-items {
-		height: ~"calc(100vh - 526px)";
+		height: calc(100vh - 526px);
 		overflow: auto;
-		border: 1px solid @border-color;
+		border: 1px solid $border-color;
 		border-top: none;
 
-		@media (max-width: @screen-xs) {
+		@media (max-width: map-get($grid-breakpoints, 'md')) {
 			height: 30vh;
 		}
 	}
@@ -359,12 +359,12 @@
 	}
 
 	.item-list {
-		border: 1px solid @border-color;
+		border: 1px solid $border-color;
 		border-top: none;
-		max-height: ~"calc(100vh - 190px)";
+		max-height: calc(100vh - 190px);
 		overflow: auto;
 
-		@media (max-width: @screen-xs) {
+		@media (max-width: map-get($grid-breakpoints, 'md')) {
 			max-height: initial;
 		}
 
@@ -402,7 +402,7 @@
 		&> .pos-list-row {
 			border: none;
 
-			@media (max-width: @screen-md) {
+			@media (max-width: map-get($grid-breakpoints, 'xl')) {
 				padding: 5px 15px;
 			}
 		}
@@ -420,7 +420,7 @@
 		justify-content: center;
 		align-items: center;
 		cursor: pointer;
-		background-color: @light-bg;
+		background-color: var(--bg-color);
 		min-height: 200px;
 	}
 
@@ -428,7 +428,7 @@
 		cursor: pointer;
 	}
 
-	@media (max-width: @screen-xs) {
+	@media (max-width: map-get($grid-breakpoints, 'md')) {
 		.page-actions {
 			max-width: 110px;
 		}
@@ -491,4 +491,4 @@
 
 .exercise-col {
 	padding: 10px;
-}
\ No newline at end of file
+}
diff --git a/erpnext/public/scss/erpnext_email.bundle.scss b/erpnext/public/scss/erpnext_email.bundle.scss
new file mode 100644
index 0000000..3c0b918
--- /dev/null
+++ b/erpnext/public/scss/erpnext_email.bundle.scss
@@ -0,0 +1,32 @@
+@import "frappe/public/scss/desk/variables";
+
+.panel-header {
+	background-color: var(--bg-color);
+	border: 1px solid $border-color;
+	border-radius: 3px 3px 0 0;
+}
+
+.panel-body {
+	background-color: #fff;
+	border: 1px solid $border-color;
+	border-top: none;
+	border-radius: 0 0 3px 3px;
+	overflow-wrap: break-word;
+}
+
+.sender-avatar {
+	width: 24px;
+	height: 24px;
+	border-radius: 3px;
+	vertical-align: middle;
+}
+
+.sender-avatar-placeholder {
+	@extend .sender-avatar;
+
+	line-height: 24px;
+	text-align: center;
+	color: $border-color;
+	border: 1px solid $border-color;
+	background-color: #fff;
+}
diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss
index 0bb8e68..9bdaa8d 100644
--- a/erpnext/public/scss/point-of-sale.scss
+++ b/erpnext/public/scss/point-of-sale.scss
@@ -129,11 +129,20 @@
 				@extend .pointer-no-select;
 				border-radius: var(--border-radius-md);
 				box-shadow: var(--shadow-base);
+				position: relative;
 
 				&:hover {
 					transform: scale(1.02, 1.02);
 				}
 
+				.item-qty-pill {
+					position: absolute;
+					display: flex;
+					margin: var(--margin-sm);
+					justify-content: flex-end;
+					right: 0px;
+				}
+
 				.item-display {
 					display: flex;
 					align-items: center;
@@ -766,9 +775,10 @@
 		> .payment-modes {
 			display: flex;
 			padding-bottom: var(--padding-sm);
-			margin-bottom: var(--margin-xs);
+			margin-bottom: var(--margin-sm);
 			overflow-x: scroll;
 			overflow-y: hidden;
+			flex-shrink: 0;
 
 			> .payment-mode-wrapper {
 				min-width: 40%;
@@ -825,9 +835,24 @@
 		> .fields-numpad-container {
 			display: flex;
 			flex: 1;
+			height: 100%;
+    		position: relative;
+			justify-content: flex-end;
 
 			> .fields-section {
 				flex: 1;
+				position: absolute;
+				display: flex;
+				flex-direction: column;
+				width: 50%;
+				height: 100%;
+				top: 0;
+				left: 0;
+				padding-bottom: var(--margin-md);
+
+				.invoice-fields {
+					overflow-y: scroll;
+				}
 			}
 
 			> .number-pad {
@@ -835,6 +860,7 @@
 				display: flex;
 				justify-content: flex-end;
 				align-items: flex-end;
+				max-width: 50%;
 
 				.numpad-container {
 					display: grid;
@@ -861,6 +887,7 @@
 			margin-bottom: var(--margin-sm);
 			justify-content: center;
 			flex-direction: column;
+			flex-shrink: 0;
 
 			> .totals {
 				display: flex;
diff --git a/erpnext/regional/germany/setup.py b/erpnext/regional/germany/setup.py
index ac1f543..c1fa6e4 100644
--- a/erpnext/regional/germany/setup.py
+++ b/erpnext/regional/germany/setup.py
@@ -1,11 +1,24 @@
 import os
 import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 
 
 def setup(company=None, patch=True):
+	make_custom_fields()
 	add_custom_roles_for_reports()
 
 
+def make_custom_fields():
+	custom_fields = {
+		'Party Account': [
+			dict(fieldname='debtor_creditor_number', label='Debtor/Creditor Number',
+			fieldtype='Data', insert_after='account', translatable=0)
+		]
+	}
+
+	create_custom_fields(custom_fields)
+
+
 def add_custom_roles_for_reports():
 	"""Add Access Control to UAE VAT 201."""
 	if not frappe.db.get_value('Custom Role', dict(report='DATEV')):
@@ -16,4 +29,4 @@
 				dict(role='Accounts User'),
 				dict(role='Accounts Manager')
 			]
-		)).insert()
\ No newline at end of file
+		)).insert()
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
index f138a80..826d51f 100644
--- a/erpnext/regional/germany/utils/datev/datev_csv.py
+++ b/erpnext/regional/germany/utils/datev/datev_csv.py
@@ -56,10 +56,10 @@
 	)
 
 	if not six.PY2:
-		data = data.encode('latin_1')
+		data = data.encode('latin_1', errors='replace')
 
 	header = get_header(filters, csv_class)
-	header = ';'.join(header).encode('latin_1')
+	header = ';'.join(header).encode('latin_1', errors='replace')
 
 	# 1st Row: Header with meta data
 	# 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here.
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 699441b..b4e7a88 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -71,13 +71,14 @@
 
 def raise_document_name_too_long_error():
 	title = _('Document ID Too Long')
-	msg = _('As you have E-Invoicing enabled, to be able to generate IRN for this invoice, ')
-	msg += _('document id {} exceed 16 letters. ').format(bold(_('should not')))
+	msg = _('As you have E-Invoicing enabled, to be able to generate IRN for this invoice')
+	msg += ', '
+	msg += _('document id {} exceed 16 letters.').format(bold(_('should not')))
 	msg += '<br><br>'
-	msg += _('You must {} your {} in order to have document id of {} length 16. ').format(
+	msg += _('You must {} your {} in order to have document id of {} length 16.').format(
 		bold(_('modify')), bold(_('naming series')), bold(_('maximum'))
 	)
-	msg += _('Please account for ammended documents too. ')
+	msg += _('Please account for ammended documents too.')
 	frappe.throw(msg, title=title)
 
 def read_json(name):
@@ -847,6 +848,7 @@
 			res = self.make_request('post', self.generate_ewaybill_url, headers, data)
 			if res.get('success'):
 				self.invoice.ewaybill = res.get('result').get('EwbNo')
+				self.invoice.eway_bill_validity = res.get('result').get('EwbValidTill')
 				self.invoice.eway_bill_cancelled = 0
 				self.invoice.update(args)
 				self.invoice.flags.updater_reference = {
@@ -944,6 +946,7 @@
 
 		self.invoice.irn = res.get('Irn')
 		self.invoice.ewaybill = res.get('EwbNo')
+		self.invoice.eway_bill_validity = res.get('EwbValidTill')
 		self.invoice.ack_no = res.get('AckNo')
 		self.invoice.ack_date = res.get('AckDt')
 		self.invoice.signed_einvoice = dec_signed_invoice
@@ -960,6 +963,7 @@
 			'label': _('IRN Generated')
 		}
 		self.update_invoice()
+
 	def attach_qrcode_image(self):
 		qrcode = self.invoice.signed_qr_code
 		doctype = self.invoice.doctype
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 9ded8da..b12e152 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -422,6 +422,9 @@
 		dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
 			depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
 
+		dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
+			depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill'),
+
 		dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
 			depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
 
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 86de90f..dc2bb93 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -879,3 +879,24 @@
 	if total_charges != additional_taxes:
 		diff = additional_taxes - total_charges
 		doc.get('items')[item_count - 1].taxable_value += diff
+
+def get_depreciation_amount(asset, depreciable_value, row):
+	depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
+
+	if row.depreciation_method in ("Straight Line", "Manual"):
+		depreciation_amount = (flt(row.value_after_depreciation) -
+			flt(row.expected_value_after_useful_life)) / depreciation_left
+	else:
+		rate_of_depreciation = row.rate_of_depreciation
+		# if its the first depreciation
+		if depreciable_value == asset.gross_purchase_amount:
+			# as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2
+			diff = date_diff(asset.available_for_use_date, row.depreciation_start_date)
+			if diff <= 180:
+				rate_of_depreciation = rate_of_depreciation / 2
+				frappe.msgprint(
+					_('As per IT Act, the rate of depreciation for the first depreciation entry is reduced by 50%.'))
+
+		depreciation_amount = flt(depreciable_value * (flt(rate_of_depreciation) / 100))
+
+	return depreciation_amount
\ No newline at end of file
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
index cbc9478..a5ca7ee 100644
--- a/erpnext/regional/report/datev/datev.py
+++ b/erpnext/regional/report/datev/datev.py
@@ -3,9 +3,9 @@
 Provide a report and downloadable CSV according to the German DATEV format.
 
 - Query report showing only the columns that contain data, formatted nicely for
-  dispay to the user.
+	dispay to the user.
 - CSV download functionality `download_datev_csv` that provides a CSV file with
-  all required columns. Used to import the data into the DATEV Software.
+	all required columns. Used to import the data into the DATEV Software.
 """
 from __future__ import unicode_literals
 
@@ -88,6 +88,32 @@
 		"fieldtype": "Dynamic Link",
 		"options": "Beleginfo - Art 2",
 		"width": 150
+	},
+	{
+		"label": "Beleginfo - Art 3",
+		"fieldname": "Beleginfo - Art 3",
+		"fieldtype": "Link",
+		"options": "DocType",
+		"width": 100
+	},
+	{
+		"label": "Beleginfo - Inhalt 3",
+		"fieldname": "Beleginfo - Inhalt 3",
+		"fieldtype": "Dynamic Link",
+		"options": "Beleginfo - Art 3",
+		"width": 150
+	},
+	{
+		"label": "Beleginfo - Art 4",
+		"fieldname": "Beleginfo - Art 4",
+		"fieldtype": "Data",
+		"width": 100
+	},
+	{
+		"label": "Beleginfo - Inhalt 4",
+		"fieldname": "Beleginfo - Inhalt 4",
+		"fieldtype": "Data",
+		"width": 150
 	}
 ]
 
@@ -120,10 +146,8 @@
 	validate_fiscal_year(from_date, to_date, company)
 
 	if not frappe.db.exists('DATEV Settings', filters.get('company')):
-		frappe.log_error(_('Please create {} for Company {}.').format(
-			'<a href="desk#List/DATEV%20Settings/List">{}</a>'.format(_('DATEV Settings')),
-			frappe.bold(filters.get('company'))
-		))
+		msg = 'Please create DATEV Settings for Company {}'.format(filters.get('company'))
+		frappe.log_error(msg, title='DATEV Settings missing')
 		return False
 
 	return True
@@ -169,7 +193,11 @@
 			gl.voucher_type as 'Beleginfo - Art 1',
 			gl.voucher_no as 'Beleginfo - Inhalt 1',
 			gl.against_voucher_type as 'Beleginfo - Art 2',
-			gl.against_voucher as 'Beleginfo - Inhalt 2'
+			gl.against_voucher as 'Beleginfo - Inhalt 2',
+			gl.party_type as 'Beleginfo - Art 3',
+			gl.party as 'Beleginfo - Inhalt 3',
+			case gl.party_type when 'Customer' then 'Debitorennummer' when 'Supplier' then 'Kreditorennummer' else NULL end as 'Beleginfo - Art 4',
+			par.debtor_creditor_number as 'Beleginfo - Inhalt 4'
 
 		FROM `tabGL Entry` gl
 
@@ -177,6 +205,19 @@
 			left join `tabAccount` acc 
 			on gl.account = acc.name
 
+			left join `tabCustomer` cus
+			on gl.party_type = 'Customer'
+			and gl.party = cus.name
+
+			left join `tabSupplier` sup
+			on gl.party_type = 'Supplier'
+			and gl.party = sup.name
+
+			left join `tabParty Account` par
+			on par.parent = gl.party
+			and par.parenttype = gl.party_type
+			and par.company = %(company)s
+
 		WHERE gl.company = %(company)s 
 		AND DATE(gl.posting_date) >= %(from_date)s
 		AND DATE(gl.posting_date) <= %(to_date)s
@@ -196,40 +237,56 @@
 	return frappe.db.sql("""
 		SELECT
 
-			acc.account_number as 'Konto',
-			CASE cus.customer_type WHEN 'Company' THEN cus.customer_name ELSE null END as 'Name (Adressatentyp Unternehmen)',
-			CASE cus.customer_type WHEN 'Individual' THEN con.last_name ELSE null END as 'Name (Adressatentyp natürl. Person)',
-			CASE cus.customer_type WHEN 'Individual' THEN con.first_name ELSE null END as 'Vorname (Adressatentyp natürl. Person)',
-			CASE cus.customer_type WHEN 'Individual' THEN '1' WHEN 'Company' THEN '2' ELSE '0' end as 'Adressatentyp',
+			par.debtor_creditor_number as 'Konto',
+			CASE cus.customer_type
+				WHEN 'Company' THEN cus.customer_name
+				ELSE null
+				END as 'Name (Adressatentyp Unternehmen)',
+			CASE cus.customer_type
+				WHEN 'Individual' THEN TRIM(SUBSTR(cus.customer_name, LOCATE(' ', cus.customer_name)))
+				ELSE null
+				END as 'Name (Adressatentyp natürl. Person)',
+			CASE cus.customer_type
+				WHEN 'Individual' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(cus.customer_name, ' ', 1), ' ', -1)
+				ELSE null
+				END as 'Vorname (Adressatentyp natürl. Person)',
+			CASE cus.customer_type
+				WHEN 'Individual' THEN '1'
+				WHEN 'Company' THEN '2'
+				ELSE '0'
+				END as 'Adressatentyp',
 			adr.address_line1 as 'Straße',
 			adr.pincode as 'Postleitzahl',
 			adr.city as 'Ort',
 			UPPER(country.code) as 'Land',
 			adr.address_line2 as 'Adresszusatz',
-			con.email_id as 'E-Mail',
-			coalesce(con.mobile_no, con.phone) as 'Telefon',
+			adr.email_id as 'E-Mail',
+			adr.phone as 'Telefon',
+			adr.fax as 'Fax',
 			cus.website as 'Internet',
 			cus.tax_id as 'Steuernummer'
 
-		FROM `tabParty Account` par
+		FROM `tabCustomer` cus
 
-			left join `tabAccount` acc
-			on acc.name = par.account
+			left join `tabParty Account` par
+			on par.parent = cus.name
+			and par.parenttype = 'Customer'
+			and par.company = %(company)s
 
-			left join `tabCustomer` cus
-			on cus.name = par.parent
+			left join `tabDynamic Link` dyn_adr
+			on dyn_adr.link_name = cus.name
+			and dyn_adr.link_doctype = 'Customer'
+			and dyn_adr.parenttype = 'Address'
 
 			left join `tabAddress` adr
-			on adr.name = cus.customer_primary_address
+			on adr.name = dyn_adr.parent
+			and adr.is_primary_address = '1'
 
 			left join `tabCountry` country
 			on country.name = adr.country
 
-			left join `tabContact` con
-			on con.name = cus.customer_primary_contact
-
-		WHERE par.company = %(company)s
-		AND par.parenttype = 'Customer'""", filters, as_dict=1)
+		WHERE adr.is_primary_address = '1'
+		""", filters, as_dict=1)
 
 
 def get_suppliers(filters):
@@ -242,35 +299,48 @@
 	return frappe.db.sql("""
 		SELECT
 
-			acc.account_number as 'Konto',
-			CASE sup.supplier_type WHEN 'Company' THEN sup.supplier_name ELSE null END as 'Name (Adressatentyp Unternehmen)',
-			CASE sup.supplier_type WHEN 'Individual' THEN con.last_name ELSE null END as 'Name (Adressatentyp natürl. Person)',
-			CASE sup.supplier_type WHEN 'Individual' THEN con.first_name ELSE null END as 'Vorname (Adressatentyp natürl. Person)',
-			CASE sup.supplier_type WHEN 'Individual' THEN '1' WHEN 'Company' THEN '2' ELSE '0' end as 'Adressatentyp',
+			par.debtor_creditor_number as 'Konto',
+			CASE sup.supplier_type
+				WHEN 'Company' THEN sup.supplier_name
+				ELSE null
+				END as 'Name (Adressatentyp Unternehmen)',
+			CASE sup.supplier_type
+				WHEN 'Individual' THEN TRIM(SUBSTR(sup.supplier_name, LOCATE(' ', sup.supplier_name)))
+				ELSE null
+				END as 'Name (Adressatentyp natürl. Person)',
+			CASE sup.supplier_type
+				WHEN 'Individual' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(sup.supplier_name, ' ', 1), ' ', -1)
+				ELSE null
+				END as 'Vorname (Adressatentyp natürl. Person)',
+			CASE sup.supplier_type
+				WHEN 'Individual' THEN '1'
+				WHEN 'Company' THEN '2'
+				ELSE '0'
+				END as 'Adressatentyp',
 			adr.address_line1 as 'Straße',
 			adr.pincode as 'Postleitzahl',
 			adr.city as 'Ort',
 			UPPER(country.code) as 'Land',
 			adr.address_line2 as 'Adresszusatz',
-			con.email_id as 'E-Mail',
-			coalesce(con.mobile_no, con.phone) as 'Telefon',
+			adr.email_id as 'E-Mail',
+			adr.phone as 'Telefon',
+			adr.fax as 'Fax',
 			sup.website as 'Internet',
 			sup.tax_id as 'Steuernummer',
 			case sup.on_hold when 1 then sup.release_date else null end as 'Zahlungssperre bis'
 
-		FROM `tabParty Account` par
+		FROM `tabSupplier` sup
 
-			left join `tabAccount` acc
-			on acc.name = par.account
-
-			left join `tabSupplier` sup
-			on sup.name = par.parent
+			left join `tabParty Account` par
+			on par.parent = sup.name
+			and par.parenttype = 'Supplier'
+			and par.company = %(company)s
 
 			left join `tabDynamic Link` dyn_adr
 			on dyn_adr.link_name = sup.name
 			and dyn_adr.link_doctype = 'Supplier'
 			and dyn_adr.parenttype = 'Address'
-			
+
 			left join `tabAddress` adr
 			on adr.name = dyn_adr.parent
 			and adr.is_primary_address = '1'
@@ -278,17 +348,8 @@
 			left join `tabCountry` country
 			on country.name = adr.country
 
-			left join `tabDynamic Link` dyn_con
-			on dyn_con.link_name = sup.name
-			and dyn_con.link_doctype = 'Supplier'
-			and dyn_con.parenttype = 'Contact'
-
-			left join `tabContact` con
-			on con.name = dyn_con.parent
-			and con.is_primary_contact = '1'
-
-		WHERE par.company = %(company)s
-		AND par.parenttype = 'Supplier'""", filters, as_dict=1)
+		WHERE adr.is_primary_address = '1'
+		""", filters, as_dict=1)
 
 
 def get_account_names(filters):
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 49ca942..51d86ff 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -490,7 +490,7 @@
 	outstanding_based_on_gle = flt(outstanding_based_on_gle[0][0]) if outstanding_based_on_gle else 0
 
 	# Outstanding based on Sales Order
-	outstanding_based_on_so = 0.0
+	outstanding_based_on_so = 0
 
 	# if credit limit check is bypassed at sales order level,
 	# we should not consider outstanding Sales Orders, when customer credit balance report is run
@@ -501,9 +501,11 @@
 			where customer=%s and docstatus = 1 and company=%s
 			and per_billed < 100 and status != 'Closed'""", (customer, company))
 
-		outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0.0
+		outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0
 
 	# Outstanding based on Delivery Note, which are not created against Sales Order
+	outstanding_based_on_dn = 0
+
 	unmarked_delivery_note_items = frappe.db.sql("""select
 			dn_item.name, dn_item.amount, dn.base_net_total, dn.base_grand_total
 		from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item
@@ -515,15 +517,29 @@
 			and ifnull(dn_item.against_sales_invoice, '') = ''
 		""", (customer, company), as_dict=True)
 
-	outstanding_based_on_dn = 0.0
+	if not unmarked_delivery_note_items:
+		return outstanding_based_on_gle + outstanding_based_on_so
+
+	si_amounts = frappe.db.sql("""
+		SELECT
+			dn_detail, sum(amount) from `tabSales Invoice Item`
+		WHERE
+			docstatus = 1
+			and dn_detail in ({})
+		GROUP BY dn_detail""".format(", ".join(
+			frappe.db.escape(dn_item.name)
+			for dn_item in unmarked_delivery_note_items
+		))
+	)
+
+	si_amounts = {si_item[0]: si_item[1] for si_item in si_amounts}
 
 	for dn_item in unmarked_delivery_note_items:
-		si_amount = frappe.db.sql("""select sum(amount)
-			from `tabSales Invoice Item`
-			where dn_detail = %s and docstatus = 1""", dn_item.name)[0][0]
+		dn_amount = flt(dn_item.amount)
+		si_amount = flt(si_amounts.get(dn_item.name))
 
-		if flt(dn_item.amount) > flt(si_amount) and dn_item.base_net_total:
-			outstanding_based_on_dn += ((flt(dn_item.amount) - flt(si_amount)) \
+		if dn_amount > si_amount and dn_item.base_net_total:
+			outstanding_based_on_dn += ((dn_amount - si_amount)
 				/ dn_item.base_net_total) * dn_item.base_grand_total
 
 	return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn
diff --git a/erpnext/selling/doctype/installation_note/installation_note.js b/erpnext/selling/doctype/installation_note/installation_note.js
index 7fd0877..27a3b35 100644
--- a/erpnext/selling/doctype/installation_note/installation_note.js
+++ b/erpnext/selling/doctype/installation_note/installation_note.js
@@ -30,8 +30,8 @@
 frappe.provide("erpnext.selling");
 
 // TODO commonify this code
-erpnext.selling.InstallationNote = frappe.ui.form.Controller.extend({
-	refresh: function() {
+erpnext.selling.InstallationNote = class InstallationNote extends frappe.ui.form.Controller {
+	refresh() {
 		var me = this;
 		if (this.frm.doc.docstatus===0) {
 			this.frm.add_custom_button(__('From Delivery Note'),
@@ -54,7 +54,7 @@
 				}, "fa fa-download", "btn-default"
 			);
 		}
-	},
-});
+	}
+};
 
-$.extend(cur_frm.cscript, new erpnext.selling.InstallationNote({frm: cur_frm}));
\ No newline at end of file
+extend_cscript(cur_frm.cscript, new erpnext.selling.InstallationNote({frm: cur_frm}));
diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js
index 5a0d9c9..7fecd4b 100644
--- a/erpnext/selling/doctype/quotation/quotation.js
+++ b/erpnext/selling/doctype/quotation/quotation.js
@@ -36,13 +36,12 @@
 	}
 });
 
-erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
-	onload: function(doc, dt, dn) {
+erpnext.selling.QuotationController = class QuotationController extends erpnext.selling.SellingController {
+	onload(doc, dt, dn) {
 		var me = this;
-		this._super(doc, dt, dn);
-
-	},
-	party_name: function() {
+		super.onload(doc, dt, dn);
+	}
+	party_name() {
 		var me = this;
 		erpnext.utils.get_party_details(this.frm, null, null, function() {
 			me.apply_price_list();
@@ -51,9 +50,9 @@
 		if(me.frm.doc.quotation_to=="Lead" && me.frm.doc.party_name) {
 			me.frm.trigger("get_lead_details");
 		}
-	},
-	refresh: function(doc, dt, dn) {
-		this._super(doc, dt, dn);
+	}
+	refresh(doc, dt, dn) {
+		super.refresh(doc, dt, dn);
 		doctype = doc.quotation_to == 'Customer' ? 'Customer':'Lead';
 		frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'party_name', doctype: doctype}
 
@@ -121,9 +120,9 @@
 
 		this.toggle_reqd_lead_customer();
 
-	},
+	}
 
-	set_dynamic_field_label: function(){
+	set_dynamic_field_label(){
 		if (this.frm.doc.quotation_to == "Customer")
 		{
 			this.frm.set_df_property("party_name", "label", "Customer");
@@ -138,22 +137,22 @@
 				return{	query: "erpnext.controllers.queries.lead_query" }
 			}
 		}
-	},
+	}
 
-	toggle_reqd_lead_customer: function() {
+	toggle_reqd_lead_customer() {
 		var me = this;
 
 		// to overwrite the customer_filter trigger from queries.js
 		this.frm.toggle_reqd("party_name", this.frm.doc.quotation_to);
 		this.frm.set_query('customer_address', this.address_query);
 		this.frm.set_query('shipping_address_name', this.address_query);
-	},
+	}
 
-	tc_name: function() {
+	tc_name() {
 		this.get_terms();
-	},
+	}
 
-	address_query: function(doc) {
+	address_query(doc) {
 		return {
 			query: 'frappe.contacts.doctype.address.address.address_query',
 			filters: {
@@ -161,20 +160,20 @@
 				link_name: doc.party_name
 			}
 		};
-	},
+	}
 
-	validate_company_and_party: function(party_field) {
+	validate_company_and_party(party_field) {
 		if(!this.frm.doc.quotation_to) {
 			frappe.msgprint(__("Please select a value for {0} quotation_to {1}", [this.frm.doc.doctype, this.frm.doc.name]));
 			return false;
 		} else if (this.frm.doc.quotation_to == "Lead") {
 			return true;
 		} else {
-			return this._super(party_field);
+			return super.validate_company_and_party(party_field);
 		}
-	},
+	}
 
-	get_lead_details: function() {
+	get_lead_details() {
 		var me = this;
 		if(!this.frm.doc.quotation_to === "Lead") {
 			return;
@@ -198,7 +197,7 @@
 			}
 		})
 	}
-});
+};
 
 cur_frm.script_manager.make(erpnext.selling.QuotationController);
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index e3b41e6..b42c615 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -102,14 +102,14 @@
 	}
 });
 
-erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
-	onload: function(doc, dt, dn) {
-		this._super();
-	},
+erpnext.selling.SalesOrderController = class SalesOrderController extends erpnext.selling.SellingController {
+	onload(doc, dt, dn) {
+		super.onload(doc, dt, dn);
+	}
 
-	refresh: function(doc, dt, dn) {
+	refresh(doc, dt, dn) {
 		var me = this;
-		this._super();
+		super.refresh();
 		let allow_delivery = false;
 
 		if (doc.docstatus==1) {
@@ -241,14 +241,14 @@
 		}
 
 		this.order_type(doc);
-	},
+	}
 
 	create_pick_list() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.create_pick_list",
 			frm: this.frm
 		})
-	},
+	}
 
 	make_work_order() {
 		var me = this;
@@ -343,33 +343,33 @@
 				}
 			}
 		});
-	},
+	}
 
-	order_type: function() {
+	order_type() {
 		this.toggle_delivery_date();
-	},
+	}
 
-	tc_name: function() {
+	tc_name() {
 		this.get_terms();
-	},
+	}
 
-	make_material_request: function() {
+	make_material_request() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_material_request",
 			frm: this.frm
 		})
-	},
+	}
 
-	skip_delivery_note: function() {
+	skip_delivery_note() {
 		this.toggle_delivery_date();
-	},
+	}
 
-	toggle_delivery_date: function() {
+	toggle_delivery_date() {
 		this.frm.fields_dict.items.grid.toggle_reqd("delivery_date",
 			(this.frm.doc.order_type == "Sales" && !this.frm.doc.skip_delivery_note));
-	},
+	}
 
-	make_raw_material_request: function() {
+	make_raw_material_request() {
 		var me = this;
 		this.frm.call({
 			doc: this.frm.doc,
@@ -390,9 +390,9 @@
 				}
 			}
 		});
-	},
+	}
 
-	make_raw_material_request_dialog: function(r) {
+	make_raw_material_request_dialog(r) {
 		var fields = [
 			{fieldtype:'Check', fieldname:'include_exploded_items',
 				label: __('Include Exploded Items')},
@@ -447,9 +447,9 @@
 			primary_action_label: __('Create')
 		});
 		d.show();
-	},
+	}
 
-	make_delivery_note_based_on_delivery_date: function() {
+	make_delivery_note_based_on_delivery_date() {
 		var me = this;
 
 		var delivery_dates = [];
@@ -509,51 +509,51 @@
 		} else {
 			this.make_delivery_note();
 		}
-	},
+	}
 
-	make_delivery_note: function() {
+	make_delivery_note() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
 			frm: this.frm
 		})
-	},
+	}
 
-	make_sales_invoice: function() {
+	make_sales_invoice() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_sales_invoice",
 			frm: this.frm
 		})
-	},
+	}
 
-	make_maintenance_schedule: function() {
+	make_maintenance_schedule() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_schedule",
 			frm: this.frm
 		})
-	},
+	}
 
-	make_project: function() {
+	make_project() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_project",
 			frm: this.frm
 		})
-	},
+	}
 
-	make_inter_company_order: function() {
+	make_inter_company_order() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_inter_company_purchase_order",
 			frm: this.frm
 		});
-	},
+	}
 
-	make_maintenance_visit: function() {
+	make_maintenance_visit() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_visit",
 			frm: this.frm
 		})
-	},
+	}
 
-	make_purchase_order: function(){
+	make_purchase_order(){
 		let pending_items = this.frm.doc.items.some((item) =>{
 			let pending_qty = flt(item.stock_qty) - flt(item.ordered_qty);
 			return pending_qty > 0;
@@ -690,9 +690,9 @@
 		dialog.get_field("items_for_po").refresh();
 		dialog.wrapper.find('.grid-heading-row .grid-row-check').click();
 		dialog.show();
-	},
+	}
 
-	hold_sales_order: function(){
+	hold_sales_order(){
 		var me = this;
 		var d = new frappe.ui.Dialog({
 			title: __('Reason for Hold'),
@@ -724,11 +724,11 @@
 			}
 		});
 		d.show();
-	},
-	close_sales_order: function(){
+	}
+	close_sales_order(){
 		this.frm.cscript.update_status("Close", "Closed")
-	},
-	update_status: function(label, status){
+	}
+	update_status(label, status){
 		var doc = this.frm.doc;
 		var me = this;
 		frappe.ui.form.is_saving = true;
@@ -743,5 +743,6 @@
 			}
 		});
 	}
-});
-$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm}));
+};
+
+extend_cscript(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm}));
diff --git a/erpnext/selling/doctype/sms_center/sms_center.js b/erpnext/selling/doctype/sms_center/sms_center.js
index dda2803..974cfc7 100644
--- a/erpnext/selling/doctype/sms_center/sms_center.js
+++ b/erpnext/selling/doctype/sms_center/sms_center.js
@@ -1,7 +1,7 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-$.extend(cur_frm.cscript, {
+extend_cscript(cur_frm.cscript, {
 	message: function () {
 		var total_characters = this.frm.doc.message.length;
 		var total_msg = 1;
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index e3405e0..6db4150 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -7,7 +7,7 @@
 		single_column: true
 	});
 
-	frappe.require('assets/js/point-of-sale.min.js', function() {
+	frappe.require('point-of-sale.bundle.js', function() {
 		wrapper.pos = new erpnext.PointOfSale.Controller(wrapper);
 		window.cur_pos = wrapper.pos;
 	});
@@ -19,4 +19,4 @@
 		wrapper.pos.wrapper.html("");
 		wrapper.pos.check_opening_entry();
 	}
-};
\ No newline at end of file
+};
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 8adf5bf..4f4f1b2 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -56,10 +56,6 @@
 				dialog.fields_dict.balance_details.grid.refresh();
 			});
 		}
-		const pos_profile_query = {
-			query: 'erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query',
-			filters: { company: frappe.defaults.get_default('company') }
-		}
 		const dialog = new frappe.ui.Dialog({
 			title: __('Create POS Opening Entry'),
 			static: true,
@@ -105,6 +101,10 @@
 			primary_action_label: __('Submit')
 		});
 		dialog.show();
+		const pos_profile_query = {
+			query: 'erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query',
+			filters: { company: dialog.fields_dict.company.get_value() }
+		};
 	}
 
 	async prepare_app_defaults(data) {
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index 709fe57..b8a82a9 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -81,13 +81,26 @@
 		const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
 		const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange";
 
+		let qty_to_display = actual_qty;
+
+		if (Math.round(qty_to_display) > 999) {
+			qty_to_display = Math.round(qty_to_display)/1000;
+			qty_to_display = qty_to_display.toFixed(1) + 'K';
+		}
+
 		function get_item_image_html() {
 			if (!me.hide_images && item_image) {
-				return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
+				return `<div class="item-qty-pill">
+							<span class="indicator-pill whitespace-nowrap ${indicator_color}">${qty_to_display}</span>
+						</div>
+						<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
 							<img class="h-full" src="${item_image}" alt="${frappe.get_abbr(item.item_name)}" style="object-fit: cover;">
 						</div>`;
 			} else {
-				return `<div class="item-display abbr">${frappe.get_abbr(item.item_name)}</div>`;
+				return `<div class="item-qty-pill">
+							<span class="indicator-pill whitespace-nowrap ${indicator_color}">${qty_to_display}</span>
+						</div>
+						<div class="item-display abbr">${frappe.get_abbr(item.item_name)}</div>`;
 			}
 		}
 
@@ -95,13 +108,12 @@
 			`<div class="item-wrapper"
 				data-item-code="${escape(item.item_code)}" data-serial-no="${escape(serial_no)}"
 				data-batch-no="${escape(batch_no)}" data-uom="${escape(stock_uom)}"
-				title="Avaiable Qty: ${actual_qty}">
+				title="${item.item_name}">
 
 				${get_item_image_html()}
 
 				<div class="item-detail">
 					<div class="item-name">
-						<span class="indicator ${indicator_color}"></span>
 						${frappe.ellipsis(item.item_name, 18)}
 					</div>
 					<div class="item-rate">${format_currency(item.price_list_rate, item.currency, 0) || 0}</div>
@@ -316,4 +328,4 @@
 	toggle_component(show) {
 		show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
 	}
-};
\ No newline at end of file
+};
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 0428573..eb02867 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -9,15 +9,15 @@
 cur_frm.email_field = "contact_email";
 
 frappe.provide("erpnext.selling");
-erpnext.selling.SellingController = erpnext.TransactionController.extend({
-	setup: function() {
-		this._super();
+erpnext.selling.SellingController = class SellingController extends erpnext.TransactionController {
+	setup() {
+		super.setup();
 		this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate");
 		this.frm.add_fetch("sales_person", "commission_rate", "commission_rate");
-	},
+	}
 
-	onload: function() {
-		this._super();
+	onload() {
+		super.onload();
 		this.setup_queries();
 		this.frm.set_query('shipping_rule', function() {
 			return {
@@ -26,9 +26,9 @@
 				}
 			};
 		});
-	},
+	}
 
-	setup_queries: function() {
+	setup_queries() {
 		var me = this;
 
 		$.each([["customer", "customer"],
@@ -81,10 +81,10 @@
 			});
 		}
 
-	},
+	}
 
-	refresh: function() {
-		this._super();
+	refresh() {
+		super.refresh();
 
 		frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
 
@@ -95,45 +95,45 @@
 			this.frm.toggle_display("packing_list", packing_list_exists ? true : false);
 		}
 		this.toggle_editable_price_list_rate();
-	},
+	}
 
-	customer: function() {
+	customer() {
 		var me = this;
 		erpnext.utils.get_party_details(this.frm, null, null, function() {
 			me.apply_price_list();
 		});
-	},
+	}
 
-	customer_address: function() {
+	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: function() {
+	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");
-	},
+	}
 
-	sales_partner: function() {
+	sales_partner() {
 		this.apply_pricing_rule();
-	},
+	}
 
-	campaign: function() {
+	campaign() {
 		this.apply_pricing_rule();
-	},
+	}
 
-	selling_price_list: function() {
+	selling_price_list() {
 		this.apply_price_list();
 		this.set_dynamic_labels();
-	},
+	}
 
-	discount_percentage: function(doc, cdt, cdn) {
+	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: function(doc, cdt, cdn) {
+	discount_amount(doc, cdt, cdn) {
 
 		if(doc.name === cdn) {
 			return;
@@ -142,9 +142,9 @@
 		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: function(doc, cdt, cdn, field) {
+	apply_discount_on_item(doc, cdt, cdn, field) {
 		var item = frappe.get_doc(cdt, cdn);
 		if(!item.price_list_rate) {
 			item[field] = 0.0;
@@ -152,14 +152,14 @@
 			this.price_list_rate(doc, cdt, cdn);
 		}
 		this.set_gross_profit(item);
-	},
+	}
 
-	commission_rate: function() {
+	commission_rate() {
 		this.calculate_commission();
 		refresh_field("total_commission");
-	},
+	}
 
-	total_commission: function() {
+	total_commission() {
 		if(this.frm.doc.base_net_total) {
 			frappe.model.round_floats_in(this.frm.doc, ["base_net_total", "total_commission"]);
 
@@ -175,9 +175,9 @@
 			this.frm.set_value("commission_rate",
 				flt(this.frm.doc.total_commission * 100.0 / this.frm.doc.base_net_total));
 		}
-	},
+	}
 
-	allocated_percentage: function(doc, cdt, cdn) {
+	allocated_percentage(doc, cdt, cdn) {
 		var sales_person = frappe.get_doc(cdt, cdn);
 		if(sales_person.allocated_percentage) {
 
@@ -193,15 +193,15 @@
 			refresh_field(["allocated_percentage", "allocated_amount", "commission_rate","incentives"], sales_person.name,
 				sales_person.parentfield);
 		}
-	},
+	}
 
-	sales_person: function(doc, cdt, cdn) {
+	sales_person(doc, cdt, cdn) {
 		var row = frappe.get_doc(cdt, cdn);
 		this.calculate_incentive(row);
 		refresh_field("incentives",row.name,row.parentfield);
-	},
+	}
 
-	warehouse: function(doc, cdt, cdn) {
+	warehouse(doc, cdt, cdn) {
 		var me = this;
 		var item = frappe.get_doc(cdt, cdn);
 
@@ -239,18 +239,18 @@
 				});
 			}
 		})
-	},
+	}
 
-	toggle_editable_price_list_rate: function() {
+	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) {
 			df.read_only = 0;
 		}
-	},
+	}
 
-	calculate_commission: function() {
+	calculate_commission() {
 		if(this.frm.fields_dict.commission_rate) {
 			if(this.frm.doc.commission_rate > 100) {
 				var msg = __(frappe.meta.get_label(this.frm.doc.doctype, "commission_rate", this.frm.doc.name)) +
@@ -262,9 +262,9 @@
 			this.frm.doc.total_commission = flt(this.frm.doc.base_net_total * this.frm.doc.commission_rate / 100.0,
 				precision("total_commission"));
 		}
-	},
+	}
 
-	calculate_contribution: function() {
+	calculate_contribution() {
 		var me = this;
 		$.each(this.frm.doc.doctype.sales_team || [], function(i, sales_person) {
 			frappe.model.round_floats_in(sales_person);
@@ -274,18 +274,18 @@
 					precision("allocated_amount", sales_person));
 			}
 		});
-	},
+	}
 
-	calculate_incentive: function(row) {
+	calculate_incentive(row) {
 		if(row.allocated_amount)
 		{
 			row.incentives = flt(
 					row.allocated_amount * row.commission_rate / 100.0,
 					precision("incentives", row));
 		}
-	},
+	}
 
-	batch_no: function(doc, cdt, cdn) {
+	batch_no(doc, cdt, cdn) {
 		var me = this;
 		var item = frappe.get_doc(cdt, cdn);
 
@@ -312,14 +312,14 @@
 				});
 			}
 		})
-	},
+	}
 
-	set_dynamic_labels: function() {
-		this._super();
+	set_dynamic_labels() {
+		super.set_dynamic_labels();
 		this.set_product_bundle_help(this.frm.doc);
-	},
+	}
 
-	set_product_bundle_help: function(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);
@@ -337,9 +337,9 @@
 			}
 		}
 		refresh_field('product_bundle_help');
-	},
+	}
 
-	company_address: function() {
+	company_address() {
 		var me = this;
 		if(this.frm.doc.company_address) {
 			frappe.call({
@@ -354,42 +354,42 @@
 		} else {
 			this.frm.set_value("company_address_display", "");
 		}
-	},
+	}
 
-	conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) {
-	    this._super(doc, cdt, cdn, dont_fetch_price_list_rate);
+	conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate) {
+	    super.conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate);
 		if(frappe.meta.get_docfield(cdt, "stock_qty", cdn) &&
 			in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
 				if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return;
 				this.set_batch_number(cdt, cdn);
 			}
-	},
+	}
 
-	batch_no: function(doc, cdt, cdn) {
-		this._super(doc, cdt, cdn);
-	},
+	batch_no(doc, cdt, cdn) {
+		super.batch_no(doc, cdt, cdn);
+	}
 
-	qty: function(doc, cdt, cdn) {
-		this._super(doc, cdt, cdn);
+	qty(doc, cdt, cdn) {
+		super.qty(doc, cdt, cdn);
 
 		if(in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
 			if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return;
 			this.set_batch_number(cdt, cdn);
 		}
-	},
+	}
 
 	/* Determine appropriate batch number and set it in the form.
 	* @param {string} cdt - Document Doctype
 	* @param {string} cdn - Document name
 	*/
-	set_batch_number: function(cdt, cdn) {
+	set_batch_number(cdt, cdn) {
 		const doc = frappe.get_doc(cdt, cdn);
 		if (doc && doc.has_batch_no && doc.warehouse) {
 			this._set_batch_number(doc);
 		}
-	},
+	}
 
-	_set_batch_number: function(doc) {
+	_set_batch_number(doc) {
 		let args = {'item_code': doc.item_code, 'warehouse': doc.warehouse, 'qty': flt(doc.qty) * flt(doc.conversion_factor)};
 		if (doc.has_serial_no && doc.serial_no) {
 			args['serial_no'] = doc.serial_no
@@ -406,9 +406,9 @@
 				}
 			}
 		});
-	},
+	}
 
-	update_auto_repeat_reference: function(doc) {
+	update_auto_repeat_reference(doc) {
 		if (doc.auto_repeat) {
 			frappe.call({
 				method:"frappe.automation.doctype.auto_repeat.auto_repeat.update_reference",
@@ -426,7 +426,7 @@
 			})
 		}
 	}
-});
+};
 
 frappe.ui.form.on(cur_frm.doctype,"project", function(frm) {
 	if(in_list(["Delivery Note", "Sales Invoice"], frm.doc.doctype)) {
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index c2b5e4f..9957aad 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -169,9 +169,9 @@
 					return;
 				}
 				frappe.call({
-					method: "erpnext.setup.doctype.company.delete_company_transactions.delete_company_transactions",
+					method: "erpnext.setup.doctype.company.company.create_transaction_deletion_request",
 					args: {
-						company_name: data.company_name
+						company: data.company_name
 					},
 					freeze: true,
 					callback: function(r, rt) {
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 64e027d..077538d 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -613,4 +613,13 @@
 	if out:
 		return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(y[1], x[1])))[0][0]
 	else:
-		return None
\ No newline at end of file
+		return None
+
+@frappe.whitelist()
+def create_transaction_deletion_request(company):
+	tdr = frappe.get_doc({
+		'doctype': 'Transaction Deletion Record',
+		'company': company
+	})
+	tdr.insert()
+	tdr.submit()
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
deleted file mode 100644
index 8367a25..0000000
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-from frappe.utils import cint
-from frappe import _
-from frappe.desk.notifications import clear_notifications
-
-import functools
-
-@frappe.whitelist()
-def delete_company_transactions(company_name):
-	frappe.only_for("System Manager")
-	doc = frappe.get_doc("Company", company_name)
-
-	if frappe.session.user != doc.owner and frappe.session.user != 'Administrator':
-		frappe.throw(_("Transactions can only be deleted by the creator of the Company"),
-			frappe.PermissionError)
-
-	delete_bins(company_name)
-	delete_lead_addresses(company_name)
-
-	for doctype in frappe.db.sql_list("""select parent from
-		tabDocField where fieldtype='Link' and options='Company'"""):
-		if doctype not in ("Account", "Cost Center", "Warehouse", "Budget",
-			"Party Account", "Employee", "Sales Taxes and Charges Template",
-			"Purchase Taxes and Charges Template", "POS Profile", "BOM",
-			"Company", "Bank Account", "Item Tax Template", "Mode Of Payment", "Mode of Payment Account",
-			"Item Default", "Customer", "Supplier", "GST Account"):
-				delete_for_doctype(doctype, company_name)
-
-	# reset company values
-	doc.total_monthly_sales = 0
-	doc.sales_monthly_history = None
-	doc.save()
-	# Clear notification counts
-	clear_notifications()
-
-def delete_for_doctype(doctype, company_name):
-	meta = frappe.get_meta(doctype)
-	company_fieldname = meta.get("fields", {"fieldtype": "Link",
-		"options": "Company"})[0].fieldname
-
-	if not meta.issingle:
-		if not meta.istable:
-			# delete communication
-			delete_communications(doctype, company_name, company_fieldname)
-
-			# delete children
-			for df in meta.get_table_fields():
-				frappe.db.sql("""delete from `tab{0}` where parent in
-					(select name from `tab{1}` where `{2}`=%s)""".format(df.options,
-						doctype, company_fieldname), company_name)
-
-		#delete version log
-		frappe.db.sql("""delete from `tabVersion` where ref_doctype=%s and docname in
-			(select name from `tab{0}` where `{1}`=%s)""".format(doctype,
-				company_fieldname), (doctype, company_name))
-
-		# delete parent
-		frappe.db.sql("""delete from `tab{0}`
-			where {1}= %s """.format(doctype, company_fieldname), company_name)
-
-		# reset series
-		naming_series = meta.get_field("naming_series")
-		if naming_series and naming_series.options:
-			prefixes = sorted(naming_series.options.split("\n"),
-				key=functools.cmp_to_key(lambda a, b: len(b) - len(a)))
-
-			for prefix in prefixes:
-				if prefix:
-					last = frappe.db.sql("""select max(name) from `tab{0}`
-						where name like %s""".format(doctype), prefix + "%")
-					if last and last[0][0]:
-						last = cint(last[0][0].replace(prefix, ""))
-					else:
-						last = 0
-
-					frappe.db.sql("""update tabSeries set current = %s
-						where name=%s""", (last, prefix))
-
-def delete_bins(company_name):
-	frappe.db.sql("""delete from tabBin where warehouse in
-			(select name from tabWarehouse where company=%s)""", company_name)
-
-def delete_lead_addresses(company_name):
-	"""Delete addresses to which leads are linked"""
-	leads = frappe.get_all("Lead", filters={"company": company_name})
-	leads = [ "'%s'"%row.get("name") for row in leads ]
-	addresses = []
-	if leads:
-		addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
-			in ({leads})""".format(leads=",".join(leads)))
-
-		if addresses:
-			addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
-
-			frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
-				name not in (select distinct dl1.parent from `tabDynamic Link` dl1
-				inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
-				and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses)))
-
-			frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead'
-				and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads)))
-
-		frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
-
-def delete_communications(doctype, company_name, company_fieldname):
-		reference_docs = frappe.get_all(doctype, filters={company_fieldname:company_name})
-		reference_doc_names = [r.name for r in reference_docs]
-
-		communications = frappe.get_all("Communication", filters={"reference_doctype":doctype,"reference_name":["in", reference_doc_names]})
-		communication_names = [c.name for c in communications]
-
-		frappe.delete_doc("Communication", communication_names, ignore_permissions=True)
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index 29f6c37..e1c803a 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -86,15 +86,6 @@
 					self.delete_mode_of_payment(template)
 					frappe.delete_doc("Company", template)
 
-	def test_delete_communication(self):
-		from erpnext.setup.doctype.company.delete_company_transactions import delete_communications
-		company = create_child_company()
-		lead = create_test_lead_in_company(company)
-		communication = create_company_communication("Lead", lead)
-		delete_communications("Lead", "Test Company", "company")
-		self.assertFalse(frappe.db.exists("Communcation", communication))
-		self.assertFalse(frappe.db.exists({"doctype":"Comunication Link", "link_name": communication}))
-
 	def delete_mode_of_payment(self, company):
 		frappe.db.sql(""" delete from `tabMode of Payment Account`
 			where company =%s """, (company))
diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.js b/erpnext/setup/doctype/currency_exchange/currency_exchange.js
index a8ea55c..85036a1 100644
--- a/erpnext/setup/doctype/currency_exchange/currency_exchange.js
+++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.js
@@ -1,30 +1,30 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-$.extend(cur_frm.cscript, {
+extend_cscript(cur_frm.cscript, {
 	onload: function() {
 		if(cur_frm.doc.__islocal) {
 			cur_frm.set_value("to_currency", frappe.defaults.get_global_default("currency"));
 		}
 	},
-	
+
 	refresh: function() {
 		cur_frm.cscript.set_exchange_rate_label();
 	},
-	
+
 	from_currency: function() {
 		cur_frm.cscript.set_exchange_rate_label();
 	},
-	
+
 	to_currency: function() {
 		cur_frm.cscript.set_exchange_rate_label();
 	},
-	
+
 	set_exchange_rate_label: function() {
 		if(cur_frm.doc.from_currency && cur_frm.doc.to_currency) {
 			var default_label = __(frappe.meta.docfield_map[cur_frm.doctype]["exchange_rate"].label);
-			cur_frm.fields_dict.exchange_rate.set_label(default_label + 
+			cur_frm.fields_dict.exchange_rate.set_label(default_label +
 				repl(" (1 %(from_currency)s = [?] %(to_currency)s)", cur_frm.doc));
 		}
 	}
-});
\ No newline at end of file
+});
diff --git a/erpnext/setup/doctype/transaction_deletion_record/__init__.py b/erpnext/setup/doctype/transaction_deletion_record/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/__init__.py
diff --git a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
new file mode 100644
index 0000000..bbe6836
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestTransactionDeletionRecord(unittest.TestCase):
+	def setUp(self):
+		create_company('Dunder Mifflin Paper Co')
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	def test_doctypes_contain_company_field(self):
+		tdr = create_transaction_deletion_request('Dunder Mifflin Paper Co')
+		for doctype in tdr.doctypes:
+			contains_company = False
+			doctype_fields = frappe.get_meta(doctype.doctype_name).as_dict()['fields']
+			for doctype_field in doctype_fields:
+				if doctype_field['fieldtype'] == 'Link' and doctype_field['options'] == 'Company':
+					contains_company = True
+					break
+			self.assertTrue(contains_company)
+	
+	def test_no_of_docs_is_correct(self):
+		for i in range(5):
+			create_task('Dunder Mifflin Paper Co')
+		tdr = create_transaction_deletion_request('Dunder Mifflin Paper Co')
+		for doctype in tdr.doctypes:
+			if doctype.doctype_name == 'Task':
+				self.assertEqual(doctype.no_of_docs, 5)
+
+	def test_deletion_is_successful(self):
+		create_task('Dunder Mifflin Paper Co')
+		create_transaction_deletion_request('Dunder Mifflin Paper Co')
+		tasks_containing_company = frappe.get_all('Task',
+		filters = {
+			'company' : 'Dunder Mifflin Paper Co'
+		})
+		self.assertEqual(tasks_containing_company, [])
+		
+def create_company(company_name):
+	company = frappe.get_doc({
+		'doctype': 'Company',
+		'company_name': company_name,
+		'default_currency': 'INR'
+	})		
+	company.insert(ignore_if_duplicate = True)
+
+def create_transaction_deletion_request(company):
+	tdr = frappe.get_doc({
+		'doctype': 'Transaction Deletion Record',
+		'company': company
+	})
+	tdr.insert()
+	tdr.submit()
+	return tdr
+
+
+def create_task(company):
+	task = frappe.get_doc({
+		'doctype': 'Task',
+		'company': company,
+		'subject': 'Delete'
+	})
+	task.insert()
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js
new file mode 100644
index 0000000..20caa15
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Transaction Deletion Record', {
+	onload: function(frm) {
+		if (frm.doc.docstatus == 0) {
+			let doctypes_to_be_ignored_array;	
+			frappe.call({
+				method: 'erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.get_doctypes_to_be_ignored',
+				callback: function(r) {
+					doctypes_to_be_ignored_array = r.message;
+					populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm);
+					frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false);
+					frm.refresh_field('doctypes_to_be_ignored');
+				}
+			});
+		}
+
+		frm.get_field('doctypes_to_be_ignored').grid.cannot_add_rows = true;
+		frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false);
+		frm.refresh_field('doctypes_to_be_ignored');
+	},
+
+	refresh: function(frm) {
+		frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false);
+		frm.refresh_field('doctypes_to_be_ignored');
+	}
+	
+});
+
+function populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm) {
+	if (!(frm.doc.doctypes_to_be_ignored)) {
+		var i;
+		for (i = 0; i < doctypes_to_be_ignored_array.length; i++) {     
+			frm.add_child('doctypes_to_be_ignored', {
+				doctype_name: doctypes_to_be_ignored_array[i]					
+			});
+		}
+	}
+}
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
new file mode 100644
index 0000000..9313f95
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
@@ -0,0 +1,79 @@
+{
+ "actions": [],
+ "autoname": "TDL.####",
+ "creation": "2021-04-06 20:17:18.404716",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "doctypes",
+  "doctypes_to_be_ignored",
+  "amended_from",
+  "status"
+ ],
+ "fields": [
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "doctypes",
+   "fieldtype": "Table",
+   "label": "Summary",
+   "options": "Transaction Deletion Record Item",
+   "read_only": 1
+  },
+  {
+   "fieldname": "doctypes_to_be_ignored",
+   "fieldtype": "Table",
+   "label": "Excluded DocTypes",
+   "options": "Transaction Deletion Record Item"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Transaction Deletion Record",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "label": "Status",
+   "options": "Draft\nCompleted"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-05-08 23:13:48.049879",
+ "modified_by": "Administrator",
+ "module": "Setup",
+ "name": "Transaction Deletion Record",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
new file mode 100644
index 0000000..38f8de7
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.utils import cint
+import frappe
+from frappe.model.document import Document
+from frappe import _
+from frappe.desk.notifications import clear_notifications
+
+class TransactionDeletionRecord(Document):
+	def validate(self):
+		frappe.only_for('System Manager')
+		company_obj = frappe.get_doc('Company', self.company)
+		if frappe.session.user != company_obj.owner and frappe.session.user != 'Administrator':
+			frappe.throw(_('Transactions can only be deleted by the creator of the Company or the Administrator.'), 
+				frappe.PermissionError)
+		doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
+		for doctype in self.doctypes_to_be_ignored:
+			if doctype.doctype_name not in doctypes_to_be_ignored_list:
+				frappe.throw(_("DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it. "), title=_("Not Allowed"))
+
+	def before_submit(self):
+		if not self.doctypes_to_be_ignored:
+			self.populate_doctypes_to_be_ignored_table()
+
+		self.delete_bins()
+		self.delete_lead_addresses()
+		
+		company_obj = frappe.get_doc('Company', self.company)
+		# reset company values
+		company_obj.total_monthly_sales = 0
+		company_obj.sales_monthly_history = None
+		company_obj.save()
+		# Clear notification counts
+		clear_notifications()
+
+		singles = frappe.get_all('DocType', filters = {'issingle': 1}, pluck = 'name')
+		tables = frappe.get_all('DocType', filters = {'istable': 1}, pluck = 'name')
+		doctypes_to_be_ignored_list = singles
+		for doctype in self.doctypes_to_be_ignored:
+			doctypes_to_be_ignored_list.append(doctype.doctype_name)
+
+		docfields = frappe.get_all('DocField', 
+			filters = {
+				'fieldtype': 'Link', 
+				'options': 'Company',
+				'parent': ['not in', doctypes_to_be_ignored_list]},
+			fields=['parent', 'fieldname'])
+	
+		for docfield in docfields:
+			if docfield['parent'] != self.doctype:
+				no_of_docs = frappe.db.count(docfield['parent'], {
+							docfield['fieldname'] : self.company
+						})
+
+				if no_of_docs > 0:
+					self.delete_version_log(docfield['parent'], docfield['fieldname'])
+					self.delete_communications(docfield['parent'], docfield['fieldname'])
+
+					# populate DocTypes table
+					if docfield['parent'] not in tables:
+						self.append('doctypes', {
+							'doctype_name' : docfield['parent'],
+							'no_of_docs' : no_of_docs
+						})
+
+					# delete the docs linked with the specified company
+					frappe.db.delete(docfield['parent'], {
+						docfield['fieldname'] : self.company
+					})
+
+					naming_series = frappe.db.get_value('DocType', docfield['parent'], 'autoname')
+					if naming_series:
+						if '#' in naming_series:
+							self.update_naming_series(naming_series, docfield['parent'])	
+
+	def populate_doctypes_to_be_ignored_table(self):		
+		doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
+		for doctype in doctypes_to_be_ignored_list:
+			self.append('doctypes_to_be_ignored', {
+						'doctype_name' : doctype
+					})
+
+	def update_naming_series(self, naming_series, doctype_name):
+		if '.' in naming_series:
+			prefix, hashes = naming_series.rsplit('.', 1)
+		else:
+			prefix, hashes = naming_series.rsplit('{', 1)
+		last = frappe.db.sql("""select max(name) from `tab{0}`
+						where name like %s""".format(doctype_name), prefix + '%')
+		if last and last[0][0]:
+			last = cint(last[0][0].replace(prefix, ''))
+		else:
+			last = 0
+
+		frappe.db.sql("""update tabSeries set current = %s where name=%s""", (last, prefix))
+
+	def delete_version_log(self, doctype, company_fieldname):
+		frappe.db.sql("""delete from `tabVersion` where ref_doctype=%s and docname in
+			(select name from `tab{0}` where `{1}`=%s)""".format(doctype,
+				company_fieldname), (doctype, self.company))
+
+	def delete_communications(self, doctype, company_fieldname):
+		reference_docs = frappe.get_all(doctype, filters={company_fieldname:self.company})
+		reference_doc_names = [r.name for r in reference_docs]
+
+		communications = frappe.get_all('Communication', filters={'reference_doctype':doctype,'reference_name':['in', reference_doc_names]})
+		communication_names = [c.name for c in communications]
+
+		frappe.delete_doc('Communication', communication_names, ignore_permissions=True)
+
+	def delete_bins(self):
+		frappe.db.sql("""delete from tabBin where warehouse in
+				(select name from tabWarehouse where company=%s)""", self.company)
+
+	def delete_lead_addresses(self):
+		"""Delete addresses to which leads are linked"""
+		leads = frappe.get_all('Lead', filters={'company': self.company})
+		leads = ["'%s'" % row.get("name") for row in leads]
+		addresses = []
+		if leads:
+			addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
+				in ({leads})""".format(leads=",".join(leads)))
+
+			if addresses:
+				addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
+
+				frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
+					name not in (select distinct dl1.parent from `tabDynamic Link` dl1
+					inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
+					and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses)))
+
+				frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead'
+					and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads)))
+
+			frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
+
+@frappe.whitelist()
+def get_doctypes_to_be_ignored():
+	doctypes_to_be_ignored_list = ['Account', 'Cost Center', 'Warehouse', 'Budget',
+		'Party Account', 'Employee', 'Sales Taxes and Charges Template',
+		'Purchase Taxes and Charges Template', 'POS Profile', 'BOM',
+		'Company', 'Bank Account', 'Item Tax Template', 'Mode of Payment',
+		'Item Default', 'Customer', 'Supplier', 'GST Account']
+	return doctypes_to_be_ignored_list
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js
new file mode 100644
index 0000000..d7175dd
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+frappe.listview_settings['Transaction Deletion Record'] = {
+	get_indicator: function(doc) {
+		if (doc.docstatus == 0) {
+			return [__("Draft"), "red"];
+		} else {
+			return [__("Completed"), "green"];
+		}
+	}
+};
\ No newline at end of file
diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/__init__.py b/erpnext/setup/doctype/transaction_deletion_record_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record_item/__init__.py
diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json
new file mode 100644
index 0000000..be0be94
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json
@@ -0,0 +1,39 @@
+{
+ "actions": [],
+ "creation": "2021-04-07 07:34:00.124124",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "doctype_name",
+  "no_of_docs"
+ ],
+ "fields": [
+  {
+   "fieldname": "doctype_name",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "DocType",
+   "options": "DocType",
+   "reqd": 1
+  },
+  {
+   "fieldname": "no_of_docs",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Number of Docs"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-08 23:10:46.166744",
+ "modified_by": "Administrator",
+ "module": "Setup",
+ "name": "Transaction Deletion Record Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
new file mode 100644
index 0000000..2176cb1
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class TransactionDeletionRecordItem(Document):
+	pass
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index f21d55f..7ae81d7 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -428,7 +428,6 @@
 	frappe.local.flags.ignore_update_nsm = True
 	make_records(records[1:])
 	frappe.local.flags.ignore_update_nsm = False
-	
 	rebuild_tree("Department", "parent_department")
 
 
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index 933ca8a..dbf9901 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -1,11 +1,11 @@
 frappe.provide('erpnext.stock');
 
-erpnext.stock.ItemDashboard = Class.extend({
-	init: function (opts) {
+erpnext.stock.ItemDashboard = class ItemDashboard {
+	constructor(opts) {
 		$.extend(this, opts);
 		this.make();
-	},
-	make: function () {
+	}
+	make() {
 		var me = this;
 		this.start = 0;
 		if (!this.sort_by) {
@@ -79,9 +79,9 @@
 			me.refresh();
 		});
 
-	},
-	refresh: function () {
-		if (this.before_refresh) {
+	}
+	refresh() {
+		if(this.before_refresh) {
 			this.before_refresh();
 		}
 
@@ -104,9 +104,9 @@
 				me.render(r.message);
 			}
 		});
-	},
-	render: function (data) {
-		if (this.start === 0) {
+	}
+	render(data) {
+		if (this.start===0) {
 			this.max_count = 0;
 			this.result.empty();
 		}
@@ -141,11 +141,11 @@
 			$(`<div class='text-muted' style='margin: 20px 5px;'>
 				${message} </div>`).appendTo(this.result);
 		}
-	},
+	}
 
-	get_item_dashboard_data: function (data, max_count, show_item) {
-		if (!max_count) max_count = 0;
-		if (!data) data = [];
+	get_item_dashboard_data(data, max_count, show_item) {
+		if(!max_count) max_count = 0;
+		if(!data) data = [];
 
 		data.forEach(function (d) {
 			d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract;
@@ -170,9 +170,9 @@
 			can_write: can_write,
 			show_item: show_item || false
 		};
-	},
+	}
 
-	get_capacity_dashboard_data: function (data) {
+	get_capacity_dashboard_data(data) {
 		if (!data) data = [];
 
 		data.forEach(function (d) {
@@ -189,7 +189,7 @@
 			can_write: can_write,
 		};
 	}
-});
+};
 
 erpnext.stock.move_item = function (item, source, target, actual_qty, rate, callback) {
 	var dialog = new frappe.ui.Dialog({
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 334bdea..c3803f1 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -123,17 +123,17 @@
 	}
 });
 
-erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend({
-	setup: function(doc) {
+erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends erpnext.selling.SellingController {
+	setup(doc) {
 		this.setup_posting_date_time_check();
-		this._super(doc);
+		super.setup(doc);
 		this.frm.make_methods = {
 			'Delivery Trip': this.make_delivery_trip,
 		};
-	},
-	refresh: function(doc, dt, dn) {
+	}
+	refresh(doc, dt, dn) {
 		var me = this;
-		this._super();
+		super.refresh();
 		if ((!doc.is_return) && (doc.status!="Closed" || this.frm.is_new())) {
 			if (this.frm.doc.docstatus===0) {
 				this.frm.add_custom_button(__('Sales Order'),
@@ -231,64 +231,64 @@
 				erpnext.utils.make_subscription(doc.doctype, doc.name)
 			}, __('Create'))
 		}
-	},
+	}
 
-	make_shipment: function() {
+	make_shipment() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.stock.doctype.delivery_note.delivery_note.make_shipment",
 			frm: this.frm
 		})
-	},
+	}
 
-	make_sales_invoice: function() {
+	make_sales_invoice() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice",
 			frm: this.frm
 		})
-	},
+	}
 
-	make_installation_note: function() {
+	make_installation_note() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.stock.doctype.delivery_note.delivery_note.make_installation_note",
 			frm: this.frm
 		});
-	},
+	}
 
-	make_sales_return: function() {
+	make_sales_return() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_return",
 			frm: this.frm
 		})
-	},
+	}
 
-	make_delivery_trip: function() {
+	make_delivery_trip() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip",
 			frm: cur_frm
 		})
-	},
+	}
 
-	tc_name: function() {
+	tc_name() {
 		this.get_terms();
-	},
+	}
 
-	items_on_form_rendered: function(doc, grid_row) {
-		erpnext.setup_serial_no();
-	},
+	items_on_form_rendered(doc, grid_row) {
+		erpnext.setup_serial_or_batch_no();
+	}
 
-	packed_items_on_form_rendered: function(doc, grid_row) {
-		erpnext.setup_serial_no();
-	},
+	packed_items_on_form_rendered(doc, grid_row) {
+		erpnext.setup_serial_or_batch_no();
+	}
 
-	close_delivery_note: function(doc){
+	close_delivery_note(doc){
 		this.update_status("Closed")
-	},
+	}
 
-	reopen_delivery_note : function() {
+	reopen_delivery_note() {
 		this.update_status("Submitted")
-	},
+	}
 
-	update_status: function(status) {
+	update_status(status) {
 		var me = this;
 		frappe.ui.form.is_saving = true;
 		frappe.call({
@@ -302,10 +302,10 @@
 				frappe.ui.form.is_saving = false;
 			}
 		})
-	},
-});
+	}
+};
 
-$.extend(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm}));
 
 frappe.ui.form.on('Delivery Note', {
 	setup: function(frm) {
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
index a6fbb66..68cba29 100755
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
@@ -41,6 +41,15 @@
 	},
 
 	refresh: function (frm) {
+		if (frm.doc.docstatus == 1 && frm.doc.employee) {
+			frm.add_custom_button(__('Expense Claim'), function() {
+				frappe.model.open_mapped_doc({
+					method: 'erpnext.stock.doctype.delivery_trip.delivery_trip.make_expense_claim',
+					frm: cur_frm,
+				});
+			}, __("Create"));
+		}
+
 		if (frm.doc.docstatus == 1 && frm.doc.delivery_stops.length > 0) {
 			frm.add_custom_button(__("Notify Customers via Email"), function () {
 				frm.trigger('notify_customers');
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.json b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
index 879901f..11b71c2 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.json
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
@@ -21,6 +21,7 @@
   "column_break_4",
   "vehicle",
   "departure_time",
+  "employee",
   "delivery_service_stops",
   "delivery_stops",
   "calculate_arrival_time",
@@ -176,11 +177,19 @@
    "fieldtype": "Data",
    "label": "Driver Email",
    "read_only": 1
+  },
+  {
+   "fetch_from": "driver.employee",
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "label": "Employee",
+   "options": "Employee",
+   "read_only": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-01-26 22:37:14.824021",
+ "modified": "2021-04-30 21:21:36.610142",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Trip",
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
index de85bc3..81e7301 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
@@ -11,6 +11,7 @@
 from frappe.contacts.doctype.address.address import get_address_display
 from frappe.model.document import Document
 from frappe.utils import cint, get_datetime, get_link_to_form
+from frappe.model.mapper import get_mapped_doc
 
 
 class DeliveryTrip(Document):
@@ -394,3 +395,15 @@
 	employee = frappe.db.get_value("Driver", driver, "employee")
 	email = frappe.db.get_value("Employee", employee, "prefered_email")
 	return {"email": email}
+
+@frappe.whitelist()
+def make_expense_claim(source_name, target_doc=None):
+	doc = get_mapped_doc("Delivery Trip", source_name,
+		{"Delivery Trip": {
+			"doctype": "Expense Claim",
+			"field_map": {
+				"name" : "delivery_trip"
+			}
+		}}, target_doc)
+
+	return doc
\ No newline at end of file
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
index eeea6da..1e71603 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -7,7 +7,7 @@
 
 import erpnext
 import frappe
-from erpnext.stock.doctype.delivery_trip.delivery_trip import get_contact_and_address, notify_customers
+from erpnext.stock.doctype.delivery_trip.delivery_trip import get_contact_and_address, notify_customers, make_expense_claim
 from erpnext.tests.utils import create_test_contact_and_address
 from frappe.utils import add_days, flt, now_datetime, nowdate
 
@@ -28,6 +28,10 @@
 		frappe.db.sql("delete from `tabEmail Template`")
 		frappe.db.sql("delete from `tabDelivery Trip`")
 
+	def test_expense_claim_fields_are_fetched_properly(self):
+		expense_claim = make_expense_claim(self.delivery_trip.name)
+		self.assertEqual(self.delivery_trip.name, expense_claim.delivery_trip)
+
 	def test_delivery_trip_notify_customers(self):
 		notify_customers(delivery_trip=self.delivery_trip.name)
 		self.delivery_trip.load_from_db()
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 8aec893..45e3c21 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -380,7 +380,7 @@
 
 		// Show Stock Levels only if is_stock_item
 		if (frm.doc.is_stock_item) {
-			frappe.require('assets/js/item-dashboard.min.js', function() {
+			frappe.require('item-dashboard.bundle.js', function() {
 				const section = frm.dashboard.add_section('', __("Stock Levels"));
 				erpnext.item.item_dashboard = new erpnext.stock.ItemDashboard({
 					parent: section,
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 1abbc35..433f78a 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
@@ -5,8 +5,8 @@
 
 frappe.provide("erpnext.stock");
 
-erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
-	setup: function() {
+erpnext.stock.LandedCostVoucher = class LandedCostVoucher extends erpnext.stock.StockController {
+	setup() {
 		var me = this;
 		this.frm.fields_dict.purchase_receipts.grid.get_field('receipt_document').get_query =
 			function (doc, cdt, cdn) {
@@ -30,9 +30,9 @@
 		this.frm.add_fetch("receipt_document", "supplier", "supplier");
 		this.frm.add_fetch("receipt_document", "posting_date", "posting_date");
 		this.frm.add_fetch("receipt_document", "base_grand_total", "grand_total");
-	},
+	}
 
-	refresh: function() {
+	refresh() {
 		var help_content =
 			`<br><br>
 			<table class="table table-bordered" style="background-color: #f9f9f9;">
@@ -67,9 +67,9 @@
 			let company_currency = frappe.get_doc(":Company", this.frm.doc.company).default_currency;
 			this.frm.set_currency_labels(["total_taxes_and_charges"], company_currency);
 		}
-	},
+	}
 
-	get_items_from_purchase_receipts: function() {
+	get_items_from_purchase_receipts() {
 		var me = this;
 		if(!this.frm.doc.purchase_receipts.length) {
 			frappe.msgprint(__("Please enter Purchase Receipt first"));
@@ -82,22 +82,22 @@
 				}
 			});
 		}
-	},
+	}
 
-	amount: function(frm) {
+	amount(frm) {
 		this.set_total_taxes_and_charges();
 		this.set_applicable_charges_for_item();
-	},
+	}
 
-	set_total_taxes_and_charges: function() {
+	set_total_taxes_and_charges() {
 		var total_taxes_and_charges = 0.0;
 		$.each(this.frm.doc.taxes || [], function(i, d) {
 			total_taxes_and_charges += flt(d.base_amount);
 		});
 		this.frm.set_value("total_taxes_and_charges", total_taxes_and_charges);
-	},
+	}
 
-	set_applicable_charges_for_item: function() {
+	set_applicable_charges_for_item() {
 		var me = this;
 
 		if(this.frm.doc.taxes.length) {
@@ -123,15 +123,15 @@
 				refresh_field("items");
 			}
 		}
-	},
-	distribute_charges_based_on: function (frm) {
+	}
+	distribute_charges_based_on (frm) {
 		this.set_applicable_charges_for_item();
-	},
+	}
 
-	items_remove: () => {
+	items_remove() {
 		this.trigger('set_applicable_charges_for_item');
 	}
-});
+};
 
 cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher);
 
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 92c8d21..6585e1c 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -407,33 +407,33 @@
 	}
 });
 
-erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.extend({
-	tc_name: function() {
+erpnext.buying.MaterialRequestController = class MaterialRequestController extends erpnext.buying.BuyingController {
+	tc_name() {
 		this.get_terms();
-	},
+	}
 
-	item_code: function() {
+	item_code() {
 		// to override item code trigger from transaction.js
-	},
+	}
 
-	validate_company_and_party: function() {
+	validate_company_and_party() {
 		return true;
-	},
+	}
 
-	calculate_taxes_and_totals: function() {
+	calculate_taxes_and_totals() {
 		return;
-	},
+	}
 
-	validate: function() {
+	validate() {
 		set_schedule_date(this.frm);
-	},
+	}
 
-	onload: function(doc, cdt, cdn) {
+	onload(doc, cdt, cdn) {
 		this.frm.set_query("item_code", "items", function() {
 			if (doc.material_request_type == "Customer Provided") {
 				return{
 					query: "erpnext.controllers.queries.item_query",
-					filters:{ 
+					filters:{
 						'customer': me.frm.doc.customer,
 						'is_stock_item':1
 					}
@@ -450,9 +450,9 @@
 				}
 			}
 		});
-	},
+	}
 
-	items_add: function(doc, cdt, cdn) {
+	items_add(doc, cdt, cdn) {
 		var row = frappe.get_doc(cdt, cdn);
 		if(doc.schedule_date) {
 			row.schedule_date = doc.schedule_date;
@@ -460,19 +460,19 @@
 		} else {
 			this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
 		}
-	},
+	}
 
-	items_on_form_rendered: function() {
-		set_schedule_date(this.frm);
-	},
-
-	schedule_date: function() {
+	items_on_form_rendered() {
 		set_schedule_date(this.frm);
 	}
-});
+
+	schedule_date() {
+		set_schedule_date(this.frm);
+	}
+};
 
 // for backward compatibility: combine new and previous states
-$.extend(cur_frm.cscript, new erpnext.buying.MaterialRequestController({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.buying.MaterialRequestController({frm: cur_frm}));
 
 function set_schedule_date(frm) {
 	if(frm.doc.schedule_date){
diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json
index 8d7b238..4e2d9e6 100644
--- a/erpnext/stock/doctype/material_request/material_request.json
+++ b/erpnext/stock/doctype/material_request/material_request.json
@@ -181,7 +181,7 @@
    "no_copy": 1,
    "oldfieldname": "status",
    "oldfieldtype": "Select",
-   "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nOrdered\nIssued\nTransferred\nReceived",
+   "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nPartially Received\nOrdered\nIssued\nTransferred\nReceived",
    "print_hide": 1,
    "print_width": "100px",
    "read_only": 1,
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index befdad9..0182ed5 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -114,15 +114,15 @@
 	}
 });
 
-erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({
-	setup: function(doc) {
+erpnext.stock.PurchaseReceiptController = class PurchaseReceiptController extends erpnext.buying.BuyingController {
+	setup(doc) {
 		this.setup_posting_date_time_check();
-		this._super(doc);
-	},
+		super.setup(doc);
+	}
 
-	refresh: function() {
+	refresh() {
 		var me = this;
-		this._super();
+		super.refresh();
 		if(this.frm.doc.docstatus > 0) {
 			this.show_stock_ledger();
 			//removed for temporary
@@ -201,31 +201,31 @@
 		}
 
 		this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
-	},
+	}
 
-	make_purchase_invoice: function() {
+	make_purchase_invoice() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice",
 			frm: cur_frm
 		})
-	},
+	}
 
-	make_purchase_return: function() {
+	make_purchase_return() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_return",
 			frm: cur_frm
 		})
-	},
+	}
 
-	close_purchase_receipt: function() {
+	close_purchase_receipt() {
 		cur_frm.cscript.update_status("Closed");
-	},
+	}
 
-	reopen_purchase_receipt: function() {
+	reopen_purchase_receipt() {
 		cur_frm.cscript.update_status("Submitted");
-	},
+	}
 
-	make_retention_stock_entry: function() {
+	make_retention_stock_entry() {
 		frappe.call({
 			method: "erpnext.stock.doctype.stock_entry.stock_entry.move_sample_to_retention_warehouse",
 			args:{
@@ -242,16 +242,16 @@
 				}
 			}
 		});
-	},
+	}
 
-	apply_putaway_rule: function() {
+	apply_putaway_rule() {
 		if (this.frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(this.frm);
 	}
 
-});
+};
 
 // for backward compatibility: combine new and previous states
-$.extend(cur_frm.cscript, new erpnext.stock.PurchaseReceiptController({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.stock.PurchaseReceiptController({frm: cur_frm}));
 
 cur_frm.cscript.update_status = function(status) {
 	frappe.ui.form.is_saving = true;
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 868e553..5095a80 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -13,8 +13,9 @@
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
 from erpnext.stock.doctype.item.test_item import make_item
 from six import iteritems
+from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
-
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 class TestPurchaseReceipt(unittest.TestCase):
 	def setUp(self):
@@ -144,6 +145,62 @@
 		self.assertFalse(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name}))
 		self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no}))
 
+	def test_duplicate_serial_nos(self):
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+
+		item = frappe.db.exists("Item", {'item_name': 'Test Serialized Item 123'})
+		if not item:
+			item = create_item("Test Serialized Item 123")
+			item.has_serial_no = 1
+			item.serial_no_series = "TSI123-.####"
+			item.save()
+		else:
+			item = frappe.get_doc("Item", {'item_name': 'Test Serialized Item 123'})
+
+		# First make purchase receipt
+		pr = make_purchase_receipt(item_code=item.name, qty=2, rate=500)
+		pr.load_from_db()
+
+		serial_nos = frappe.db.get_value('Stock Ledger Entry',
+			{'voucher_type': 'Purchase Receipt', 'voucher_no': pr.name, 'item_code': item.name}, 'serial_no')
+
+		serial_nos = get_serial_nos(serial_nos)
+
+		self.assertEquals(get_serial_nos(pr.items[0].serial_no), serial_nos)
+
+		# Then tried to receive same serial nos in difference company
+		pr_different_company = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
+			serial_no='\n'.join(serial_nos), company='_Test Company 1', do_not_submit=True,
+			warehouse = 'Stores - _TC1')
+
+		self.assertRaises(SerialNoDuplicateError, pr_different_company.submit)
+
+		# Then made delivery note to remove the serial nos from stock
+		dn = create_delivery_note(item_code=item.name, qty=2, rate = 1500, serial_no='\n'.join(serial_nos))
+		dn.load_from_db()
+		self.assertEquals(get_serial_nos(dn.items[0].serial_no), serial_nos)
+
+		posting_date = add_days(today(), -3)
+
+		# Try to receive same serial nos again in the same company with backdated.
+		pr1 = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
+			posting_date=posting_date, serial_no='\n'.join(serial_nos), do_not_submit=True)
+
+		self.assertRaises(SerialNoExistsInFutureTransaction, pr1.submit)
+
+		# Try to receive same serial nos with different company with backdated.
+		pr2 = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
+			posting_date=posting_date, serial_no='\n'.join(serial_nos), company='_Test Company 1', do_not_submit=True,
+			warehouse = 'Stores - _TC1')
+
+		self.assertRaises(SerialNoExistsInFutureTransaction, pr2.submit)
+
+		# Receive the same serial nos after the delivery note posting date and time
+		make_purchase_receipt(item_code=item.name, qty=2, rate=500, serial_no='\n'.join(serial_nos))
+
+		# Raise the error for backdated deliver note entry cancel
+		self.assertRaises(SerialNoExistsInFutureTransaction, dn.cancel)
+
 	def test_purchase_receipt_gl_entry(self):
 		pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
 			warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
@@ -565,30 +622,6 @@
 
 		new_pr_doc.cancel()
 
-	def test_not_accept_duplicate_serial_no(self):
-		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
-
-		item_code = frappe.db.get_value('Item', {'has_serial_no': 1, 'is_fixed_asset': 0, "has_batch_no": 0})
-		if not item_code:
-			item = make_item("Test Serial Item 1", dict(has_serial_no=1, has_batch_no=0))
-			item_code = item.name
-
-		serial_no = random_string(5)
-		pr1 = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no)
-		dn = create_delivery_note(item_code=item_code, qty=1, serial_no=serial_no)
-
-		pr2 = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no, do_not_submit=True)
-		self.assertRaises(SerialNoDuplicateError, pr2.submit)
-
-		se = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=1,
-			serial_no=serial_no, basic_rate=100, do_not_submit=True)
-		se.submit()
-
-		se.cancel()
-		dn.cancel()
-		pr1.cancel()
-
 	def test_auto_asset_creation(self):
 		asset_item = "Test Asset Item"
 
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index a7dfc9e..56b046a 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -27,10 +27,11 @@
 		dn.reload()
 		self.assertRaises(QualityInspectionRejectedError, dn.submit)
 
-		frappe.db.set_value("Quality Inspection Reading", {"parent": qa.name}, "status", "Accepted")
+		frappe.db.set_value("Quality Inspection", qa.name, "status", "Accepted")
 		dn.reload()
 		dn.submit()
 
+		qa.reload()
 		qa.cancel()
 		dn.reload()
 		dn.cancel()
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 3f83780..63e9646 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -4,8 +4,9 @@
 
 from __future__ import unicode_literals
 import frappe, erpnext
+from rq.timeouts import JobTimeoutException
 from frappe.model.document import Document
-from frappe.utils import cint, get_link_to_form, add_to_date, today
+from frappe.utils import cint, get_link_to_form, add_to_date, now, today, time_diff_in_hours
 from erpnext.stock.stock_ledger import repost_future_sle
 from erpnext.accounts.utils import update_gl_entries_after, check_if_stock_and_account_balance_synced
 from frappe.utils.user import get_users_with_role
@@ -57,7 +58,8 @@
 		repost_gl_entries(doc)
 
 		doc.set_status('Completed')
-	except Exception:
+
+	except (Exception, JobTimeoutException):
 		frappe.db.rollback()
 		traceback = frappe.get_traceback()
 		frappe.log_error(traceback)
@@ -113,6 +115,12 @@
 	frappe.sendmail(recipients=recipients, subject=subject, message=message)
 
 def repost_entries():
+	job_log = frappe.get_all('Scheduled Job Log', fields = ['status', 'creation'],
+		filters = {'scheduled_job_type': 'repost_item_valuation.repost_entries'}, order_by='creation desc', limit=1)
+
+	if job_log and job_log[0]['status'] == 'Start' and time_diff_in_hours(now(), job_log[0]['creation']) < 2:
+		return
+
 	riv_entries = get_repost_item_valuation_entries()
 
 	for row in riv_entries:
@@ -127,7 +135,7 @@
 		check_if_stock_and_account_balance_synced(today(), d.name)
 
 def get_repost_item_valuation_entries():
-	date = add_to_date(today(), hours=-3)
+	date = add_to_date(now(), hours=-3)
 
 	return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation`
 		WHERE status != 'Completed' and creation <= %s and docstatus = 1
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index c02dd2e..5ecc9f8 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -243,7 +243,7 @@
 				if frappe.db.exists("Serial No", serial_no):
 					sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order",
 						"delivery_document_no", "delivery_document_type", "warehouse", "purchase_document_type",
-						"purchase_document_no", "company"], as_dict=1)
+						"purchase_document_no", "company", "status"], as_dict=1)
 
 					if sr.item_code!=sle.item_code:
 						if not allow_serial_nos_with_different_item(serial_no, sle):
@@ -266,6 +266,9 @@
 							frappe.throw(_("Serial No {0} does not belong to Warehouse {1}").format(serial_no,
 								sle.warehouse), SerialNoWarehouseError)
 
+						if not sr.purchase_document_no:
+							frappe.throw(_("Serial No {0} not in stock").format(serial_no), SerialNoNotExistsError)
+
 						if sle.voucher_type in ("Delivery Note", "Sales Invoice"):
 
 							if sr.batch_no and sr.batch_no != sle.batch_no:
@@ -382,19 +385,6 @@
 	if sn.company != sle.company:
 		return False
 
-	status = False
-	if sn.purchase_document_no:
-		if (sle.voucher_type in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"] and
-			sn.delivery_document_type not in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"]):
-			status = True
-
-		# If status is receipt then system will allow to in-ward the delivered serial no
-		if (status and sle.voucher_type == "Stock Entry" and frappe.db.get_value("Stock Entry",
-			sle.voucher_no, "purpose") in ("Material Receipt", "Material Transfer")):
-			status = False
-
-	return status
-
 def allow_serial_nos_with_different_item(sle_serial_no, sle):
 	"""
 		Allows same serial nos for raw materials and finished goods
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index ef7d54a..700093f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -783,8 +783,8 @@
 	}
 });
 
-erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
-	setup: function() {
+erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockController {
+	setup() {
 		var me = this;
 
 		this.setup_posting_date_time_check();
@@ -831,9 +831,9 @@
 
 		frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier' }
 		this.frm.set_query("supplier_address", erpnext.queries.address_query)
-	},
+	}
 
-	onload_post_render: function() {
+	onload_post_render() {
 		var me = this;
 		this.set_default_account(function() {
 			if(me.frm.doc.__islocal && me.frm.doc.company && !me.frm.doc.amended_from) {
@@ -842,9 +842,9 @@
 		});
 
 		this.frm.get_field("items").grid.set_multiple_add("item_code", "qty");
-	},
+	}
 
-	refresh: function() {
+	refresh() {
 		var me = this;
 		erpnext.toggle_naming_series();
 		this.toggle_related_fields(this.frm.doc);
@@ -855,22 +855,22 @@
 		}
 		erpnext.hide_company();
 		erpnext.utils.add_item(this.frm);
-	},
+	}
 
-	scan_barcode: function() {
+	scan_barcode() {
 		let transaction_controller= new erpnext.TransactionController({frm:this.frm});
 		transaction_controller.scan_barcode();
-	},
+	}
 
-	on_submit: function() {
+	on_submit() {
 		this.clean_up();
-	},
+	}
 
-	after_cancel: function() {
+	after_cancel() {
 		this.clean_up();
-	},
+	}
 
-	set_default_account: function(callback) {
+	set_default_account(callback) {
 		var me = this;
 
 		if(this.frm.doc.company && erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
@@ -890,9 +890,9 @@
 				}
 			});
 		}
-	},
+	}
 
-	clean_up: function() {
+	clean_up() {
 		// Clear Work Order record from locals, because it is updated via Stock Entry
 		if(this.frm.doc.work_order &&
 			in_list(["Manufacture", "Material Transfer for Manufacture", "Material Consumption for Manufacture"],
@@ -900,13 +900,13 @@
 			frappe.model.remove_from_locals("Work Order",
 				this.frm.doc.work_order);
 		}
-	},
+	}
 
-	fg_completed_qty: function() {
+	fg_completed_qty() {
 		this.get_items();
-	},
+	}
 
-	get_items: function() {
+	get_items() {
 		var me = this;
 		if(!this.frm.doc.fg_completed_qty || !this.frm.doc.bom_no)
 			frappe.throw(__("BOM and Manufacturing Quantity are required"));
@@ -922,9 +922,9 @@
 				}
 			});
 		}
-	},
+	}
 
-	work_order: function() {
+	work_order() {
 		var me = this;
 		this.toggle_enable_bom();
 		if(!me.frm.doc.work_order || me.frm.doc.job_card) {
@@ -957,13 +957,13 @@
 				}
 			}
 		});
-	},
+	}
 
-	toggle_enable_bom: function() {
+	toggle_enable_bom() {
 		this.frm.toggle_enable("bom_no", !!!this.frm.doc.work_order);
-	},
+	}
 
-	add_excise_button: function() {
+	add_excise_button() {
 		if(frappe.boot.sysdefaults.country === "India")
 			this.frm.add_custom_button(__("Excise Invoice"), function() {
 				var excise = frappe.model.make_new_doc_and_get_name('Journal Entry');
@@ -971,35 +971,35 @@
 				excise.voucher_type = 'Excise Entry';
 				frappe.set_route('Form', 'Journal Entry', excise.name);
 			}, __('Create'));
-	},
+	}
 
-	items_add: function(doc, cdt, cdn) {
+	items_add(doc, cdt, cdn) {
 		var row = frappe.get_doc(cdt, cdn);
 		this.frm.script_manager.copy_from_first_row("items", row, ["expense_account", "cost_center"]);
 
 		if(!row.s_warehouse) row.s_warehouse = this.frm.doc.from_warehouse;
 		if(!row.t_warehouse) row.t_warehouse = this.frm.doc.to_warehouse;
-	},
+	}
 
-	from_warehouse: function(doc) {
+	from_warehouse(doc) {
 		this.frm.trigger('set_tansit_warehouse');
 		this.set_warehouse_in_children(doc.items, "s_warehouse", doc.from_warehouse);
-	},
+	}
 
-	to_warehouse: function(doc) {
+	to_warehouse(doc) {
 		this.set_warehouse_in_children(doc.items, "t_warehouse", doc.to_warehouse);
-	},
+	}
 
-	set_warehouse_in_children: function(child_table, warehouse_field, warehouse) {
+	set_warehouse_in_children(child_table, warehouse_field, warehouse) {
 		let transaction_controller = new erpnext.TransactionController();
 		transaction_controller.autofill_warehouse(child_table, warehouse_field, warehouse);
-	},
+	}
 
-	items_on_form_rendered: function(doc, grid_row) {
-		erpnext.setup_serial_no();
-	},
+	items_on_form_rendered(doc, grid_row) {
+		erpnext.setup_serial_or_batch_no();
+	}
 
-	toggle_related_fields: function(doc) {
+	toggle_related_fields(doc) {
 		this.frm.toggle_enable("from_warehouse", doc.purpose!='Material Receipt');
 		this.frm.toggle_enable("to_warehouse", doc.purpose!='Material Issue');
 
@@ -1026,12 +1026,12 @@
 			doc.purpose!='Material Issue');
 
 		this.frm.fields_dict["items"].grid.set_column_disp("additional_cost", doc.purpose!='Material Issue');
-	},
+	}
 
-	supplier: function(doc) {
+	supplier(doc) {
 		erpnext.utils.get_party_details(this.frm, null, null, null);
 	}
-});
+};
 
 erpnext.stock.select_batch_and_serial_no = (frm, item) => {
 	let get_warehouse_type_and_name = (item) => {
@@ -1064,4 +1064,4 @@
 
 }
 
-$.extend(cur_frm.cscript, new erpnext.stock.StockEntry({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.stock.StockEntry({frm: cur_frm}));
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 48cfa51..2f76bc7 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -76,6 +76,7 @@
 		self.validate_difference_account()
 		self.set_job_card_data()
 		self.set_purpose_for_stock_entry()
+		self.validate_duplicate_serial_no()
 
 		if not self.from_bom:
 			self.fg_completed_qty = 0.0
@@ -587,6 +588,22 @@
 			self.purpose = frappe.get_cached_value('Stock Entry Type',
 				self.stock_entry_type, 'purpose')
 
+	def validate_duplicate_serial_no(self):
+		warehouse_wise_serial_nos = {}
+
+		# In case of repack the source and target serial nos could be same
+		for warehouse in ['s_warehouse', 't_warehouse']:
+			serial_nos = []
+			for row in self.items:
+				if not (row.serial_no and row.get(warehouse)): continue
+
+				for sn in get_serial_nos(row.serial_no):
+					if sn in serial_nos:
+						frappe.throw(_('The serial no {0} has added multiple times in the stock entry {1}')
+							.format(frappe.bold(sn), self.name))
+
+					serial_nos.append(sn)
+
 	def validate_purchase_order(self):
 		"""Throw exception if more raw material is transferred against Purchase Order than in
 		the raw materials supplied table"""
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index ac4ed5e..3badc7e 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -220,8 +220,8 @@
 
 });
 
-erpnext.stock.StockReconciliation = erpnext.stock.StockController.extend({
-	setup: function() {
+erpnext.stock.StockReconciliation = class StockReconciliation extends erpnext.stock.StockController {
+	setup() {
 		var me = this;
 
 		this.setup_posting_date_time_check();
@@ -249,17 +249,17 @@
 				}
 			}
 		}
-	},
+	}
 
-	refresh: function() {
+	refresh() {
 		if(this.frm.doc.docstatus > 0) {
 			this.show_stock_ledger();
 			if (erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
 				this.show_general_ledger();
 			}
 		}
-	},
+	}
 
-});
+};
 
 cur_frm.cscript = new erpnext.stock.StockReconciliation({frm: cur_frm});
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 7c5f4ec..0278c96 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -72,7 +72,7 @@
 
 				if item_dict.get("serial_nos"):
 					item.current_serial_no = item_dict.get("serial_nos")
-					if self.purpose == "Stock Reconciliation":
+					if self.purpose == "Stock Reconciliation" and not item.serial_no:
 						item.serial_no = item.current_serial_no
 
 				item.current_qty = item_dict.get("qty")
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index f18eabc..cf5d98d 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -5,40 +5,44 @@
  "doctype": "DocType",
  "engine": "InnoDB",
  "field_order": [
+  "item_defaults_section",
   "item_naming_by",
   "item_group",
   "stock_uom",
   "default_warehouse",
-  "sample_retention_warehouse",
   "column_break_4",
   "valuation_method",
+  "sample_retention_warehouse",
+  "use_naming_series",
+  "naming_series_prefix",
+  "section_break_9",
   "over_delivery_receipt_allowance",
   "role_allowed_to_over_deliver_receive",
-  "action_if_quality_inspection_is_not_submitted",
-  "show_barcode_field",
-  "clean_description_html",
-  "disable_serial_no_and_batch_selector",
-  "section_break_7",
+  "column_break_12",
   "auto_insert_price_list_rate_if_missing",
   "allow_negative_stock",
-  "column_break_10",
+  "show_barcode_field",
+  "clean_description_html",
+  "action_if_quality_inspection_is_not_submitted",
+  "section_break_7",
   "automatically_set_serial_nos_based_on_fifo",
   "set_qty_in_transactions_based_on_serial_no_input",
+  "column_break_10",
+  "disable_serial_no_and_batch_selector",
   "auto_material_request",
   "auto_indent",
+  "column_break_27",
   "reorder_email_notify",
   "inter_warehouse_transfer_settings_section",
   "allow_from_dn",
+  "column_break_31",
   "allow_from_pr",
   "control_historical_stock_transactions_section",
-  "role_allowed_to_create_edit_back_dated_transactions",
-  "column_break_26",
   "stock_frozen_upto",
   "stock_frozen_upto_days",
-  "stock_auth_role",
-  "batch_id_sb",
-  "use_naming_series",
-  "naming_series_prefix"
+  "column_break_26",
+  "role_allowed_to_create_edit_back_dated_transactions",
+  "stock_auth_role"
  ],
  "fields": [
   {
@@ -102,23 +106,24 @@
    "default": "1",
    "fieldname": "show_barcode_field",
    "fieldtype": "Check",
-   "label": "Show Barcode Field"
+   "label": "Show Barcode Field in Stock Transactions"
   },
   {
    "default": "1",
    "fieldname": "clean_description_html",
    "fieldtype": "Check",
-   "label": "Convert Item Description to Clean HTML"
+   "label": "Convert Item Description to Clean HTML in Transactions"
   },
   {
    "fieldname": "section_break_7",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Serialised and Batch Setting"
   },
   {
    "default": "0",
    "fieldname": "auto_insert_price_list_rate_if_missing",
    "fieldtype": "Check",
-   "label": "Auto Insert Price List Rate If Missing"
+   "label": "Auto Insert Item Price If Missing"
   },
   {
    "default": "0",
@@ -180,15 +185,10 @@
    "options": "Role"
   },
   {
-   "fieldname": "batch_id_sb",
-   "fieldtype": "Section Break",
-   "label": "Batch Identification"
-  },
-  {
    "default": "0",
    "fieldname": "use_naming_series",
    "fieldtype": "Check",
-   "label": "Use Naming Series"
+   "label": "Have Default Naming Series for Batch ID?"
   },
   {
    "default": "BATCH-",
@@ -242,6 +242,28 @@
    "fieldtype": "Link",
    "label": "Role Allowed to Over Deliver/Receive",
    "options": "Role"
+  },
+  {
+   "fieldname": "item_defaults_section",
+   "fieldtype": "Section Break",
+   "label": "Item Defaults"
+  },
+  {
+   "fieldname": "section_break_9",
+   "fieldtype": "Section Break",
+   "label": "Stock Transactions Settings"
+  },
+  {
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "column_break_27",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "column_break_31",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "icon-cog",
@@ -249,7 +271,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-03-11 18:48:14.513055",
+ "modified": "2021-04-30 17:27:42.709231",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Settings",
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 3fc1df7..d1dcdc2 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -79,7 +79,7 @@
 		get_price_list_rate(args, item, out)
 
 	if args.customer and cint(args.is_pos):
-		out.update(get_pos_profile_item_details(args.company, args))
+		out.update(get_pos_profile_item_details(args.company, args, update_data=True))
 
 	if (args.get("doctype") == "Material Request" and
 		args.get("material_request_type") == "Material Transfer"):
@@ -470,7 +470,9 @@
 			item_tax_template = _get_item_tax_template(args, item_group_doc.taxes, out)
 			item_group = item_group_doc.parent_item_group
 
-def _get_item_tax_template(args, taxes, out={}, for_validate=False):
+def _get_item_tax_template(args, taxes, out=None, for_validate=False):
+	if out is None:
+		out = {}
 	taxes_with_validity = []
 	taxes_with_no_validity = []
 
@@ -933,10 +935,10 @@
 	return bin_details
 
 def get_company_total_stock(item_code, company):
-	return frappe.db.sql("""SELECT sum(actual_qty) from 
-		(`tabBin` INNER JOIN `tabWarehouse` ON `tabBin`.warehouse = `tabWarehouse`.name) 
-		WHERE `tabWarehouse`.company = '{0}' and `tabBin`.item_code = '{1}'"""
-		.format(company, item_code))[0][0]
+	return frappe.db.sql("""SELECT sum(actual_qty) from
+		(`tabBin` INNER JOIN `tabWarehouse` ON `tabBin`.warehouse = `tabWarehouse`.name)
+		WHERE `tabWarehouse`.company = %s and `tabBin`.item_code = %s""",
+		(company, item_code))[0][0]
 
 @frappe.whitelist()
 def get_serial_no_details(item_code, warehouse, stock_qty, serial_no):
diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js
index bddffd4..f00dd3e 100644
--- a/erpnext/stock/page/stock_balance/stock_balance.js
+++ b/erpnext/stock/page/stock_balance/stock_balance.js
@@ -62,7 +62,7 @@
 
 	// page.sort_selector.wrapper.css({'margin-right': '15px', 'margin-top': '4px'});
 
-	frappe.require('assets/js/item-dashboard.min.js', function() {
+	frappe.require('item-dashboard.bundle.js', function() {
 		page.item_dashboard = new erpnext.stock.ItemDashboard({
 			parent: page.main,
 			page_length: 20,
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
index b610e7d..c0ffdc9 100644
--- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
@@ -79,7 +79,7 @@
 		}
 	});
 
-	frappe.require('assets/js/item-dashboard.min.js', function() {
+	frappe.require('item-dashboard.bundle.js', function() {
 		$(frappe.render_template('warehouse_capacity_summary_header')).appendTo(page.main);
 
 		page.capacity_dashboard = new erpnext.stock.ItemDashboard({
@@ -117,4 +117,4 @@
 		setup_click('Item');
 		setup_click('Warehouse');
 	});
-};
\ No newline at end of file
+};
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 087c12e..01927c2 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
@@ -70,7 +70,7 @@
 	return frappe.db.sql("""
 		select item_code, batch_no, warehouse, posting_date, sum(actual_qty) as actual_qty
 		from `tabStock Ledger Entry`
-		where docstatus < 2 and ifnull(batch_no, '') != '' %s
+		where is_cancelled = 0 and docstatus < 2 and ifnull(batch_no, '') != '' %s
 		group by voucher_no, batch_no, item_code, warehouse
 		order by item_code, warehouse""" %
 		conditions, as_dict=1)
diff --git a/erpnext/stock/report/serial_no_ledger/__init__.py b/erpnext/stock/report/serial_no_ledger/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/__init__.py
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
new file mode 100644
index 0000000..616312e
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
@@ -0,0 +1,52 @@
+// 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": [
+		{
+			'label': __('Item Code'),
+			'fieldtype': 'Link',
+			'fieldname': 'item_code',
+			'reqd': 1,
+			'options': 'Item',
+			get_query: function() {
+				return {
+					filters: {
+						'has_serial_no': 1
+					}
+				}
+			}
+		},
+		{
+			'label': __('Serial No'),
+			'fieldtype': 'Link',
+			'fieldname': 'serial_no',
+			'options': 'Serial No',
+			'reqd': 1
+		},
+		{
+			'label': __('Warehouse'),
+			'fieldtype': 'Link',
+			'fieldname': 'warehouse',
+			'options': 'Warehouse',
+			get_query: function() {
+				let company = frappe.query_report.get_filter_value('company');
+
+				if (company) {
+					return {
+						filters: {
+							'company': company
+						}
+					}
+				}
+			}
+		},
+		{
+			'label': __('As On Date'),
+			'fieldtype': 'Date',
+			'fieldname': 'posting_date',
+			'default': frappe.datetime.get_today()
+		},
+	]
+};
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.json b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.json
new file mode 100644
index 0000000..e20e74c
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.json
@@ -0,0 +1,33 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-04-20 13:32:41.523219",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "modified": "2021-04-20 13:33:19.015829",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Serial No Ledger",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Stock Ledger Entry",
+ "report_name": "Serial No Ledger",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Stock User"
+  },
+  {
+   "role": "Purchase User"
+  },
+  {
+   "role": "Sales User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
new file mode 100644
index 0000000..c3339fd
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
@@ -0,0 +1,53 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe import _
+from erpnext.stock.stock_ledger import get_stock_ledger_entries
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+def execute(filters=None):
+	columns = get_columns(filters)
+	data = get_data(filters)
+	return columns, data
+
+def get_columns(filters):
+	columns = [{
+		'label': _('Posting Date'),
+		'fieldtype': 'Date',
+		'fieldname': 'posting_date'
+	}, {
+		'label': _('Posting Time'),
+		'fieldtype': 'Time',
+		'fieldname': 'posting_time'
+	}, {
+		'label': _('Voucher Type'),
+		'fieldtype': 'Link',
+		'fieldname': 'voucher_type',
+		'options': 'DocType',
+		'width': 220
+	}, {
+		'label': _('Voucher No'),
+		'fieldtype': 'Dynamic Link',
+		'fieldname': 'voucher_no',
+		'options': 'voucher_type',
+		'width': 220
+	}, {
+		'label': _('Company'),
+		'fieldtype': 'Link',
+		'fieldname': 'company',
+		'options': 'Company',
+		'width': 220
+	}, {
+		'label': _('Warehouse'),
+		'fieldtype': 'Link',
+		'fieldname': 'warehouse',
+		'options': 'Warehouse',
+		'width': 220
+	}]
+
+	return columns
+
+def get_data(filters):
+	return get_stock_ledger_entries(filters, '<=', order="asc") or []
+
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index 6dfede4..bbd73e9 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -165,7 +165,7 @@
 		select
 			sle.item_code, warehouse, sle.posting_date, sle.actual_qty, sle.valuation_rate,
 			sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference,
-			sle.item_code as name, sle.voucher_no, sle.stock_value
+			sle.item_code as name, sle.voucher_no, sle.stock_value, sle.batch_no
 		from
 			`tabStock Ledger Entry` sle force index (posting_sort_index)
 		where sle.docstatus < 2 %s %s
@@ -193,7 +193,7 @@
 
 		qty_dict = iwb_map[(d.company, d.item_code, d.warehouse)]
 
-		if d.voucher_type == "Stock Reconciliation":
+		if d.voucher_type == "Stock Reconciliation" and not d.batch_no:
 			qty_diff = flt(d.qty_after_transaction) - flt(qty_dict.bal_qty)
 		else:
 			qty_diff = flt(d.actual_qty)
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js
index babc6dc..cb109f8 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js
@@ -14,7 +14,14 @@
 			"fieldname":"warehouse",
 			"label": __("Warehouse"),
 			"fieldtype": "Link",
-			"options": "Warehouse"
+			"options": "Warehouse",
+			"get_query": () => {
+				return {
+					filters: {
+						company: frappe.query_report.get_filter_value('company')
+					}
+				}
+			}
 		},
 		{
 			"fieldname":"item_code",
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
index 1183e41..808d279 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -6,6 +6,7 @@
 from frappe import _
 from frappe.utils import flt, today
 from erpnext.stock.utils import update_included_uom_in_report, is_reposting_item_valuation_in_progress
+from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_pos_reserved_qty
 
 def execute(filters=None):
 	is_reposting_item_valuation_in_progress()
@@ -49,9 +50,13 @@
 		if (re_order_level or re_order_qty) and re_order_level > bin.projected_qty:
 			shortage_qty = re_order_level - flt(bin.projected_qty)
 
+		reserved_qty_for_pos = get_pos_reserved_qty(bin.item_code, bin.warehouse)
+		if reserved_qty_for_pos:
+			bin.projected_qty -= reserved_qty_for_pos
+
 		data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse,
 			item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty,
-			bin.reserved_qty, bin.reserved_qty_for_production, bin.reserved_qty_for_sub_contract,
+			bin.reserved_qty, bin.reserved_qty_for_production, bin.reserved_qty_for_sub_contract, reserved_qty_for_pos,
 			bin.projected_qty, re_order_level, re_order_qty, shortage_qty])
 
 		if include_uom:
@@ -74,9 +79,11 @@
 		{"label": _("Requested Qty"), "fieldname": "indented_qty", "fieldtype": "Float", "width": 110, "convertible": "qty"},
 		{"label": _("Ordered Qty"), "fieldname": "ordered_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
 		{"label": _("Reserved Qty"), "fieldname": "reserved_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Reserved Qty for Production"), "fieldname": "reserved_qty_for_production", "fieldtype": "Float",
+		{"label": _("Reserved for Production"), "fieldname": "reserved_qty_for_production", "fieldtype": "Float",
 			"width": 100, "convertible": "qty"},
-		{"label": _("Reserved for sub contracting"), "fieldname": "reserved_qty_for_sub_contract", "fieldtype": "Float",
+		{"label": _("Reserved for Sub Contracting"), "fieldname": "reserved_qty_for_sub_contract", "fieldtype": "Float",
+			"width": 100, "convertible": "qty"},
+		{"label": _("Reserved for POS Transactions"), "fieldname": "reserved_qty_for_pos", "fieldtype": "Float",
 			"width": 100, "convertible": "qty"},
 		{"label": _("Projected Qty"), "fieldname": "projected_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
 		{"label": _("Reorder Level"), "fieldname": "re_order_level", "fieldtype": "Float", "width": 100, "convertible": "qty"},
diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.py b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
index ed52393..59c253c 100644
--- a/erpnext/stock/report/total_stock_summary/total_stock_summary.py
+++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
@@ -51,7 +51,7 @@
 			INNER JOIN `tabWarehouse` warehouse
 				ON warehouse.name = ledger.warehouse
 			WHERE
-				actual_qty != 0 %s""" % (columns, conditions))
+				ledger.actual_qty != 0 %s""" % (columns, conditions))
 
 def validate_filters(filters):
 	if filters.get("group_by") == 'Company' and \
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index bbfcb7a..9729987 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -2,9 +2,11 @@
 # License: GNU General Public License v3. See license.txt
 from __future__ import unicode_literals
 
-import frappe, erpnext
+import frappe
+import erpnext
+import copy
 from frappe import _
-from frappe.utils import cint, flt, cstr, now, now_datetime
+from frappe.utils import cint, flt, cstr, now, get_link_to_form
 from frappe.model.meta import get_field_precision
 from erpnext.stock.utils import get_valuation_method, get_incoming_outgoing_rate_for_cancel
 from erpnext.stock.utils import get_bin
@@ -13,6 +15,8 @@
 
 # future reposting
 class NegativeStockError(frappe.ValidationError): pass
+class SerialNoExistsInFutureTransaction(frappe.ValidationError):
+	pass
 
 _exceptions = frappe.local('stockledger_exceptions')
 # _exceptions = []
@@ -27,6 +31,9 @@
 			set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
 
 		for sle in sl_entries:
+			if sle.serial_no:
+				validate_serial_no(sle)
+
 			if cancel:
 				sle['actual_qty'] = -flt(sle.get('actual_qty'))
 
@@ -46,6 +53,30 @@
 			args = sle_doc.as_dict()
 			update_bin(args, allow_negative_stock, via_landed_cost_voucher)
 
+def validate_serial_no(sle):
+	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+	for sn in get_serial_nos(sle.serial_no):
+		args = copy.deepcopy(sle)
+		args.serial_no = sn
+		args.warehouse = ''
+
+		vouchers = []
+		for row in get_stock_ledger_entries(args, '>'):
+			voucher_type = frappe.bold(row.voucher_type)
+			voucher_no = frappe.bold(get_link_to_form(row.voucher_type, row.voucher_no))
+			vouchers.append(f'{voucher_type} {voucher_no}')
+
+		if vouchers:
+			serial_no = frappe.bold(sn)
+			msg = (f'''The serial no {serial_no} has been used in the future transactions so you need to cancel them first.
+				The list of the transactions are as below.''' + '<br><br><ul><li>')
+
+			msg += '</li><li>'.join(vouchers)
+			msg += '</li></ul>'
+
+			title = 'Cannot Submit' if not sle.get('is_cancelled') else 'Cannot Cancel'
+			frappe.throw(_(msg), title=_(title), exc=SerialNoExistsInFutureTransaction)
+
 def validate_cancellation(args):
 	if args[0].get("is_cancelled"):
 		repost_entry = frappe.db.get_value("Repost Item Valuation", {
@@ -718,7 +749,17 @@
 		conditions += " and " + previous_sle.get("warehouse_condition")
 
 	if check_serial_no and previous_sle.get("serial_no"):
-		conditions += " and serial_no like {}".format(frappe.db.escape('%{0}%'.format(previous_sle.get("serial_no"))))
+		# conditions += " and serial_no like {}".format(frappe.db.escape('%{0}%'.format(previous_sle.get("serial_no"))))
+		serial_no = previous_sle.get("serial_no")
+		conditions += (""" and
+			(
+				serial_no = {0}
+				or serial_no like {1}
+				or serial_no like {2}
+				or serial_no like {3}
+			)
+		""").format(frappe.db.escape(serial_no), frappe.db.escape('{}\n%'.format(serial_no)),
+			frappe.db.escape('%\n{}'.format(serial_no)), frappe.db.escape('%\n{}\n%'.format(serial_no)))
 
 	if not previous_sle.get("posting_date"):
 		previous_sle["posting_date"] = "1900-01-01"
@@ -793,12 +834,12 @@
 	if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \
 			and cint(erpnext.is_perpetual_inventory_enabled(company)):
 		frappe.local.message_log = []
-		form_link = frappe.utils.get_link_to_form("Item", item_code)
+		form_link = get_link_to_form("Item", item_code)
 
 		message = _("Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.").format(form_link, voucher_type, voucher_no)
-		message += "<br><br>" + _(" Here are the options to proceed:")
+		message += "<br><br>" + _("Here are the options to proceed:")
 		solutions = "<li>" + _("If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table.").format(voucher_type) + "</li>"
-		solutions += "<li>" + _("If not, you can Cancel / Submit this entry ") + _("{0}").format(frappe.bold("after")) + _(" performing either one below:") + "</li>"
+		solutions += "<li>" + _("If not, you can Cancel / Submit this entry") + " {0} ".format(frappe.bold("after")) + _("performing either one below:") + "</li>"
 		sub_solutions = "<ul><li>" + _("Create an incoming stock transaction for the Item.") + "</li>"
 		sub_solutions += "<li>" + _("Mention Valuation Rate in the Item master.") + "</li></ul>"
 		msg = message + solutions + sub_solutions + "</li>"
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index ecc9fcf..99a4e04 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -9,15 +9,17 @@
 			};
 		});
 
-		frappe.db.get_value("Support Settings", {name: "Support Settings"},
-			["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => {
-				if (r && r.track_service_level_agreement == "0") {
-					frm.set_df_property("service_level_section", "hidden", 1);
-				}
-				if (r && r.allow_resetting_service_level_agreement == "0") {
-					frm.set_df_property("reset_service_level_agreement", "hidden", 1);
-				}
-		});
+		if (frappe.model.can_read("Support Settings")) {
+			frappe.db.get_value("Support Settings", {name: "Support Settings"},
+				["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => {
+					if (r && r.track_service_level_agreement == "0") {
+						frm.set_df_property("service_level_section", "hidden", 1);
+					}
+					if (r && r.allow_resetting_service_level_agreement == "0") {
+						frm.set_df_property("reset_service_level_agreement", "hidden", 1);
+					}
+			});
+		}
 
 		if (frm.doc.service_level_agreement) {
 			frappe.call({
diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.js b/erpnext/support/doctype/warranty_claim/warranty_claim.js
index 79f4675..358768e 100644
--- a/erpnext/support/doctype/warranty_claim/warranty_claim.js
+++ b/erpnext/support/doctype/warranty_claim/warranty_claim.js
@@ -36,8 +36,8 @@
 	}
 });
 
-erpnext.support.WarrantyClaim = frappe.ui.form.Controller.extend({
-	refresh: function() {
+erpnext.support.WarrantyClaim = class WarrantyClaim extends frappe.ui.form.Controller {
+	refresh() {
 		frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
 
 		if(!cur_frm.doc.__islocal &&
@@ -45,17 +45,17 @@
 			cur_frm.add_custom_button(__('Maintenance Visit'),
 				this.make_maintenance_visit);
 		}
-	},
+	}
 
-	make_maintenance_visit: function() {
+	make_maintenance_visit() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.support.doctype.warranty_claim.warranty_claim.make_maintenance_visit",
 			frm: cur_frm
 		})
 	}
-});
+};
 
-$.extend(cur_frm.cscript, new erpnext.support.WarrantyClaim({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.support.WarrantyClaim({frm: cur_frm}));
 
 cur_frm.fields_dict['serial_no'].get_query = function(doc, cdt, cdn) {
 	var cond = [];
@@ -93,4 +93,4 @@
 			]
 		}
 	}
-};
\ No newline at end of file
+};
diff --git a/erpnext/templates/generators/item/item.html b/erpnext/templates/generators/item/item.html
index 135982d..17f6880 100644
--- a/erpnext/templates/generators/item/item.html
+++ b/erpnext/templates/generators/item/item.html
@@ -28,9 +28,7 @@
 
 {% block base_scripts %}
 <!-- js should be loaded in body! -->
-<script type="text/javascript" src="/assets/frappe/js/lib/jquery/jquery.min.js"></script>
-<script type="text/javascript" src="/assets/js/frappe-web.min.js"></script>
-<script type="text/javascript" src="/assets/js/control.min.js"></script>
-<script type="text/javascript" src="/assets/js/dialog.min.js"></script>
-<script type="text/javascript" src="/assets/js/bootstrap-4-web.min.js"></script>
-{% endblock %}
\ No newline at end of file
+{{ include_script("frappe-web.bundle.js") }}
+{{ include_script("controls.bundle.js") }}
+{{ include_script("dialog.bundle.js") }}
+{% endblock %}
diff --git a/erpnext/templates/includes/rfq.js b/erpnext/templates/includes/rfq.js
index b56c416..37beb5a 100644
--- a/erpnext/templates/includes/rfq.js
+++ b/erpnext/templates/includes/rfq.js
@@ -11,23 +11,23 @@
 	doc.buying_price_list = "{{ doc.buying_price_list }}"
 });
 
-rfq = Class.extend({
-	init: function(){
+rfq = class rfq {
+	constructor(){
 		this.onfocus_select_all();
 		this.change_qty();
 		this.change_rate();
 		this.terms();
 		this.submit_rfq();
 		this.navigate_quotations();
-	},
+	}
 
-	onfocus_select_all: function(){
+	onfocus_select_all(){
 		$("input").click(function(){
 			$(this).select();
 		})
-	},
+	}
 
-	change_qty: function(){
+	change_qty(){
 		var me = this;
 		$('.rfq-items').on("change", ".rfq-qty", function(){
 			me.idx = parseFloat($(this).attr('data-idx'));
@@ -36,9 +36,9 @@
 			me.update_qty_rate();
 			$(this).val(format_number(me.qty, doc.number_format, 2));
 		})
-	},
+	}
 
-	change_rate: function(){
+	change_rate(){
 		var me = this;
 		$(".rfq-items").on("change", ".rfq-rate", function(){
 			me.idx = parseFloat($(this).attr('data-idx'));
@@ -47,15 +47,15 @@
 			me.update_qty_rate();
 			$(this).val(format_number(me.rate, doc.number_format, 2));
 		})
-	},
+	}
 
-	terms: function(){
+	terms(){
 		$(".terms").on("change", ".terms-feedback", function(){
 			doc.terms = $(this).val();
 		})
-	},
+	}
 
-	update_qty_rate: function(){
+	update_qty_rate(){
 		var me = this;
 		doc.grand_total = 0.0;
 		$.each(doc.items, function(idx, data){
@@ -69,9 +69,9 @@
 			doc.grand_total += flt(data.amount);
 			$('.tax-grand-total').text(format_number(doc.grand_total, doc.number_format, 2));
 		})
-	},
+	}
 
-	submit_rfq: function(){
+	submit_rfq(){
 		$('.btn-sm').click(function(){
 			frappe.freeze();
 			frappe.call({
@@ -90,12 +90,12 @@
 				}
 			})
 		})
-	},
+	}
 
-	navigate_quotations: function() {
+	navigate_quotations() {
 		$('.quotations').click(function(){
 			name = $(this).attr('idx')
 			window.location.href = "/quotations/" + encodeURIComponent(name);
 		})
 	}
-})
+}
diff --git a/erpnext/templates/pages/cart.html b/erpnext/templates/pages/cart.html
index ea34371..c64c634 100644
--- a/erpnext/templates/pages/cart.html
+++ b/erpnext/templates/pages/cart.html
@@ -139,9 +139,7 @@
 
 {% block base_scripts %}
 <!-- js should be loaded in body! -->
-<script type="text/javascript" src="/assets/frappe/js/lib/jquery/jquery.min.js"></script>
-<script type="text/javascript" src="/assets/js/frappe-web.min.js"></script>
-<script type="text/javascript" src="/assets/js/control.min.js"></script>
-<script type="text/javascript" src="/assets/js/dialog.min.js"></script>
-<script type="text/javascript" src="/assets/js/bootstrap-4-web.min.js"></script>
+{{ include_script("frappe-web.bundle.js") }}
+{{ include_script("controls.bundle.js") }}
+{{ include_script("dialog.bundle.js") }}
 {% endblock %}
diff --git a/yarn.lock b/yarn.lock
index 635bb06..242695c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1540,16 +1540,11 @@
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
 
-ini@1.3.7:
+ini@1.3.7, ini@~1.3.0:
   version "1.3.7"
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
   integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
 
-ini@~1.3.0:
-  version "1.3.5"
-  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
-  integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
-
 is-callable@^1.1.5:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e"