Merge branch 'develop' into prospect
diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml
index 0be9bd8..0ece0d8 100644
--- a/.github/workflows/ui-tests.yml
+++ b/.github/workflows/ui-tests.yml
@@ -95,7 +95,7 @@
         run: cd ~/frappe-bench/ && bench --site test_site execute erpnext.setup.utils.before_tests
 
       - name: cypress pre-requisites
-        run: cd ~/frappe-bench/apps/frappe && yarn add cypress-file-upload@^5 --no-lockfile
+        run: cd ~/frappe-bench/apps/frappe && yarn add cypress-file-upload@^5 @testing-library/cypress@^8 --no-lockfile
 
 
       - name: Build Assets
diff --git a/erpnext/accounts/custom/address.py b/erpnext/accounts/custom/address.py
index c417a49..834227b 100644
--- a/erpnext/accounts/custom/address.py
+++ b/erpnext/accounts/custom/address.py
@@ -1,7 +1,7 @@
 import frappe
 from frappe import _
 from frappe.contacts.doctype.address.address import Address
-from frappe.contacts.doctype.address.address import get_address_templates
+from frappe.contacts.doctype.address.address import get_address_templates, get_address_display
 
 class ERPNextAddress(Address):
 	def validate(self):
@@ -22,6 +22,16 @@
 			frappe.throw(_("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
 				title=_("Company Not Linked"))
 
+	def on_update(self):
+		"""
+		After Address is updated, update the related 'Primary Address' on Customer.
+		"""
+		address_display = get_address_display(self.as_dict())
+		filters = { "customer_primary_address": self.name }
+		customers = frappe.db.get_all("Customer", filters=filters, as_list=True)
+		for customer_name in customers:
+			frappe.db.set_value("Customer", customer_name[0], "primary_address", address_display)
+			
 @frappe.whitelist()
 def get_shipping_address(company, address = None):
 	filters = [
diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.js b/erpnext/accounts/doctype/accounting_period/test_accounting_period.js
deleted file mode 100644
index 71ce5b8..0000000
--- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Accounting Period", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Accounting Period
-		() => frappe.tests.make('Accounting Period', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js
index e44af3a..0627675 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js
@@ -6,46 +6,3 @@
 
 	}
 });
-
-frappe.tour['Accounts Settings'] = [
-	{
-		fieldname: "acc_frozen_upto",
-		title: "Accounts Frozen Upto",
-		description: __("Freeze accounting transactions up to specified date, nobody can make/modify entry except the specified Role."),
-	},
-	{
-		fieldname: "frozen_accounts_modifier",
-		title: "Role Allowed to Set Frozen Accounts & Edit Frozen Entries",
-		description: __("Users with this Role are allowed to set frozen accounts and create/modify accounting entries against frozen accounts.")
-	},
-	{
-		fieldname: "determine_address_tax_category_from",
-		title: "Determine Address Tax Category From",
-		description: __("Tax category can be set on Addresses. An address can be Shipping or Billing address. Set which addres to select when applying Tax Category.")
-	},
-	{
-		fieldname: "over_billing_allowance",
-		title: "Over Billing Allowance Percentage",
-		description: __("The percentage by which you can overbill transactions. For example, if the order value is $100 for an Item and percentage here is set as 10% then you are allowed to bill for $110.")
-	},
-	{
-		fieldname: "credit_controller",
-		title: "Credit Controller",
-		description: __("Select the role that is allowed to submit transactions that exceed credit limits set. The credit limit can be set in the Customer form.")
-	},
-	{
-		fieldname: "make_payment_via_journal_entry",
-		title: "Make Payment via Journal Entry",
-		description: __("When checked, if user proceeds to make payment from an invoice, the system will open a Journal Entry instead of a Payment Entry.")
-	},
-	{
-		fieldname: "unlink_payment_on_cancellation_of_invoice",
-		title: "Unlink Payment on Cancellation of Invoice",
-		description: __("If checked, system will unlink the payment against the respective invoice.")
-	},
-	{
-		fieldname: "unlink_advance_payment_on_cancelation_of_order",
-		title: "Unlink Advance Payment on Cancellation of Order",
-		description: __("Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.")
-	}
-];
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index a246ae5..7d0ecfb 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -19,6 +19,7 @@
   "delete_linked_ledger_entries",
   "book_asset_depreciation_entry_automatically",
   "unlink_advance_payment_on_cancelation_of_order",
+  "enable_common_party_accounting",
   "post_change_gl_entries",
   "enable_discount_accounting",
   "tax_settings_section",
@@ -268,6 +269,12 @@
    "fieldname": "enable_discount_accounting",
    "fieldtype": "Check",
    "label": "Enable Discount Accounting"
+  },
+  {
+   "default": "0",
+   "fieldname": "enable_common_party_accounting",
+   "fieldtype": "Check",
+   "label": "Enable Common Party Accounting"
   }
  ],
  "icon": "icon-cog",
@@ -275,7 +282,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-08-09 13:08:04.335416",
+ "modified": "2021-08-19 11:17:38.788054",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/bank/test_bank.js b/erpnext/accounts/doctype/bank/test_bank.js
deleted file mode 100644
index 9ec2644..0000000
--- a/erpnext/accounts/doctype/bank/test_bank.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Bank
-		() => frappe.tests.make('Bank', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_account/test_bank_account.js b/erpnext/accounts/doctype/bank_account/test_bank_account.js
deleted file mode 100644
index c20a799..0000000
--- a/erpnext/accounts/doctype/bank_account/test_bank_account.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Account", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Bank Account
-		() => frappe.tests.make('Bank Account', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.js b/erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.js
deleted file mode 100644
index f599998..0000000
--- a/erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Account Subtype", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Bank Account Subtype
-		() => frappe.tests.make('Bank Account Subtype', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.js b/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.js
deleted file mode 100644
index 0c60920..0000000
--- a/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Guarantee", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Bank Guarantee
-		() => frappe.tests.make('Bank Guarantee', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.js b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.js
deleted file mode 100644
index 305119e..0000000
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Transaction", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Bank Transaction
-		() => frappe.tests.make('Bank Transaction', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.js b/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.js
deleted file mode 100644
index 12ca254..0000000
--- a/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Cash Flow Mapper", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Cash Flow Mapper
-		() => frappe.tests.make('Cash Flow Mapper', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.js b/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.js
deleted file mode 100644
index 1970ca8..0000000
--- a/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Cash Flow Mapping", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Cash Flow Mapping
-		() => frappe.tests.make('Cash Flow Mapping', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.js b/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.js
deleted file mode 100644
index 12546ce..0000000
--- a/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Cash Flow Mapping Template", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Cash Flow Mapping Template
-		() => frappe.tests.make('Cash Flow Mapping Template', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.js b/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.js
deleted file mode 100644
index eecabda..0000000
--- a/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Cash Flow Mapping Template Details", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Cash Flow Mapping Template Details
-		() => frappe.tests.make('Cash Flow Mapping Template Details', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.js b/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.js
deleted file mode 100644
index a7fcc8d..0000000
--- a/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Cashier Closing", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Cashier Closing
-		() => frappe.tests.make('Cashier Closing', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.js
deleted file mode 100644
index b075a01..0000000
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Chart of Accounts Importer", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Chart of Accounts Importer
-		() => frappe.tests.make('Chart of Accounts Importer', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.js b/erpnext/accounts/doctype/coupon_code/test_coupon_code.js
deleted file mode 100644
index 460fedc..0000000
--- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Coupon Code", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Coupon Code
-		() => frappe.tests.make('Coupon Code', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.js
deleted file mode 100644
index 57c6a78..0000000
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Exchange Rate Revaluation", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Exchange Rate Revaluation
-		() => frappe.tests.make('Exchange Rate Revaluation', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/finance_book/test_finance_book.js b/erpnext/accounts/doctype/finance_book/test_finance_book.js
deleted file mode 100644
index 9fb7d4f..0000000
--- a/erpnext/accounts/doctype/finance_book/test_finance_book.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Finance Book", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Finance Book
-		() => frappe.tests.make('Finance Book', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/gl_entry/test_gl_entry.js b/erpnext/accounts/doctype/gl_entry/test_gl_entry.js
deleted file mode 100644
index 2986e5e..0000000
--- a/erpnext/accounts/doctype/gl_entry/test_gl_entry.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: GL Entry", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially('GL Entry', [
-		// insert a new GL Entry
-		() => frappe.tests.make([
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/item_tax_template/test_item_tax_template.js b/erpnext/accounts/doctype/item_tax_template/test_item_tax_template.js
deleted file mode 100644
index 6893499..0000000
--- a/erpnext/accounts/doctype/item_tax_template/test_item_tax_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Item Tax Template", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Item Tax Template
-		() => frappe.tests.make('Item Tax Template', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.js b/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.js
deleted file mode 100644
index a916b67..0000000
--- a/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Loyalty Point Entry", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Loyalty Point Entry
-		() => frappe.tests.make('Loyalty Point Entry', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.js b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.js
deleted file mode 100644
index 9321c14..0000000
--- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Loyalty Program", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Loyalty Program
-		() => frappe.tests.make('Loyalty Program', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.js
deleted file mode 100644
index f95d0d8..0000000
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Opening Invoice Creation Tool", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Opening Invoice Creation Tool
-		() => frappe.tests.make('Opening Invoice Creation Tool', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/accounts/doctype/party_link/__init__.py
similarity index 100%
rename from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
rename to erpnext/accounts/doctype/party_link/__init__.py
diff --git a/erpnext/accounts/doctype/party_link/party_link.js b/erpnext/accounts/doctype/party_link/party_link.js
new file mode 100644
index 0000000..6da9291
--- /dev/null
+++ b/erpnext/accounts/doctype/party_link/party_link.js
@@ -0,0 +1,33 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Party Link', {
+	refresh: function(frm) {
+		frm.set_query('primary_role', () => {
+			return {
+				filters: {
+					name: ['in', ['Customer', 'Supplier']]
+				}
+			};
+		});
+
+		frm.set_query('secondary_role', () => {
+			let party_types = Object.keys(frappe.boot.party_account_types)
+				.filter(p => p != frm.doc.primary_role);
+			return {
+				filters: {
+					name: ['in', party_types]
+				}
+			};
+		});
+	},
+
+	primary_role(frm) {
+		frm.set_value('primary_party', '');
+		frm.set_value('secondary_role', '');
+	},
+
+	secondary_role(frm) {
+		frm.set_value('secondary_party', '');
+	}
+});
diff --git a/erpnext/accounts/doctype/party_link/party_link.json b/erpnext/accounts/doctype/party_link/party_link.json
new file mode 100644
index 0000000..a1bb15f
--- /dev/null
+++ b/erpnext/accounts/doctype/party_link/party_link.json
@@ -0,0 +1,102 @@
+{
+ "actions": [],
+ "autoname": "ACC-PT-LNK-.###.",
+ "creation": "2021-08-18 21:06:53.027695",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "primary_role",
+  "secondary_role",
+  "column_break_2",
+  "primary_party",
+  "secondary_party"
+ ],
+ "fields": [
+  {
+   "fieldname": "primary_role",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Primary Role",
+   "options": "DocType",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "primary_role",
+   "fieldname": "secondary_role",
+   "fieldtype": "Link",
+   "label": "Secondary Role",
+   "mandatory_depends_on": "primary_role",
+   "options": "DocType"
+  },
+  {
+   "depends_on": "primary_role",
+   "fieldname": "primary_party",
+   "fieldtype": "Dynamic Link",
+   "label": "Primary Party",
+   "mandatory_depends_on": "primary_role",
+   "options": "primary_role"
+  },
+  {
+   "depends_on": "secondary_role",
+   "fieldname": "secondary_party",
+   "fieldtype": "Dynamic Link",
+   "label": "Secondary Party",
+   "mandatory_depends_on": "secondary_role",
+   "options": "secondary_role"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-08-25 20:08:56.761150",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Party Link",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "primary_party",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/party_link/party_link.py b/erpnext/accounts/doctype/party_link/party_link.py
new file mode 100644
index 0000000..7d58506
--- /dev/null
+++ b/erpnext/accounts/doctype/party_link/party_link.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+
+class PartyLink(Document):
+	def validate(self):
+		if self.primary_role not in ['Customer', 'Supplier']:
+			frappe.throw(_("Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."),
+				title=_("Invalid Primary Role"))
+		
+		existing_party_link = frappe.get_all('Party Link', {
+			'primary_party': self.secondary_party
+		}, pluck="primary_role")
+		if existing_party_link:
+			frappe.throw(_('{} {} is already linked with another {}')
+				.format(self.secondary_role, self.secondary_party, existing_party_link[0]))
+		
+		existing_party_link = frappe.get_all('Party Link', {
+			'secondary_party': self.primary_party
+		}, pluck="primary_role")
+		if existing_party_link:
+			frappe.throw(_('{} {} is already linked with another {}')
+				.format(self.primary_role, self.primary_party, existing_party_link[0]))
diff --git a/erpnext/accounts/doctype/party_link/test_party_link.py b/erpnext/accounts/doctype/party_link/test_party_link.py
new file mode 100644
index 0000000..a3ea395
--- /dev/null
+++ b/erpnext/accounts/doctype/party_link/test_party_link.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestPartyLink(unittest.TestCase):
+	pass
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index abacee9..b259b11 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -755,9 +755,11 @@
 
 			if self.payment_type in ('Pay', 'Internal Transfer'):
 				dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit"
+				rev_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
 				against = self.party or self.paid_from
 			elif self.payment_type == 'Receive':
 				dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit"
+				rev_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
 				against = self.party or self.paid_to
 
 			payment_or_advance_account = self.get_party_account_for_taxes()
@@ -779,14 +781,13 @@
 					"cost_center": d.cost_center
 				}, account_currency, item=d))
 
-			#Intentionally use -1 to get net values in party account
 			if not d.included_in_paid_amount or self.advance_tax_account:
 				gl_entries.append(
 					self.get_gl_dict({
 						"account": payment_or_advance_account,
 						"against": against,
-						dr_or_cr: -1 * tax_amount,
-						dr_or_cr + "_in_account_currency": -1 * base_tax_amount
+						rev_dr_or_cr: tax_amount,
+						rev_dr_or_cr + "_in_account_currency": base_tax_amount
 						if account_currency==self.company_currency
 						else d.tax_amount,
 						"cost_center": self.cost_center,
diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.js b/erpnext/accounts/doctype/payment_order/test_payment_order.js
deleted file mode 100644
index f63fc54..0000000
--- a/erpnext/accounts/doctype/payment_order/test_payment_order.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Payment Order", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Payment Order
-		() => frappe.tests.make('Payment Order', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.js b/erpnext/accounts/doctype/payment_request/test_payment_request.js
deleted file mode 100644
index 070b595..0000000
--- a/erpnext/accounts/doctype/payment_request/test_payment_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Payment Request", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Payment Request
-		() => frappe.tests.make('Payment Request', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/payment_term/test_payment_term.js b/erpnext/accounts/doctype/payment_term/test_payment_term.js
deleted file mode 100644
index b26e42a..0000000
--- a/erpnext/accounts/doctype/payment_term/test_payment_term.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Payment Term", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Payment Term
-		() => frappe.tests.make('Payment Term', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.js b/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.js
deleted file mode 100644
index 494a0ed..0000000
--- a/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Payment Terms Template", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Payment Terms Template
-		() => frappe.tests.make('Payment Terms Template', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
index 2d19391..2a636bb 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
@@ -13,59 +13,49 @@
 
 class TestPeriodClosingVoucher(unittest.TestCase):
 	def test_closing_entry(self):
-		year_start_date = get_fiscal_year(today(), company="_Test Company")[1]
+		frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
 
-		make_journal_entry("_Test Bank - _TC", "Sales - _TC", 400,
-			"_Test Cost Center - _TC", posting_date=now(), submit=True)
+		company = create_company()
+		cost_center = create_cost_center('Test Cost Center 1')
 
-		make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 600, "_Test Cost Center - _TC", posting_date=now(), submit=True)
+		jv1 = make_journal_entry(
+			amount=400,
+			account1="Cash - TPC",
+			account2="Sales - TPC",
+			cost_center=cost_center,
+			posting_date=now(),
+			save=False
+		)
+		jv1.company = company
+		jv1.save()
+		jv1.submit()
 
-		random_expense_account = frappe.db.sql("""
-			select t1.account,
-				sum(t1.debit) - sum(t1.credit) as balance,
-				sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) \
-					as balance_in_account_currency
-			from `tabGL Entry` t1, `tabAccount` t2
-			where t1.account = t2.name and t2.root_type = 'Expense'
-				and t2.docstatus < 2 and t2.company = '_Test Company'
-				and t1.posting_date between %s and %s
-			group by t1.account
-			having sum(t1.debit) > sum(t1.credit)
-			limit 1""", (year_start_date, today()), as_dict=True)
-
-		profit_or_loss = frappe.db.sql("""select sum(t1.debit) - sum(t1.credit) as balance
-			from `tabGL Entry` t1, `tabAccount` t2
-			where t1.account = t2.name and t2.report_type = 'Profit and Loss'
-			and t2.docstatus < 2 and t2.company = '_Test Company'
-			and t1.posting_date between %s and %s""", (year_start_date, today()))
-
-		profit_or_loss = flt(profit_or_loss[0][0]) if profit_or_loss else 0
+		jv2 = make_journal_entry(
+			amount=600,
+			account1="Cost of Goods Sold - TPC",
+			account2="Cash - TPC",
+			cost_center=cost_center,
+			posting_date=now(),
+			save=False
+		)
+		jv2.company = company
+		jv2.save()
+		jv2.submit()
 
 		pcv = self.make_period_closing_voucher()
+		surplus_account = pcv.closing_account_head
 
-		# Check value for closing account
-		gle_amount_for_closing_account = frappe.db.sql("""select debit - credit
-			from `tabGL Entry` where voucher_type='Period Closing Voucher' and voucher_no=%s
-			and account = '_Test Account Reserves and Surplus - _TC'""", pcv.name)
+		expected_gle = (
+			('Cost of Goods Sold - TPC', 0.0, 600.0),
+			(surplus_account, 600.0, 400.0),
+			('Sales - TPC', 400.0, 0.0)
+		)
 
-		gle_amount_for_closing_account = flt(gle_amount_for_closing_account[0][0]) \
-			if gle_amount_for_closing_account else 0
+		pcv_gle = frappe.db.sql("""
+			select account, debit, credit from `tabGL Entry` where voucher_no=%s order by account
+		""", (pcv.name))
 
-		self.assertEqual(gle_amount_for_closing_account, profit_or_loss)
-
-		if random_expense_account:
-			# Check posted value for teh above random_expense_account
-			gle_for_random_expense_account = frappe.db.sql("""
-				select sum(debit - credit) as amount,
-					sum(debit_in_account_currency - credit_in_account_currency) as amount_in_account_currency
-				from `tabGL Entry`
-				where voucher_type='Period Closing Voucher' and voucher_no=%s and account =%s""",
-				(pcv.name, random_expense_account[0].account), as_dict=True)
-
-			self.assertEqual(gle_for_random_expense_account[0].amount, -1*random_expense_account[0].balance)
-			self.assertEqual(gle_for_random_expense_account[0].amount_in_account_currency,
-				-1*random_expense_account[0].balance_in_account_currency)
+		self.assertEqual(pcv_gle, expected_gle)
 
 	def test_cost_center_wise_posting(self):
 		frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
@@ -93,31 +83,23 @@
 			debit_to="Debtors - TPC"
 		)
 
-		pcv = frappe.get_doc({
-			"transaction_date": today(),
-			"posting_date": today(),
-			"fiscal_year": get_fiscal_year(today())[0],
-			"company": "Test PCV Company",
-			"cost_center_wise_pnl": 1,
-			"closing_account_head": surplus_account,
-			"remarks": "Test",
-			"doctype": "Period Closing Voucher"
-		})
-		pcv.insert()
-		pcv.submit()
+		pcv = self.make_period_closing_voucher()
+		surplus_account = pcv.closing_account_head
 
 		expected_gle = (
-			('Sales - TPC', 200.0, 0.0, cost_center2),
+			(surplus_account, 0.0, 400.0, cost_center1),
 			(surplus_account, 0.0, 200.0, cost_center2),
 			('Sales - TPC', 400.0, 0.0, cost_center1),
-			(surplus_account, 0.0, 400.0, cost_center1)
+			('Sales - TPC', 200.0, 0.0, cost_center2),
 		)
 
 		pcv_gle = frappe.db.sql("""
-			select account, debit, credit, cost_center from `tabGL Entry` where voucher_no=%s
+			select account, debit, credit, cost_center
+			from `tabGL Entry` where voucher_no=%s
+			order by account, cost_center
 		""", (pcv.name))
 
-		self.assertTrue(pcv_gle, expected_gle)
+		self.assertEqual(pcv_gle, expected_gle)
 
 	def test_period_closing_with_finance_book_entries(self):
 		frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
@@ -146,39 +128,35 @@
 		jv.save()
 		jv.submit()
 
-		pcv = frappe.get_doc({
-			"transaction_date": today(),
-			"posting_date": today(),
-			"fiscal_year": get_fiscal_year(today())[0],
-			"company": company,
-			"closing_account_head": surplus_account,
-			"remarks": "Test",
-			"doctype": "Period Closing Voucher"
-		})
-		pcv.insert()
-		pcv.submit()
+		pcv = self.make_period_closing_voucher()
+		surplus_account = pcv.closing_account_head
 
 		expected_gle = (
-			(surplus_account, 0.0, 400.0, ''),
+			(surplus_account, 0.0, 400.0, None),
 			(surplus_account, 0.0, 400.0, jv.finance_book),
-			('Sales - TPC', 400.0, 0.0, ''),
+			('Sales - TPC', 400.0, 0.0, None),
 			('Sales - TPC', 400.0, 0.0, jv.finance_book)
 		)
 
 		pcv_gle = frappe.db.sql("""
-			select account, debit, credit, finance_book from `tabGL Entry` where voucher_no=%s
+			select account, debit, credit, finance_book
+			from `tabGL Entry` where voucher_no=%s
+			order by account, finance_book
 		""", (pcv.name))
 
-		self.assertTrue(pcv_gle, expected_gle)
+		self.assertEqual(pcv_gle, expected_gle)
 
 	def make_period_closing_voucher(self):
+		surplus_account = create_account()
+		cost_center = create_cost_center("Test Cost Center 1")
 		pcv = frappe.get_doc({
 			"doctype": "Period Closing Voucher",
-			"closing_account_head": "_Test Account Reserves and Surplus - _TC",
-			"company": "_Test Company",
-			"fiscal_year": get_fiscal_year(today(), company="_Test Company")[0],
+			"transaction_date": today(),
 			"posting_date": today(),
-			"cost_center": "_Test Cost Center - _TC",
+			"company": "Test PCV Company",
+			"fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0],
+			"cost_center": cost_center,
+			"closing_account_head": surplus_account,
 			"remarks": "test"
 		})
 		pcv.insert()
diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.js
deleted file mode 100644
index 48109b1..0000000
--- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: POS Closing Entry", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new POS Closing Entry
-		() => frappe.tests.make('POS Closing Entry', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/pos_profile/test_pos_profile.js b/erpnext/accounts/doctype/pos_profile/test_pos_profile.js
deleted file mode 100644
index 42e5b7f..0000000
--- a/erpnext/accounts/doctype/pos_profile/test_pos_profile.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: POS Profile", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially('POS Profile', [
-		// insert a new POS Profile
-		() => frappe.tests.make([
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.js b/erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.js
deleted file mode 100644
index 5449ab7..0000000
--- a/erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: POS Profile User", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new POS Profile User
-		() => frappe.tests.make('POS Profile User', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/pos_settings/test_pos_settings.js b/erpnext/accounts/doctype/pos_settings/test_pos_settings.js
deleted file mode 100644
index 639c94e..0000000
--- a/erpnext/accounts/doctype/pos_settings/test_pos_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: POS Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new POS Settings
-		() => frappe.tests.make('POS Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 94abf3b..5467cb0 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -475,7 +475,20 @@
 						frappe.msgprint(_("User has not applied rule on the invoice {0}")
 							.format(doc.name))
 					else:
-						doc.set(field, d.get(pr_field))
+						if not d.coupon_code_based:
+							doc.set(field, d.get(pr_field))
+						elif doc.get('coupon_code'):
+							# coupon code based pricing rule
+							coupon_code_pricing_rule = frappe.db.get_value('Coupon Code', doc.get('coupon_code'), 'pricing_rule')
+							if coupon_code_pricing_rule == d.name:
+								# if selected coupon code is linked with pricing rule
+								doc.set(field, d.get(pr_field))
+							else:
+								# reset discount if not linked
+								doc.set(field, 0)
+						else:
+							# if coupon code based but no coupon code selected
+							doc.set(field, 0)
 
 				doc.calculate_taxes_and_totals()
 			elif d.price_or_product_discount == 'Product':
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index a16795e..e2f02f3 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -415,6 +415,8 @@
 		self.update_project()
 		update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
 
+		self.process_common_party_accounting()
+
 	def make_gl_entries(self, gl_entries=None, from_repost=False):
 		if not gl_entries:
 			gl_entries = self.get_gl_entries()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 1cf0df0..fe3ed16 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -253,6 +253,8 @@
 		if "Healthcare" in active_domains:
 			manage_invoice_submit_cancel(self, "on_submit")
 
+		self.process_common_party_accounting()
+
 	def validate_pos_return(self):
 
 		if self.is_pos and self.is_return:
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index c3d83c7..e06a3bb 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -151,7 +151,7 @@
 		si1 = create_sales_invoice(rate=1000)
 		si2 = create_sales_invoice(rate=300)
 		si3 = create_sales_invoice(qty=-1, rate=300, is_return=1)
-		
+
 
 		pe = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Bank - _TC")
 		pe.append('references', {
@@ -1140,6 +1140,18 @@
 		self.assertEqual(loss_for_si['credit'], loss_for_return_si['debit'])
 		self.assertEqual(loss_for_si['debit'], loss_for_return_si['credit'])
 
+	def test_incoming_rate_for_stand_alone_credit_note(self):
+		return_si = create_sales_invoice(is_return=1, update_stock=1, qty=-1, rate=90000, incoming_rate=10,
+			company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', debit_to='Debtors - TCP1',
+			income_account='Sales - TCP1', expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1')
+
+		incoming_rate = frappe.db.get_value('Stock Ledger Entry', {'voucher_no': return_si.name}, 'incoming_rate')
+		debit_amount = frappe.db.get_value('GL Entry',
+			{'voucher_no': return_si.name, 'account': 'Stock In Hand - TCP1'}, 'debit')
+
+		self.assertEqual(debit_amount, 10.0)
+		self.assertEqual(incoming_rate, 10.0)
+
 	def test_discount_on_net_total(self):
 		si = frappe.copy_doc(test_records[2])
 		si.apply_discount_on = "Net Total"
@@ -1816,23 +1828,13 @@
 		acc_settings.save()
 
 	def test_inter_company_transaction(self):
+		from erpnext.selling.doctype.customer.test_customer import create_internal_customer
 
-		if not frappe.db.exists("Customer", "_Test Internal Customer"):
-			customer = frappe.get_doc({
-				"customer_group": "_Test Customer Group",
-				"customer_name": "_Test Internal Customer",
-				"customer_type": "Individual",
-				"doctype": "Customer",
-				"territory": "_Test Territory",
-				"is_internal_customer": 1,
-				"represents_company": "_Test Company 1"
-			})
-
-			customer.append("companies", {
-				"company": "Wind Power LLC"
-			})
-
-			customer.insert()
+		create_internal_customer(
+			customer_name="_Test Internal Customer",
+			represents_company="_Test Company 1",
+			allowed_to_interact_with="Wind Power LLC"
+		)
 
 		if not frappe.db.exists("Supplier", "_Test Internal Supplier"):
 			supplier = frappe.get_doc({
@@ -1958,8 +1960,43 @@
 		frappe.local.enable_perpetual_inventory['_Test Company 1'] = old_perpetual_inventory
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock)
 
+	def test_sle_if_target_warehouse_exists_accidentally(self):
+		"""
+			Check if inward entry exists if Target Warehouse accidentally exists
+			but Customer is not an internal customer.
+		"""
+		se = make_stock_entry(
+			item_code="138-CMS Shoe",
+			target="Finished Goods - _TC",
+			company = "_Test Company",
+			qty=1,
+			basic_rate=500
+		)
+
+		si = frappe.copy_doc(test_records[0])
+		si.update_stock = 1
+		si.set_warehouse = "Finished Goods - _TC"
+		si.set_target_warehouse = "Stores - _TC"
+		si.get("items")[0].warehouse = "Finished Goods - _TC"
+		si.get("items")[0].target_warehouse = "Stores - _TC"
+		si.insert()
+		si.submit()
+
+		sles = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": si.name},
+			fields=["name", "actual_qty"])
+
+		# check if only one SLE for outward entry is created
+		self.assertEqual(len(sles), 1)
+		self.assertEqual(sles[0].actual_qty, -1)
+
+		# tear down
+		si.cancel()
+		se.cancel()
+
 	def test_internal_transfer_gl_entry(self):
 		## Create internal transfer account
+		from erpnext.selling.doctype.customer.test_customer import create_internal_customer
+
 		account = create_account(account_name="Unrealized Profit",
 			parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
 
@@ -2163,6 +2200,50 @@
 			self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
 			self.assertTrue(schedule.journal_entry)
 
+	def test_sales_invoice_against_supplier(self):
+		from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import make_customer
+		from erpnext.buying.doctype.supplier.test_supplier import create_supplier
+
+		# create a customer
+		customer = make_customer(customer="_Test Common Supplier")
+		# create a supplier
+		supplier = create_supplier(supplier_name="_Test Common Supplier").name
+
+		# create a party link between customer & supplier
+		# set primary role as supplier
+		party_link = frappe.new_doc("Party Link")
+		party_link.primary_role = "Supplier"
+		party_link.primary_party = supplier
+		party_link.secondary_role = "Customer"
+		party_link.secondary_party = customer
+		party_link.save()
+
+		# enable common party accounting
+		frappe.db.set_value('Accounts Settings', None, 'enable_common_party_accounting', 1)
+
+		# create a sales invoice
+		si = create_sales_invoice(customer=customer, parent_cost_center="_Test Cost Center - _TC")
+
+		# check outstanding of sales invoice
+		si.reload()
+		self.assertEqual(si.status, 'Paid')
+		self.assertEqual(flt(si.outstanding_amount), 0.0)
+
+		# check creation of journal entry
+		jv = frappe.get_all('Journal Entry Account', {
+			'account': si.debit_to,
+			'party_type': 'Customer',
+			'party': si.customer,
+			'reference_type': si.doctype,
+			'reference_name': si.name
+		}, pluck='credit_in_account_currency')
+
+		self.assertTrue(jv)
+		self.assertEqual(jv[0], si.grand_total)
+
+		party_link.delete()
+		frappe.db.set_value('Accounts Settings', None, 'enable_common_party_accounting', 0)
+
 def get_sales_invoice_for_e_invoice():
 	si = make_sales_invoice_for_ewaybill()
 	si.naming_series = 'INV-2020-.#####'
@@ -2375,7 +2456,8 @@
 		"asset": args.asset or None,
 		"cost_center": args.cost_center or "_Test Cost Center - _TC",
 		"serial_no": args.serial_no,
-		"conversion_factor": 1
+		"conversion_factor": 1,
+		"incoming_rate": args.incoming_rate or 0
 	})
 
 	if not args.do_not_save:
@@ -2472,29 +2554,6 @@
 	"row_id": 1
 	}]
 
-def create_internal_customer(customer_name, represents_company, allowed_to_interact_with):
-	if not frappe.db.exists("Customer", customer_name):
-		customer = frappe.get_doc({
-			"customer_group": "_Test Customer Group",
-			"customer_name": customer_name,
-			"customer_type": "Individual",
-			"doctype": "Customer",
-			"territory": "_Test Territory",
-			"is_internal_customer": 1,
-			"represents_company": represents_company
-		})
-
-		customer.append("companies", {
-			"company": allowed_to_interact_with
-		})
-
-		customer.insert()
-		customer_name = customer.name
-	else:
-		customer_name = frappe.db.get_value("Customer", customer_name)
-
-	return customer_name
-
 def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with):
 	if not frappe.db.exists("Supplier", supplier_name):
 		supplier = frappe.get_doc({
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index c77076c..b90f3f0 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -53,7 +53,6 @@
   "column_break_24",
   "base_net_rate",
   "base_net_amount",
-  "incoming_rate",
   "drop_ship",
   "delivered_by_supplier",
   "accounting",
@@ -81,6 +80,7 @@
   "target_warehouse",
   "quality_inspection",
   "batch_no",
+  "incoming_rate",
   "col_break5",
   "allow_zero_valuation_rate",
   "serial_no",
@@ -807,12 +807,12 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:parent.is_return && parent.update_stock && !parent.return_against",
    "fieldname": "incoming_rate",
    "fieldtype": "Currency",
-   "label": "Incoming Rate",
+   "label": "Incoming Rate (Costing)",
    "no_copy": 1,
-   "print_hide": 1,
-   "read_only": 1
+   "print_hide": 1
   },
   {
    "depends_on": "eval: doc.uom != doc.stock_uom",
@@ -833,7 +833,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-08-12 20:15:47.668399",
+ "modified": "2021-08-19 13:41:53.435827",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js
index 0e01188..97a6fdd 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js
@@ -5,25 +5,3 @@
 
 {% include "erpnext/public/js/controllers/accounts.js" %}
 
-frappe.tour['Sales Taxes and Charges Template'] = [
-	{
-		fieldname: "title",
-		title: __("Title"),
-		description: __("A name by which you will identify this template. You can change this later."),
-	},
-	{
-		fieldname: "company",
-		title: __("Company"),
-		description: __("Company for which this tax template will be applicable"),
-	},
-	{
-		fieldname: "is_default",
-		title: __("Is this Default?"),
-		description: __("Set this template as the default for all sales transactions"),
-	},
-	{
-		fieldname: "taxes",
-		title: __("Taxes Table"),
-		description: __("You can add a row for a tax rule here. These rules can be applied on the net total, or can be a flat amount."),
-	}
-];
diff --git a/erpnext/accounts/doctype/share_transfer/test_share_transfer.js b/erpnext/accounts/doctype/share_transfer/test_share_transfer.js
deleted file mode 100644
index e5530fa..0000000
--- a/erpnext/accounts/doctype/share_transfer/test_share_transfer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Share Transfer", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Share Transfer
-		() => frappe.tests.make('Share Transfer', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/share_type/test_share_type.js b/erpnext/accounts/doctype/share_type/test_share_type.js
deleted file mode 100644
index 620afa2..0000000
--- a/erpnext/accounts/doctype/share_type/test_share_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Share Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Share Type
-		() => frappe.tests.make('Share Type', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/shareholder/test_shareholder.js b/erpnext/accounts/doctype/shareholder/test_shareholder.js
deleted file mode 100644
index 61c5312..0000000
--- a/erpnext/accounts/doctype/shareholder/test_shareholder.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shareholder", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Shareholder
-		() => frappe.tests.make('Shareholder', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.js b/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.js
deleted file mode 100644
index 15d3df2..0000000
--- a/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Subscription Invoice", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Subscription Invoice
-		() => frappe.tests.make('Subscription Invoice', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
index 771611a..878ae09 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
@@ -21,7 +21,7 @@
   "column_break_13",
   "billing_interval_count",
   "payment_plan_section",
-  "payment_plan_id",
+  "product_price_id",
   "column_break_16",
   "payment_gateway",
   "accounting_dimensions_section",
@@ -115,11 +115,6 @@
    "label": "Payment Plan"
   },
   {
-   "fieldname": "payment_plan_id",
-   "fieldtype": "Data",
-   "label": "Payment Plan"
-  },
-  {
    "fieldname": "column_break_16",
    "fieldtype": "Column Break"
   },
@@ -144,10 +139,15 @@
    "fieldtype": "Link",
    "label": "Cost Center",
    "options": "Cost Center"
+  },
+  {
+   "fieldname": "product_price_id",
+   "fieldtype": "Data",
+   "label": "Product Price ID"
   }
  ],
  "links": [],
- "modified": "2021-08-09 10:53:44.205774",
+ "modified": "2021-08-13 10:53:44.205774",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Subscription Plan",
diff --git a/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.js b/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.js
deleted file mode 100644
index 3ceb9a6..0000000
--- a/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Subscription Plan", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Subscription Plan
-		() => frappe.tests.make('Subscription Plan', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.js b/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.js
deleted file mode 100644
index 5a751ea..0000000
--- a/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Subscription Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Subscription Settings
-		() => frappe.tests.make('Subscription Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/tax_category/test_tax_category.js b/erpnext/accounts/doctype/tax_category/test_tax_category.js
deleted file mode 100644
index 5142456..0000000
--- a/erpnext/accounts/doctype/tax_category/test_tax_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Tax Category", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Tax Category
-		() => frappe.tests.make('Tax Category', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/tax_rule/test_tax_rule.js b/erpnext/accounts/doctype/tax_rule/test_tax_rule.js
deleted file mode 100644
index 72d177d..0000000
--- a/erpnext/accounts/doctype/tax_rule/test_tax_rule.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Tax Rule", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Tax Rule
-		() => frappe.tests.make('Tax Rule', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.js b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.js
deleted file mode 100644
index eab98d4..0000000
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Tax Withholding Category", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Tax Withholding Category
-		() => frappe.tests.make('Tax Withholding Category', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/form_tour/accounts_settings/accounts_settings.json b/erpnext/accounts/form_tour/accounts_settings/accounts_settings.json
new file mode 100644
index 0000000..e2bf50d
--- /dev/null
+++ b/erpnext/accounts/form_tour/accounts_settings/accounts_settings.json
@@ -0,0 +1,113 @@
+{
+ "creation": "2021-06-29 17:00:18.273054",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-06-29 17:00:26.145996",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Accounts Settings",
+ "owner": "Administrator",
+ "reference_doctype": "Accounts Settings",
+ "save_on_complete": 0,
+ "steps": [
+  {
+   "description": "The percentage by which you can overbill transactions. For example, if the order value is $100 for an Item and percentage here is set as 10% then you are allowed to bill for $110.",
+   "field": "",
+   "fieldname": "over_billing_allowance",
+   "fieldtype": "Currency",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Over Billing Allowance (%)",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Over Billing Allowance Percentage"
+  },
+  {
+   "description": "Select the role that is allowed to overbill a transactions.",
+   "field": "",
+   "fieldname": "role_allowed_to_over_bill",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Role Allowed to Over Bill ",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Role Allowed to Over Bill"
+  },
+  {
+   "description": "If checked, system will unlink the payment against the respective invoice.",
+   "field": "",
+   "fieldname": "unlink_payment_on_cancellation_of_invoice",
+   "fieldtype": "Check",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Unlink Payment on Cancellation of Invoice",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Unlink Payment on Cancellation of Invoice"
+  },
+  {
+   "description": "Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.",
+   "field": "",
+   "fieldname": "unlink_advance_payment_on_cancelation_of_order",
+   "fieldtype": "Check",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Unlink Advance Payment on Cancellation of Order",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Unlink Advance Payment on Cancellation of Order"
+  },
+  {
+   "description": "Tax category can be set on Addresses. An address can be Shipping or Billing address. Set which addres to select when applying Tax Category.",
+   "field": "",
+   "fieldname": "determine_address_tax_category_from",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Determine Address Tax Category From",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Determine Address Tax Category From"
+  },
+  {
+   "description": "Freeze accounting transactions up to specified date, nobody can make/modify entry except the specified Role.",
+   "field": "",
+   "fieldname": "acc_frozen_upto",
+   "fieldtype": "Date",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Accounts Frozen Till Date",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Accounts Frozen Upto"
+  },
+  {
+   "description": "Users with this Role are allowed to set frozen accounts and create/modify accounting entries against frozen accounts.",
+   "field": "",
+   "fieldname": "frozen_accounts_modifier",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Role Allowed to Set Frozen Accounts and Edit Frozen Entries",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries"
+  },
+  {
+   "description": "Select the role that is allowed to submit transactions that exceed credit limits set. The credit limit can be set in the Customer form.",
+   "field": "",
+   "fieldname": "credit_controller",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Credit Controller",
+   "parent_field": "",
+   "position": "Left",
+   "title": "Credit Controller"
+  }
+ ],
+ "title": "Accounts Settings"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/form_tour/purchase_invoice/purchase_invoice.json b/erpnext/accounts/form_tour/purchase_invoice/purchase_invoice.json
new file mode 100644
index 0000000..2dffcd1
--- /dev/null
+++ b/erpnext/accounts/form_tour/purchase_invoice/purchase_invoice.json
@@ -0,0 +1,96 @@
+{
+ "creation": "2021-06-29 16:31:48.558826",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-06-29 16:31:48.558826",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Purchase Invoice",
+ "owner": "Administrator",
+ "reference_doctype": "Purchase Invoice",
+ "save_on_complete": 1,
+ "steps": [
+  {
+   "description": "Select Supplier",
+   "field": "",
+   "fieldname": "supplier",
+   "fieldtype": "Link",
+   "has_next_condition": 1,
+   "is_table_field": 0,
+   "label": "Supplier",
+   "next_step_condition": "supplier",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Select Supplier"
+  },
+  {
+   "description": "Add items in the table",
+   "field": "",
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Items",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "List of Items"
+  },
+  {
+   "child_doctype": "Purchase Invoice Item",
+   "description": "Select an item",
+   "field": "",
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 1,
+   "label": "Item",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Right",
+   "title": "Select Item"
+  },
+  {
+   "child_doctype": "Purchase Invoice Item",
+   "description": "Enter the quantity",
+   "field": "",
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "has_next_condition": 0,
+   "is_table_field": 1,
+   "label": "Accepted Qty",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Right",
+   "title": "Enter Quantity"
+  },
+  {
+   "child_doctype": "Purchase Invoice Item",
+   "description": "Enter rate of the item",
+   "field": "",
+   "fieldname": "rate",
+   "fieldtype": "Currency",
+   "has_next_condition": 0,
+   "is_table_field": 1,
+   "label": "Rate",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Right",
+   "title": "Enter Rate"
+  },
+  {
+   "description": "You can add taxes here",
+   "field": "",
+   "fieldname": "taxes",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Purchase Taxes and Charges",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Select taxes"
+  }
+ ],
+ "title": "Purchase Invoice"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json b/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
new file mode 100644
index 0000000..7de9ae1
--- /dev/null
+++ b/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
@@ -0,0 +1,65 @@
+{
+ "creation": "2021-08-24 12:28:18.044902",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 12:28:18.044902",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Taxes and Charges Template",
+ "owner": "Administrator",
+ "reference_doctype": "Sales Taxes and Charges Template",
+ "save_on_complete": 0,
+ "steps": [
+  {
+   "description": "A name by which you will identify this template. You can change this later.",
+   "field": "",
+   "fieldname": "title",
+   "fieldtype": "Data",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Title",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Title"
+  },
+  {
+   "description": "Company for which this tax template will be applicable",
+   "field": "",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Company",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Company"
+  },
+  {
+   "description": "Set this template as the default for all sales transactions",
+   "field": "",
+   "fieldname": "is_default",
+   "fieldtype": "Check",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Default",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Is this Default Tax Template?"
+  },
+  {
+   "description": "You can add a row for a tax rule here. These rules can be applied on the net total, or can be a flat amount.",
+   "field": "",
+   "fieldname": "taxes",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Sales Taxes and Charges",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Taxes Table"
+  }
+ ],
+ "title": "Sales Taxes and Charges Template"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json
index 6b5c5a1..2e0ab43 100644
--- a/erpnext/accounts/module_onboarding/accounts/accounts.json
+++ b/erpnext/accounts/module_onboarding/accounts/accounts.json
@@ -13,35 +13,35 @@
  "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts",
  "idx": 0,
  "is_complete": 0,
- "modified": "2020-10-30 15:41:15.547225",
+ "modified": "2021-08-13 11:59:35.690443",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts",
  "owner": "Administrator",
  "steps": [
   {
+   "step": "Company"
+  },
+  {
    "step": "Chart of Accounts"
   },
   {
    "step": "Setup Taxes"
   },
   {
-   "step": "Create a Product"
+   "step": "Accounts Settings"
   },
   {
-   "step": "Create a Supplier"
+   "step": "Cost Centers for Report and Budgeting"
   },
   {
    "step": "Create Your First Purchase Invoice"
   },
   {
-   "step": "Create a Customer"
+   "step": "Updating Opening Balances"
   },
   {
-   "step": "Create Your First Sales Invoice"
-  },
-  {
-   "step": "Configure Account Settings"
+   "step": "Financial Statements"
   }
  ],
  "subtitle": "Accounts, Invoices, Taxation, and more.",
diff --git a/erpnext/accounts/onboarding_step/accounts_settings/accounts_settings.json b/erpnext/accounts/onboarding_step/accounts_settings/accounts_settings.json
new file mode 100644
index 0000000..3f44a73
--- /dev/null
+++ b/erpnext/accounts/onboarding_step/accounts_settings/accounts_settings.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Take a quick walk-through of Accounts Settings",
+ "creation": "2021-06-29 16:42:03.400731",
+ "description": "# Account Settings\n\nIn ERPNext, Accounting features are configurable as per your business needs. Accounts Settings is the place to define some of your accounting preferences like:\n\n - Credit Limit and over billing settings\n - Taxation preferences\n - Deferred accounting preferences\n",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 1,
+ "is_skipped": 0,
+ "modified": "2021-08-13 11:50:06.227835",
+ "modified_by": "Administrator",
+ "name": "Accounts Settings",
+ "owner": "Administrator",
+ "reference_document": "Accounts Settings",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Accounts Settings",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json
index fc49bd6..67553ba 100644
--- a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json
+++ b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json
@@ -1,10 +1,10 @@
 {
- "action": "Go to Page",
- "action_label": "View Chart of Accounts",
+ "action": "Watch Video",
+ "action_label": "Learn more about Chart of Accounts",
  "callback_message": "You can continue with the onboarding after exploring this page",
  "callback_title": "Awesome Work",
  "creation": "2020-05-13 19:58:20.928127",
- "description": "# Chart Of Accounts\n\nThe Chart of Accounts is the blueprint of the accounts in your organization.\nIt is a tree view of the names of the Accounts (Ledgers and Groups) that a Company requires to manage its books of accounts. ERPNext sets up a simple chart of accounts for each Company you create, but you can modify it according to your needs and legal requirements.\n\nFor each company, Chart of Accounts signifies the way to classify the accounting entries, mostly\nbased on statutory (tax, compliance to government regulations) requirements.\n\nThere's a brief video tutorial about chart of accounts in the next step.",
+ "description": "# Chart Of Accounts\n\nERPNext sets up a simple chart of accounts for each Company you create, but you can modify it according to business and legal requirements.",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
@@ -12,7 +12,7 @@
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-10-30 14:35:59.474920",
+ "modified": "2021-08-13 11:46:25.878506",
  "modified_by": "Administrator",
  "name": "Chart of Accounts",
  "owner": "Administrator",
@@ -21,5 +21,6 @@
  "show_form_tour": 0,
  "show_full_form": 0,
  "title": "Review Chart of Accounts",
- "validate_action": 0
+ "validate_action": 0,
+ "video_url": "https://www.youtube.com/embed/AcfMCT7wLLo"
 }
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/company/company.json b/erpnext/accounts/onboarding_step/company/company.json
new file mode 100644
index 0000000..4992e4d
--- /dev/null
+++ b/erpnext/accounts/onboarding_step/company/company.json
@@ -0,0 +1,22 @@
+{
+ "action": "Go to Page",
+ "action_label": "Let's Review your Company",
+ "creation": "2021-06-29 14:47:42.497318",
+ "description": "# Company\n\nIn ERPNext, you can also create multiple companies, and establish relationships (group/subsidiary) among them.\n\nWithin the company master, you can capture various default accounts for that Company and set crucial settings related to the accounting methodology followed for a company. \n",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 11:43:35.767341",
+ "modified_by": "Administrator",
+ "name": "Company",
+ "owner": "Administrator",
+ "path": "app/company",
+ "reference_document": "Company",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Review Company",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/cost_centers_for_report_and_budgeting/cost_centers_for_report_and_budgeting.json b/erpnext/accounts/onboarding_step/cost_centers_for_report_and_budgeting/cost_centers_for_report_and_budgeting.json
new file mode 100644
index 0000000..252b075
--- /dev/null
+++ b/erpnext/accounts/onboarding_step/cost_centers_for_report_and_budgeting/cost_centers_for_report_and_budgeting.json
@@ -0,0 +1,21 @@
+{
+ "action": "Go to Page",
+ "action_label": "View Cost Center Tree",
+ "creation": "2021-07-12 12:02:05.726608",
+ "description": "# Cost Centers for Budgeting and Analysis\n\nWhile your Books of Accounts are framed to fulfill statutory requirements, you can set up Cost Center and Accounting Dimensions to address your companies reporting and budgeting requirements.\n\nClick here to learn more about how  <b>[Cost Center](https://docs.erpnext.com/docs/v13/user/manual/en/accounts/cost-center)</b> and <b> [Dimensions](https://docs.erpnext.com/docs/v13/user/manual/en/accounts/accounting-dimensions)</b> allow you to get advanced financial analytics reports from ERPNext.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 11:55:08.510366",
+ "modified_by": "Administrator",
+ "name": "Cost Centers for Report and Budgeting",
+ "owner": "Administrator",
+ "path": "cost-center/view/tree",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Cost Centers for Budgeting and Analysis",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json
index ddbc89e..f4e298e 100644
--- a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json
+++ b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json
@@ -1,14 +1,15 @@
 {
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s create your first Purchase Invoice",
  "creation": "2020-05-14 22:10:07.049704",
- "description": "# What's a Purchase Invoice?\n\nA Purchase Invoice is a bill you receive from your Suppliers against which you need to make the payment.\nPurchase Invoice is the exact opposite of your Sales Invoice. Here you accrue expenses to your Supplier. \n\nThe following is what a typical purchase cycle looks like, however you can create a purchase invoice directly as well.\n\n![Purchase Flow](https://docs.erpnext.com/docs/assets/img/accounts/pi-flow.png)\n\n",
+ "description": "# Create your first Purchase Invoice\n\nA Purchase Invoice is a bill received from a Supplier for a product(s) or service(s) delivery to your company. You can track payables through Purchase Invoice and process Payment Entries against it.\n\nPurchase Invoices can also be created against a Purchase Order or Purchase Receipt.",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-10-30 15:30:26.337773",
+ "modified": "2021-08-13 11:56:11.677253",
  "modified_by": "Administrator",
  "name": "Create Your First Purchase Invoice",
  "owner": "Administrator",
diff --git a/erpnext/accounts/onboarding_step/financial_statements/financial_statements.json b/erpnext/accounts/onboarding_step/financial_statements/financial_statements.json
new file mode 100644
index 0000000..85cf9cd
--- /dev/null
+++ b/erpnext/accounts/onboarding_step/financial_statements/financial_statements.json
@@ -0,0 +1,23 @@
+{
+ "action": "View Report",
+ "creation": "2021-07-12 12:08:47.026115",
+ "description": "# Financial Statements\n\nIn ERPNext, you can get crucial financial reports like [Balance Sheet] and [Profit and Loss] statements with a click of a button. You can run in the report for a different period and plot analytics charts premised on statement data. For more reports, check sections like Financial Statements, General Ledger, and Profitability reports.\n\n<b>[Check Accounting reports](https://docs.erpnext.com/docs/v13/user/manual/en/accounts/accounting-reports)</b>",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 11:59:18.767407",
+ "modified_by": "Administrator",
+ "name": "Financial Statements",
+ "owner": "Administrator",
+ "reference_report": "General Ledger",
+ "report_description": "General Ledger",
+ "report_reference_doctype": "GL Entry",
+ "report_type": "Script Report",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Financial Statements",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
index a492201..9f4c873 100644
--- a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
+++ b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
@@ -1,21 +1,21 @@
 {
  "action": "Create Entry",
- "action_label": "Make a Sales Tax Template",
+ "action_label": "Manage Sales Tax Templates",
  "creation": "2020-05-13 19:29:43.844463",
- "description": "# Setting up Taxes\n\nAny sophisticated accounting system, including ERPNext will have automatic tax calculations for your transactions. These calculations are based on user defined rules in compliance to local rules and regulations.\n\nERPNext allows this via *Tax Templates*. These templates can be used in Sales Orders and Sales Invoices. Other types of charges that may apply to your invoices (like shipping, insurance etc.) can also be configured as taxes.\n\nFor Tax Accounts that you want to use in the tax templates, go to:\n\n`> Accounting > Taxes > Sales Taxes and Charges Template`\n\nYou can read more about these templates in our documentation [here](https://docs.erpnext.com/docs/user/manual/en/selling/sales-taxes-and-charges-template)\n",
+ "description": "# Setting up Taxes\n\nERPNext lets you configure your taxes so that they are automatically applied in your buying and selling transactions. You can configure them globally or even on Items. ERPNext taxes are pre-configured for most regions.\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-10-30 14:54:18.087383",
+ "modified": "2021-08-13 11:48:37.238610",
  "modified_by": "Administrator",
  "name": "Setup Taxes",
  "owner": "Administrator",
  "reference_document": "Sales Taxes and Charges Template",
  "show_form_tour": 1,
  "show_full_form": 1,
- "title": "Lets create a Tax Template for Sales ",
+ "title": "Setting up Taxes",
  "validate_action": 0
 }
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/updating_opening_balances/updating_opening_balances.json b/erpnext/accounts/onboarding_step/updating_opening_balances/updating_opening_balances.json
new file mode 100644
index 0000000..c0849a4
--- /dev/null
+++ b/erpnext/accounts/onboarding_step/updating_opening_balances/updating_opening_balances.json
@@ -0,0 +1,22 @@
+{
+ "action": "Watch Video",
+ "action_label": "Learn how to update opening balances",
+ "creation": "2021-07-12 11:53:50.525030",
+ "description": "# Updating Opening Balances\n\nOnce you close the financial statement in previous accounting software, you can update the same as opening in your ERPNext's Balance Sheet accounts. This will allow you to get complete financial statements from ERPNext in the coming years, and discontinue the parallel accounting system right away.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "intro_video_url": "https://www.youtube.com/embed/U5wPIvEn-0c",
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 11:56:45.483418",
+ "modified_by": "Administrator",
+ "name": "Updating Opening Balances",
+ "owner": "Administrator",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Updating Opening Balances",
+ "validate_action": 1,
+ "video_url": "https://www.youtube.com/embed/U5wPIvEn-0c"
+}
\ No newline at end of file
diff --git a/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.js b/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.js
deleted file mode 100644
index f70dcd2..0000000
--- a/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Agriculture Analysis Criteria", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Agriculture Analysis Criteria
-		() => frappe.tests.make('Agriculture Analysis Criteria', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.js b/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.js
deleted file mode 100644
index a012c4b..0000000
--- a/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Agriculture Task", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Agriculture Task
-		() => frappe.tests.make('Agriculture Task', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.js b/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.js
deleted file mode 100644
index 786c047..0000000
--- a/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Plant Analysis", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Plant Analysis
-		() => frappe.tests.make('Plant Analysis', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.js b/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.js
deleted file mode 100644
index 29128eb..0000000
--- a/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Soil Analysis", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Soil Analysis
-		() => frappe.tests.make('Soil Analysis', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/agriculture/doctype/weather/test_weather.js b/erpnext/agriculture/doctype/weather/test_weather.js
deleted file mode 100644
index b5009a4..0000000
--- a/erpnext/agriculture/doctype/weather/test_weather.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Weather", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Weather
-		() => frappe.tests.make('Weather', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/doctype/asset/test_asset.js b/erpnext/assets/doctype/asset/test_asset.js
deleted file mode 100644
index 6119e38..0000000
--- a/erpnext/assets/doctype/asset/test_asset.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Asset
-		() => frappe.tests.make('Asset', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/doctype/asset_category/test_asset_category.js b/erpnext/assets/doctype/asset_category/test_asset_category.js
deleted file mode 100644
index 7e343b7..0000000
--- a/erpnext/assets/doctype/asset_category/test_asset_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Category", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Asset Category
-		() => frappe.tests.make('Asset Category', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.js
deleted file mode 100644
index f9b38a1..0000000
--- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Maintenance", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Asset Maintenance
-		() => frappe.tests.make('Asset Maintenance', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.js b/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.js
deleted file mode 100644
index 4e80184..0000000
--- a/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Maintenance Log", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Asset Maintenance Log
-		() => frappe.tests.make('Asset Maintenance Log', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.js b/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.js
deleted file mode 100644
index 41bf696..0000000
--- a/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Maintenance Team", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Asset Maintenance Team
-		() => frappe.tests.make('Asset Maintenance Team', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.js b/erpnext/assets/doctype/asset_movement/test_asset_movement.js
deleted file mode 100644
index b951576..0000000
--- a/erpnext/assets/doctype/asset_movement/test_asset_movement.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Movement", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Asset Movement
-		() => frappe.tests.make('Asset Movement', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.js b/erpnext/assets/doctype/asset_repair/test_asset_repair.js
deleted file mode 100644
index 7424ffe..0000000
--- a/erpnext/assets/doctype/asset_repair/test_asset_repair.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Repair", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Asset Repair
-		() => frappe.tests.make('Asset Repair', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.js b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.js
deleted file mode 100644
index 32831c6..0000000
--- a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Value Adjustment", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Asset Value Adjustment
-		() => frappe.tests.make('Asset Value Adjustment', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/doctype/location/test_location.js b/erpnext/assets/doctype/location/test_location.js
deleted file mode 100644
index 3c06b63..0000000
--- a/erpnext/assets/doctype/location/test_location.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Location", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Location
-		() => frappe.tests.make('Location', [
-			// values to be set
-			{ location_name: 'Basil Farm' }
-		]),
-		() => {
-			assert.equal(cur_frm.doc.name, 'Basil Farm');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.js b/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.js
deleted file mode 100644
index d942e2a..0000000
--- a/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Maintenance Team Member", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Maintenance Team Member
-		() => frappe.tests.make('Maintenance Team Member', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/assets/form_tour/asset/asset.json b/erpnext/assets/form_tour/asset/asset.json
new file mode 100644
index 0000000..7c47a38
--- /dev/null
+++ b/erpnext/assets/form_tour/asset/asset.json
@@ -0,0 +1,125 @@
+{
+ "creation": "2021-08-24 16:55:10.923434",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 16:55:10.923434",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset",
+ "owner": "Administrator",
+ "reference_doctype": "Asset",
+ "save_on_complete": 0,
+ "steps": [
+  {
+   "description": "Select Naming Series based on which Asset ID will be generated",
+   "field": "",
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Naming Series",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Naming Series"
+  },
+  {
+   "description": "Select an Asset Item",
+   "field": "",
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Item Code",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Item Code"
+  },
+  {
+   "description": "Select a Location",
+   "field": "",
+   "fieldname": "location",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Location",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Location"
+  },
+  {
+   "description": "Check Is Existing Asset",
+   "field": "",
+   "fieldname": "is_existing_asset",
+   "fieldtype": "Check",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Is Existing Asset",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Is Existing Asset?"
+  },
+  {
+   "description": "Set Available for use date",
+   "field": "",
+   "fieldname": "available_for_use_date",
+   "fieldtype": "Date",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Available-for-use Date",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Available For Use Date"
+  },
+  {
+   "description": "Set Gross purchase amount",
+   "field": "",
+   "fieldname": "gross_purchase_amount",
+   "fieldtype": "Currency",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Gross Purchase Amount",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Gross Purchase Amount"
+  },
+  {
+   "description": "Set Purchase Date",
+   "field": "",
+   "fieldname": "purchase_date",
+   "fieldtype": "Date",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Purchase Date",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Purchase Date"
+  },
+  {
+   "description": "Check Calculate Depreciation",
+   "field": "",
+   "fieldname": "calculate_depreciation",
+   "fieldtype": "Check",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Calculate Depreciation",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Calculate Depreciation"
+  },
+  {
+   "description": "Enter depreciation which has already been booked for this asset",
+   "field": "",
+   "fieldname": "opening_accumulated_depreciation",
+   "fieldtype": "Currency",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Opening Accumulated Depreciation",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Accumulated Depreciation"
+  }
+ ],
+ "title": "Asset"
+}
\ No newline at end of file
diff --git a/erpnext/assets/form_tour/asset_category/asset_category.json b/erpnext/assets/form_tour/asset_category/asset_category.json
new file mode 100644
index 0000000..0283444
--- /dev/null
+++ b/erpnext/assets/form_tour/asset_category/asset_category.json
@@ -0,0 +1,65 @@
+{
+ "creation": "2021-08-24 12:48:20.763173",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 12:48:20.763173",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Category",
+ "owner": "Administrator",
+ "reference_doctype": "Asset Category",
+ "save_on_complete": 0,
+ "steps": [
+  {
+   "description": "Name Asset category. You can create categories based on Asset Types like Furniture, Property, Electronics etc.",
+   "field": "",
+   "fieldname": "asset_category_name",
+   "fieldtype": "Data",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Asset Category Name",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Asset Category Name"
+  },
+  {
+   "description": "Check to enable Capital Work in Progress accounting",
+   "field": "",
+   "fieldname": "enable_cwip_accounting",
+   "fieldtype": "Check",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Enable Capital Work in Progress Accounting",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Enable CWIP Accounting"
+  },
+  {
+   "description": "Add a row to define Depreciation Method and other details. Note that you can leave Finance Book blank to have it's accounting done in the primary books of accounts.",
+   "field": "",
+   "fieldname": "finance_books",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Finance Books",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Finance Book Detail"
+  },
+  {
+   "description": "Select the Fixed Asset and Depreciation accounts applicable for this Asset Category type",
+   "field": "",
+   "fieldname": "accounts",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Accounts",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Accounts"
+  }
+ ],
+ "title": "Asset Category"
+}
\ No newline at end of file
diff --git a/erpnext/assets/module_onboarding/assets/assets.json b/erpnext/assets/module_onboarding/assets/assets.json
index 1086ab4..e6df88b 100644
--- a/erpnext/assets/module_onboarding/assets/assets.json
+++ b/erpnext/assets/module_onboarding/assets/assets.json
@@ -13,26 +13,26 @@
  "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset",
  "idx": 0,
  "is_complete": 0,
- "modified": "2020-07-08 14:05:51.828497",
+ "modified": "2021-08-24 17:50:41.573281",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Assets",
  "owner": "Administrator",
  "steps": [
   {
-   "step": "Introduction to Assets"
+   "step": "Fixed Asset Accounts"
   },
   {
-   "step": "Create a Fixed Asset Item"
+   "step": "Asset Category"
   },
   {
-   "step": "Create an Asset Category"
+   "step": "Asset Item"
   },
   {
-   "step": "Purchase an Asset Item"
+   "step": "Asset Purchase"
   },
   {
-   "step": "Create an Asset"
+   "step": "Existing Asset"
   }
  ],
  "subtitle": "Assets, Depreciations, Repairs, and more.",
diff --git a/erpnext/assets/onboarding_step/asset_category/asset_category.json b/erpnext/assets/onboarding_step/asset_category/asset_category.json
new file mode 100644
index 0000000..033e866
--- /dev/null
+++ b/erpnext/assets/onboarding_step/asset_category/asset_category.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Let's review existing Asset Category",
+ "creation": "2021-08-13 14:26:18.656303",
+ "description": "# Asset Category\n\nAn Asset Category classifies different assets of a Company.\n\nYou can create an Asset Category based on the type of assets. For example, all your desktops and laptops can be part of an Asset Category named \"Electronic Equipments\". Create a separate category for furniture. Also, you can update default properties for each category, like:\n - Depreciation type and duration\n - Fixed asset account\n - Depreciation account\n",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-24 12:49:37.665239",
+ "modified_by": "Administrator",
+ "name": "Asset Category",
+ "owner": "Administrator",
+ "reference_document": "Asset Category",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Define Asset Category",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/onboarding_step/asset_item/asset_item.json b/erpnext/assets/onboarding_step/asset_item/asset_item.json
new file mode 100644
index 0000000..8a174c5
--- /dev/null
+++ b/erpnext/assets/onboarding_step/asset_item/asset_item.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Let's create a new Asset item",
+ "creation": "2021-08-13 14:27:07.277167",
+ "description": "# Asset Item\n\nAsset items are created based on Asset Category. You can create one or multiple items against once Asset Category. The sales and purchase transaction for Asset is done via Asset Item. ",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-16 13:59:18.362233",
+ "modified_by": "Administrator",
+ "name": "Asset Item",
+ "owner": "Administrator",
+ "reference_document": "Item",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Create an Asset Item",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/onboarding_step/asset_purchase/asset_purchase.json b/erpnext/assets/onboarding_step/asset_purchase/asset_purchase.json
new file mode 100644
index 0000000..54611ed
--- /dev/null
+++ b/erpnext/assets/onboarding_step/asset_purchase/asset_purchase.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Let's create a Purchase Receipt",
+ "creation": "2021-08-13 14:27:53.678621",
+ "description": "# Purchase an Asset\n\nAssets purchases process if done following the standard Purchase cycle. If capital work in progress is enabled in Asset Category, Asset will be created as soon as Purchase Receipt is created for it. You can quickly create a Purchase Receipt for Asset and see its impact on books of accounts.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-24 17:26:57.180637",
+ "modified_by": "Administrator",
+ "name": "Asset Purchase",
+ "owner": "Administrator",
+ "reference_document": "Purchase Receipt",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Purchase an Asset",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/onboarding_step/existing_asset/existing_asset.json b/erpnext/assets/onboarding_step/existing_asset/existing_asset.json
new file mode 100644
index 0000000..052d5e8
--- /dev/null
+++ b/erpnext/assets/onboarding_step/existing_asset/existing_asset.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Add an existing Asset",
+ "creation": "2021-08-13 14:28:30.650459",
+ "description": "# Add an Existing Asset\n\nIf you are just starting with ERPNext, you will need to enter Assets you already possess. You can add them as existing fixed assets in ERPNext. Please note that you will have to make a Journal Entry separately updating the opening balance in the fixed asset account.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-16 14:03:48.850471",
+ "modified_by": "Administrator",
+ "name": "Existing Asset",
+ "owner": "Administrator",
+ "reference_document": "Asset",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Add an Existing Asset",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/onboarding_step/fixed_asset_accounts/fixed_asset_accounts.json b/erpnext/assets/onboarding_step/fixed_asset_accounts/fixed_asset_accounts.json
new file mode 100644
index 0000000..cebee7a
--- /dev/null
+++ b/erpnext/assets/onboarding_step/fixed_asset_accounts/fixed_asset_accounts.json
@@ -0,0 +1,21 @@
+{
+ "action": "Go to Page",
+ "action_label": "Let's walk-through Chart of Accounts to review setup",
+ "creation": "2021-08-13 14:23:09.297765",
+ "description": "# Fixed Asset Accounts\n\nWith the company, a host of fixed asset accounts are pre-configured. To ensure your asset transactions are leading to correct accounting entries, you can review and set up following asset accounts as per your business  requirements.\n - Fixed asset accounts (Asset account)\n - Accumulated depreciation\n - Capital Work in progress (CWIP) account\n - Asset Depreciation account (Expense account)",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-24 17:46:37.646174",
+ "modified_by": "Administrator",
+ "name": "Fixed Asset Accounts",
+ "owner": "Administrator",
+ "path": "app/account/view/tree",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Review Fixed Asset Accounts",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 9f82af9..f4af893 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -14,7 +14,7 @@
 from erpnext.utilities.transaction_base import TransactionBase
 from erpnext.buying.utils import update_last_purchase_rate
 from erpnext.controllers.sales_and_purchase_return import validate_return
-from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled
+from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled, get_party_account
 from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_transaction,
 	apply_pricing_rule_for_free_items, get_applied_pricing_rules)
 from erpnext.exceptions import InvalidCurrency
@@ -1363,6 +1363,67 @@
 
 		return False
 
+	def process_common_party_accounting(self):
+		is_invoice = self.doctype in ['Sales Invoice', 'Purchase Invoice']
+		if not is_invoice:
+			return
+
+		if frappe.db.get_single_value('Accounts Settings', 'enable_common_party_accounting'):
+			party_link = self.get_common_party_link()
+			if party_link and self.outstanding_amount:
+				self.create_advance_and_reconcile(party_link)
+
+	def get_common_party_link(self):
+		party_type, party = self.get_party()
+		return frappe.db.get_value(
+			doctype='Party Link',
+			filters={'secondary_role': party_type, 'secondary_party': party},
+			fieldname=['primary_role', 'primary_party'],
+			as_dict=True
+		)
+
+	def create_advance_and_reconcile(self, party_link):
+		secondary_party_type, secondary_party = self.get_party()
+		primary_party_type, primary_party = party_link.primary_role, party_link.primary_party
+
+		primary_account = get_party_account(primary_party_type, primary_party, self.company)
+		secondary_account = get_party_account(secondary_party_type, secondary_party, self.company)
+
+		jv = frappe.new_doc('Journal Entry')
+		jv.voucher_type = 'Journal Entry'
+		jv.posting_date = self.posting_date
+		jv.company = self.company
+		jv.remark = 'Adjustment for {} {}'.format(self.doctype, self.name)
+
+		reconcilation_entry = frappe._dict()
+		advance_entry = frappe._dict()
+
+		reconcilation_entry.account = secondary_account
+		reconcilation_entry.party_type = secondary_party_type
+		reconcilation_entry.party = secondary_party
+		reconcilation_entry.reference_type = self.doctype
+		reconcilation_entry.reference_name = self.name
+		reconcilation_entry.cost_center = self.cost_center
+
+		advance_entry.account = primary_account
+		advance_entry.party_type = primary_party_type
+		advance_entry.party = primary_party
+		advance_entry.cost_center = self.cost_center
+		advance_entry.is_advance = 'Yes'
+
+		if self.doctype == 'Sales Invoice':
+			reconcilation_entry.credit_in_account_currency = self.outstanding_amount
+			advance_entry.debit_in_account_currency = self.outstanding_amount
+		else:
+			advance_entry.credit_in_account_currency = self.outstanding_amount
+			reconcilation_entry.debit_in_account_currency = self.outstanding_amount
+
+		jv.append('accounts', reconcilation_entry)
+		jv.append('accounts', advance_entry)
+
+		jv.save()
+		jv.submit()
+
 @frappe.whitelist()
 def get_tax_rate(account_head):
 	return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
diff --git a/erpnext/controllers/employee_boarding_controller.py b/erpnext/controllers/employee_boarding_controller.py
index 1898222..f43c804 100644
--- a/erpnext/controllers/employee_boarding_controller.py
+++ b/erpnext/controllers/employee_boarding_controller.py
@@ -5,7 +5,9 @@
 from frappe import _
 from frappe.desk.form import assign_to
 from frappe.model.document import Document
-from frappe.utils import flt, unique
+from frappe.utils import flt, unique, add_days
+from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
+from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
 
 class EmployeeBoardingController(Document):
 	'''
@@ -41,10 +43,14 @@
 
 	def create_task_and_notify_user(self):
 		# create the task for the given project and assign to the concerned person
+		holiday_list = self.get_holiday_list()
+
 		for activity in self.activities:
 			if activity.task:
 				continue
 
+			dates = self.get_task_dates(activity, holiday_list)
+
 			task = frappe.get_doc({
 				'doctype': 'Task',
 				'project': self.project,
@@ -52,7 +58,9 @@
 				'description': activity.description,
 				'department': self.department,
 				'company': self.company,
-				'task_weight': activity.task_weight
+				'task_weight': activity.task_weight,
+				'exp_start_date': dates[0],
+				'exp_end_date': dates[1]
 			}).insert(ignore_permissions=True)
 			activity.db_set('task', task.name)
 
@@ -79,6 +87,36 @@
 			if users:
 				self.assign_task_to_users(task, users)
 
+	def get_holiday_list(self):
+		if self.doctype == 'Employee Separation':
+			return get_holiday_list_for_employee(self.employee)
+		else:
+			if self.employee:
+				return get_holiday_list_for_employee(self.employee)
+			else:
+				if not self.holiday_list:
+					frappe.throw(_('Please set the Holiday List.'), frappe.MandatoryError)
+				else:
+					return self.holiday_list
+
+	def get_task_dates(self, activity, holiday_list):
+		start_date = end_date = None
+
+		if activity.begin_on:
+			start_date = add_days(self.boarding_begins_on, activity.begin_on)
+			start_date = self.update_if_holiday(start_date, holiday_list)
+
+			if activity.duration:
+				end_date = add_days(self.boarding_begins_on, activity.begin_on + activity.duration)
+				end_date = self.update_if_holiday(end_date, holiday_list)
+
+		return [start_date, end_date]
+
+	def update_if_holiday(self, date, holiday_list):
+		while is_holiday(holiday_list, date):
+			date = add_days(date, 1)
+		return date
+
 	def assign_task_to_users(self, task, users):
 		for user in users:
 			args = {
@@ -103,7 +141,8 @@
 @frappe.whitelist()
 def get_onboarding_details(parent, parenttype):
 	return frappe.get_all('Employee Boarding Activity',
-		fields=['activity_name', 'role', 'user', 'required_for_employee_creation', 'description', 'task_weight'],
+		fields=['activity_name', 'role', 'user', 'required_for_employee_creation',
+			'description', 'task_weight', 'begin_on', 'duration'],
 		filters={'parent': parent, 'parenttype': parenttype},
 		order_by= 'idx')
 
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 5ee1f2f..01486fc 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -394,19 +394,6 @@
 	if not return_against:
 		return_against = frappe.get_cached_value(voucher_type, voucher_no, "return_against")
 
-	if not return_against and voucher_type == 'Sales Invoice' and sle:
-		return get_incoming_rate({
-			"item_code": sle.item_code,
-			"warehouse": sle.warehouse,
-			"posting_date": sle.get('posting_date'),
-			"posting_time": sle.get('posting_time'),
-			"qty": sle.actual_qty,
-			"serial_no": sle.get('serial_no'),
-			"company": sle.company,
-			"voucher_type": sle.voucher_type,
-			"voucher_no": sle.voucher_no
-		}, raise_error_if_no_rate=False)
-
 	return_against_item_field = get_return_against_item_fields(voucher_type)
 
 	filters = get_filters(voucher_type, voucher_no, voucher_detail_no,
@@ -417,7 +404,24 @@
 	else:
 		select_field = "abs(stock_value_difference / actual_qty)"
 
-	return flt(frappe.db.get_value("Stock Ledger Entry", filters, select_field))
+	rate = flt(frappe.db.get_value("Stock Ledger Entry", filters, select_field))
+	if not (rate and return_against) and voucher_type in ['Sales Invoice', 'Delivery Note']:
+		rate = frappe.db.get_value(f'{voucher_type} Item', voucher_detail_no, 'incoming_rate')
+
+		if not rate and sle:
+			rate = get_incoming_rate({
+				"item_code": sle.item_code,
+				"warehouse": sle.warehouse,
+				"posting_date": sle.get('posting_date'),
+				"posting_time": sle.get('posting_time'),
+				"qty": sle.actual_qty,
+				"serial_no": sle.get('serial_no'),
+				"company": sle.company,
+				"voucher_type": sle.voucher_type,
+				"voucher_no": sle.voucher_no
+			}, raise_error_if_no_rate=False)
+
+	return rate
 
 def get_return_against_item_fields(voucher_type):
 	return_against_item_fields = {
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index fc2cc97..844c40c 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -362,7 +362,7 @@
 				sales_order.update_reserved_qty(so_item_rows)
 
 	def set_incoming_rate(self):
-		if self.doctype not in ("Delivery Note", "Sales Invoice", "Sales Order"):
+		if self.doctype not in ("Delivery Note", "Sales Invoice"):
 			return
 
 		items = self.get("items") + (self.get("packed_items") or [])
@@ -371,18 +371,19 @@
 				# Get incoming rate based on original item cost based on valuation method
 				qty = flt(d.get('stock_qty') or d.get('actual_qty'))
 
-				d.incoming_rate = get_incoming_rate({
-					"item_code": d.item_code,
-					"warehouse": d.warehouse,
-					"posting_date": self.get('posting_date') or self.get('transaction_date'),
-					"posting_time": self.get('posting_time') or nowtime(),
-					"qty": qty if cint(self.get("is_return")) else (-1 * qty),
-					"serial_no": d.get('serial_no'),
-					"company": self.company,
-					"voucher_type": self.doctype,
-					"voucher_no": self.name,
-					"allow_zero_valuation": d.get("allow_zero_valuation")
-				}, raise_error_if_no_rate=False)
+				if not d.incoming_rate:
+					d.incoming_rate = get_incoming_rate({
+						"item_code": d.item_code,
+						"warehouse": d.warehouse,
+						"posting_date": self.get('posting_date') or self.get('transaction_date'),
+						"posting_time": self.get('posting_time') or nowtime(),
+						"qty": qty if cint(self.get("is_return")) else (-1 * qty),
+						"serial_no": d.get('serial_no'),
+						"company": self.company,
+						"voucher_type": self.doctype,
+						"voucher_no": self.name,
+						"allow_zero_valuation": d.get("allow_zero_valuation")
+					}, raise_error_if_no_rate=False)
 
 				# For internal transfers use incoming rate as the valuation rate
 				if self.is_internal_transfer():
@@ -422,7 +423,7 @@
 					or (cint(self.is_return) and self.docstatus==2)):
 						sl_entries.append(self.get_sle_for_source_warehouse(d))
 
-				if d.target_warehouse:
+				if d.target_warehouse and self.get("is_internal_customer"):
 					sl_entries.append(self.get_sle_for_target_warehouse(d))
 
 				if d.warehouse and ((not cint(self.is_return) and self.docstatus==2)
diff --git a/erpnext/crm/doctype/contract/test_contract.js b/erpnext/crm/doctype/contract/test_contract.js
deleted file mode 100644
index 4c77c3d..0000000
--- a/erpnext/crm/doctype/contract/test_contract.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Contract", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Contract
-		() => frappe.tests.make('Contract', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.js b/erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.js
deleted file mode 100644
index 2a2d5e1..0000000
--- a/erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Contract Fulfilment Checklist", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Contract Fulfilment Checklist
-		() => frappe.tests.make('Contract Fulfilment Checklist', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/crm/doctype/contract_template/test_contract_template.js b/erpnext/crm/doctype/contract_template/test_contract_template.js
deleted file mode 100644
index 6aaddd7..0000000
--- a/erpnext/crm/doctype/contract_template/test_contract_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Contract Template", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Contract Template
-		() => frappe.tests.make('Contract Template', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/crm/doctype/market_segment/test_market_segment.js b/erpnext/crm/doctype/market_segment/test_market_segment.js
deleted file mode 100644
index aa4b868..0000000
--- a/erpnext/crm/doctype/market_segment/test_market_segment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Market Segment", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Market Segment
-		() => frappe.tests.make('Market Segment', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/crm/doctype/opportunity_type/test_opportunity_type.js b/erpnext/crm/doctype/opportunity_type/test_opportunity_type.js
deleted file mode 100644
index 3a1ede9..0000000
--- a/erpnext/crm/doctype/opportunity_type/test_opportunity_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Opportunity Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Opportunity Type
-		() => frappe.tests.make('Opportunity Type', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/crm/doctype/sales_stage/test_sales_stage.js b/erpnext/crm/doctype/sales_stage/test_sales_stage.js
deleted file mode 100644
index 807af1f..0000000
--- a/erpnext/crm/doctype/sales_stage/test_sales_stage.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sales Stage", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Sales Stage
-		() => frappe.tests.make('Sales Stage', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/academic_year/test_academic_year.js b/erpnext/education/doctype/academic_year/test_academic_year.js
deleted file mode 100644
index 51e9cf3..0000000
--- a/erpnext/education/doctype/academic_year/test_academic_year.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// Testing Setup Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Academic Year', function(assert){
-	assert.expect(3);
-	let done = assert.async();
-	frappe.run_serially([
-		() => {
-			return frappe.tests.make('Academic Year', [
-				{academic_year_name: '2016-17'},
-				{year_start_date: '2016-07-20'},
-				{year_end_date:'2017-06-20'},
-			]);
-		},
-
-		() => {
-			assert.ok(cur_frm.doc.academic_year_name=='2016-17');
-			assert.ok(cur_frm.doc.year_start_date=='2016-07-20');
-			assert.ok(cur_frm.doc.year_end_date=='2017-06-20');
-		},
-		() => done()
-	]);
-});
diff --git a/erpnext/education/doctype/article/test_article.js b/erpnext/education/doctype/article/test_article.js
deleted file mode 100644
index 9dbf063..0000000
--- a/erpnext/education/doctype/article/test_article.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Article", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Article
-		() => frappe.tests.make('Article', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/content_question/test_content_question.js b/erpnext/education/doctype/content_question/test_content_question.js
deleted file mode 100644
index cc869a8..0000000
--- a/erpnext/education/doctype/content_question/test_content_question.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Content Question", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Content Question
-		() => frappe.tests.make('Content Question', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/course_activity/test_course_activity.js b/erpnext/education/doctype/course_activity/test_course_activity.js
deleted file mode 100644
index c89c89e..0000000
--- a/erpnext/education/doctype/course_activity/test_course_activity.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Activity", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Course Activity
-		() => frappe.tests.make('Course Activity', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/course_content/test_course_content.js b/erpnext/education/doctype/course_content/test_course_content.js
deleted file mode 100644
index 786e67e..0000000
--- a/erpnext/education/doctype/course_content/test_course_content.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Content", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Course Content
-		() => frappe.tests.make('Course Content', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/course_enrollment/test_course_enrollment.js b/erpnext/education/doctype/course_enrollment/test_course_enrollment.js
deleted file mode 100644
index 216cc30..0000000
--- a/erpnext/education/doctype/course_enrollment/test_course_enrollment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Enrollment", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Course Enrollment
-		() => frappe.tests.make('Course Enrollment', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/course_schedule/test_course_schedule.js b/erpnext/education/doctype/course_schedule/test_course_schedule.js
deleted file mode 100644
index 5cdb67b..0000000
--- a/erpnext/education/doctype/course_schedule/test_course_schedule.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Schedule", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Course Schedule
-		() => frappe.tests.make('Course Schedule', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.js b/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.js
deleted file mode 100644
index 4419d18..0000000
--- a/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Scheduling Tool", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Course Scheduling Tool
-		() => frappe.tests.make('Course Scheduling Tool', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/course_topic/test_course_topic.js b/erpnext/education/doctype/course_topic/test_course_topic.js
deleted file mode 100644
index d8d154f..0000000
--- a/erpnext/education/doctype/course_topic/test_course_topic.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Topic", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Course Topic
-		() => frappe.tests.make('Course Topic', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/fee_category/test_fee_category.js b/erpnext/education/doctype/fee_category/test_fee_category.js
deleted file mode 100644
index a08ed33..0000000
--- a/erpnext/education/doctype/fee_category/test_fee_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Fee Category", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Fee Category
-		() => frappe.tests.make('Fee Category', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/fee_schedule/test_fee_schedule.js b/erpnext/education/doctype/fee_schedule/test_fee_schedule.js
deleted file mode 100644
index d495b4c..0000000
--- a/erpnext/education/doctype/fee_schedule/test_fee_schedule.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Fee Schedule", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially('Fee Schedule', [
-		// insert a new Fee Schedule
-		() => frappe.tests.make([
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/fee_structure/test_fee_structure.js b/erpnext/education/doctype/fee_structure/test_fee_structure.js
deleted file mode 100644
index 61f4135..0000000
--- a/erpnext/education/doctype/fee_structure/test_fee_structure.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Fee Structure", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Fee Structure
-		() => frappe.tests.make('Fee Structure', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/program_enrollment/test_program_enrollment.js b/erpnext/education/doctype/program_enrollment/test_program_enrollment.js
deleted file mode 100644
index aea81a0..0000000
--- a/erpnext/education/doctype/program_enrollment/test_program_enrollment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Program Enrollment", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Program Enrollment
-		() => frappe.tests.make('Program Enrollment', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.js b/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.js
deleted file mode 100644
index 8d55104..0000000
--- a/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Program Enrollment Tool", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Program Enrollment Tool
-		() => frappe.tests.make('Program Enrollment Tool', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/question/test_question.js b/erpnext/education/doctype/question/test_question.js
deleted file mode 100644
index 509939c..0000000
--- a/erpnext/education/doctype/question/test_question.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Question", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Question
-		() => frappe.tests.make('Question', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/quiz/test_quiz.js b/erpnext/education/doctype/quiz/test_quiz.js
deleted file mode 100644
index 147d139..0000000
--- a/erpnext/education/doctype/quiz/test_quiz.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quiz", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Quiz
-		() => frappe.tests.make('Quiz', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/quiz_activity/test_quiz_activity.js b/erpnext/education/doctype/quiz_activity/test_quiz_activity.js
deleted file mode 100644
index 94b5ab7..0000000
--- a/erpnext/education/doctype/quiz_activity/test_quiz_activity.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quiz Activity", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Quiz Activity
-		() => frappe.tests.make('Quiz Activity', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/quiz_result/test_quiz_result.js b/erpnext/education/doctype/quiz_result/test_quiz_result.js
deleted file mode 100644
index 43f53a1..0000000
--- a/erpnext/education/doctype/quiz_result/test_quiz_result.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quiz Result", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Quiz Result
-		() => frappe.tests.make('Quiz Result', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/school_house/test_school_house.js b/erpnext/education/doctype/school_house/test_school_house.js
deleted file mode 100644
index dde63ec..0000000
--- a/erpnext/education/doctype/school_house/test_school_house.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: School House", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new School House
-		() => frappe.tests.make('School House', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/student/test_student.js b/erpnext/education/doctype/student/test_student.js
deleted file mode 100644
index e18d39a..0000000
--- a/erpnext/education/doctype/student/test_student.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Student", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Student
-		() => frappe.tests.make('Student', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/student_language/test_student_language.js b/erpnext/education/doctype/student_language/test_student_language.js
deleted file mode 100644
index 9b25569..0000000
--- a/erpnext/education/doctype/student_language/test_student_language.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Student Language", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Student Language
-		() => frappe.tests.make('Student Language', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/student_report_generation_tool/test_student_report_generation_tool.js b/erpnext/education/doctype/student_report_generation_tool/test_student_report_generation_tool.js
deleted file mode 100644
index 10be092..0000000
--- a/erpnext/education/doctype/student_report_generation_tool/test_student_report_generation_tool.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Student Report Generation Tool", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Student Report Generation Tool
-		() => frappe.tests.make('Student Report Generation Tool', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/topic/test_topic.js b/erpnext/education/doctype/topic/test_topic.js
deleted file mode 100644
index 4460b79..0000000
--- a/erpnext/education/doctype/topic/test_topic.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Topic", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Topic
-		() => frappe.tests.make('Topic', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/education/doctype/topic_content/test_topic_content.js b/erpnext/education/doctype/topic_content/test_topic_content.js
deleted file mode 100644
index bf9a62d..0000000
--- a/erpnext/education/doctype/topic_content/test_topic_content.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Topic Content", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Topic Content
-		() => frappe.tests.make('Topic Content', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.js b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.js
deleted file mode 100644
index 9c89909..0000000
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Amazon MWS Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Amazon MWS Settings
-		() => frappe.tests.make('Amazon MWS Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.js b/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.js
deleted file mode 100644
index caa9399..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: GoCardless Mandate", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new GoCardless Mandate
-		() => frappe.tests.make('GoCardless Mandate', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.js b/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.js
deleted file mode 100644
index b6daad8..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: GoCardless Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new GoCardless Settings
-		() => frappe.tests.make('GoCardless Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.js
deleted file mode 100644
index dc91347..0000000
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Plaid Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Plaid Settings
-		() => frappe.tests.make('Plaid Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.js b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.js
deleted file mode 100644
index b71d704..0000000
--- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: QuickBooks Migrator", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new QuickBooks Migrator
-		() => frappe.tests.make('QuickBooks Migrator', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.js
deleted file mode 100644
index 433c5e2..0000000
--- a/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Tally Migration", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Tally Migration
-		() => frappe.tests.make('Tally Migration', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.js b/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.js
deleted file mode 100644
index ea06ab2..0000000
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Woocommerce Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Woocommerce Settings
-		() => frappe.tests.make('Woocommerce Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/erpnext_integrations/stripe_integration.py b/erpnext/erpnext_integrations/stripe_integration.py
index 108b4c0..820c740 100644
--- a/erpnext/erpnext_integrations/stripe_integration.py
+++ b/erpnext/erpnext_integrations/stripe_integration.py
@@ -2,11 +2,12 @@
 # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-from __future__ import unicode_literals
+import stripe
+
 import frappe
 from frappe import _
 from frappe.integrations.utils import create_request_log
-import stripe
+
 
 def create_stripe_subscription(gateway_controller, data):
 	stripe_settings = frappe.get_doc("Stripe Settings", gateway_controller)
@@ -23,31 +24,38 @@
 	except Exception:
 		frappe.log_error(frappe.get_traceback())
 		return{
-			"redirect_to": frappe.redirect_to_message(_('Server Error'), _("It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.")),
+			"redirect_to": frappe.redirect_to_message(
+				_('Server Error'),
+				_("It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.")
+			),
 			"status": 401
 		}
 
 
 def create_subscription_on_stripe(stripe_settings):
-		items = []
-		for payment_plan in stripe_settings.payment_plans:
-			plan = frappe.db.get_value("Subscription Plan", payment_plan.plan, "payment_plan_id")
-			items.append({"plan": plan, "quantity": payment_plan.qty})
+	items = []
+	for payment_plan in stripe_settings.payment_plans:
+		plan = frappe.db.get_value("Subscription Plan", payment_plan.plan, "product_price_id")
+		items.append({"price": plan, "quantity": payment_plan.qty})
 
-		try:
-			customer = stripe.Customer.create(description=stripe_settings.data.payer_name, email=stripe_settings.data.payer_email, source=stripe_settings.data.stripe_token_id)
-			subscription = stripe.Subscription.create(customer=customer, items=items)
+	try:
+		customer = stripe.Customer.create(
+			source=stripe_settings.data.stripe_token_id,
+			description=stripe_settings.data.payer_name,
+			email=stripe_settings.data.payer_email
+		)
 
-			if subscription.status == "active":
-				stripe_settings.integration_request.db_set('status', 'Completed', update_modified=False)
-				stripe_settings.flags.status_changed_to = "Completed"
+		subscription = stripe.Subscription.create(customer=customer, items=items)
 
-			else:
-				stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False)
-				frappe.log_error('Subscription N°: ' + subscription.id, 'Stripe Payment not completed')
+		if subscription.status == "active":
+			stripe_settings.integration_request.db_set('status', 'Completed', update_modified=False)
+			stripe_settings.flags.status_changed_to = "Completed"
 
-		except Exception:
+		else:
 			stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False)
-			frappe.log_error(frappe.get_traceback())
+			frappe.log_error('Subscription N°: ' + subscription.id, 'Stripe Payment not completed')
+	except Exception:
+		stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False)
+		frappe.log_error(frappe.get_traceback())
 
-		return stripe_settings.finalize_request()
+	return stripe_settings.finalize_request()
diff --git a/erpnext/healthcare/doctype/antibiotic/test_antibiotic.js b/erpnext/healthcare/doctype/antibiotic/test_antibiotic.js
deleted file mode 100644
index b92103d..0000000
--- a/erpnext/healthcare/doctype/antibiotic/test_antibiotic.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Antibiotic", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Antibiotic
-		() => frappe.tests.make('Antibiotic', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/appointment_type/test_appointment_type.js b/erpnext/healthcare/doctype/appointment_type/test_appointment_type.js
deleted file mode 100644
index 93274e5..0000000
--- a/erpnext/healthcare/doctype/appointment_type/test_appointment_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Appointment Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Appointment Type
-		() => frappe.tests.make('Appointment Type', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.js
deleted file mode 100644
index 80ef3d5..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Clinical Procedure", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Clinical Procedure
-		() => frappe.tests.make('Clinical Procedure', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/test_clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/test_clinical_procedure_template.js
deleted file mode 100644
index 1dde8b5..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_template/test_clinical_procedure_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Clinical Procedure Template", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Clinical Procedure Template
-		() => frappe.tests.make('Clinical Procedure Template', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/complaint/test_complaint.js b/erpnext/healthcare/doctype/complaint/test_complaint.js
deleted file mode 100644
index 9ff44d8..0000000
--- a/erpnext/healthcare/doctype/complaint/test_complaint.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Complaint", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Complaint
-		() => frappe.tests.make('Complaint', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/diagnosis/test_diagnosis.js b/erpnext/healthcare/doctype/diagnosis/test_diagnosis.js
deleted file mode 100644
index cacfef5..0000000
--- a/erpnext/healthcare/doctype/diagnosis/test_diagnosis.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Diagnosis", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Diagnosis
-		() => frappe.tests.make('Diagnosis', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/dosage_form/test_dosage_form.js b/erpnext/healthcare/doctype/dosage_form/test_dosage_form.js
deleted file mode 100644
index ba54ab1..0000000
--- a/erpnext/healthcare/doctype/dosage_form/test_dosage_form.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Dosage Form", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Dosage Form
-		() => frappe.tests.make('Dosage Form', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json
index d91e6bf..a65c566 100644
--- a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json
+++ b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json
@@ -56,6 +56,7 @@
    "reqd": 1
   },
   {
+   "allow_in_quick_entry": 1,
    "fieldname": "dosage_form",
    "fieldtype": "Link",
    "ignore_user_permissions": 1,
@@ -109,7 +110,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-09-30 23:32:09.495288",
+ "modified": "2021-06-11 11:53:06.343704",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Drug Prescription",
diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.json b/erpnext/healthcare/doctype/fee_validity/fee_validity.json
index b001bf0..d76b42e 100644
--- a/erpnext/healthcare/doctype/fee_validity/fee_validity.json
+++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.json
@@ -46,13 +46,13 @@
   {
    "fieldname": "visited",
    "fieldtype": "Int",
-   "label": "Visited yet",
+   "label": "Visits Completed",
    "read_only": 1
   },
   {
    "fieldname": "valid_till",
    "fieldtype": "Date",
-   "label": "Valid till",
+   "label": "Valid Till",
    "read_only": 1
   },
   {
@@ -106,7 +106,7 @@
  ],
  "in_create": 1,
  "links": [],
- "modified": "2020-03-17 20:25:06.487418",
+ "modified": "2021-08-26 10:51:05.609349",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Fee Validity",
diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.py b/erpnext/healthcare/doctype/fee_validity/fee_validity.py
index 5b9c179..59586e0 100644
--- a/erpnext/healthcare/doctype/fee_validity/fee_validity.py
+++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.py
@@ -11,7 +11,6 @@
 class FeeValidity(Document):
 	def validate(self):
 		self.update_status()
-		self.set_start_date()
 
 	def update_status(self):
 		if self.visited >= self.max_visits:
@@ -19,13 +18,6 @@
 		else:
 			self.status = 'Pending'
 
-	def set_start_date(self):
-		self.start_date = getdate()
-		for appointment in self.ref_appointments:
-			appointment_date = frappe.db.get_value('Patient Appointment', appointment.appointment, 'appointment_date')
-			if getdate(appointment_date) < self.start_date:
-				self.start_date = getdate(appointment_date)
-
 
 def create_fee_validity(appointment):
 	if not check_is_new_patient(appointment):
@@ -36,11 +28,9 @@
 	fee_validity.patient = appointment.patient
 	fee_validity.max_visits = frappe.db.get_single_value('Healthcare Settings', 'max_visits') or 1
 	valid_days = frappe.db.get_single_value('Healthcare Settings', 'valid_days') or 1
-	fee_validity.visited = 1
+	fee_validity.visited = 0
+	fee_validity.start_date = getdate(appointment.appointment_date)
 	fee_validity.valid_till = getdate(appointment.appointment_date) + datetime.timedelta(days=int(valid_days))
-	fee_validity.append('ref_appointments', {
-		'appointment': appointment.name
-	})
 	fee_validity.save(ignore_permissions=True)
 	return fee_validity
 
diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.js b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.js
deleted file mode 100644
index 0ebb974..0000000
--- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Fee Validity", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Fee Validity
-		() => frappe.tests.make('Fee Validity', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py
index 82e7136..4a17872 100644
--- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py
+++ b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py
@@ -22,14 +22,14 @@
 		item = create_healthcare_service_items()
 		healthcare_settings = frappe.get_single("Healthcare Settings")
 		healthcare_settings.enable_free_follow_ups = 1
-		healthcare_settings.max_visits = 2
+		healthcare_settings.max_visits = 1
 		healthcare_settings.valid_days = 7
 		healthcare_settings.automate_appointment_invoicing = 1
 		healthcare_settings.op_consulting_charge_item = item
 		healthcare_settings.save(ignore_permissions=True)
 		patient, medical_department, practitioner = create_healthcare_docs()
 
-		# For first appointment, invoice is generated
+		# For first appointment, invoice is generated. First appointment not considered in fee validity
 		appointment = create_appointment(patient, practitioner, nowdate())
 		invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
 		self.assertEqual(invoiced, 1)
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/test_healthcare_practitioner.js b/erpnext/healthcare/doctype/healthcare_practitioner/test_healthcare_practitioner.js
deleted file mode 100644
index 75aa208..0000000
--- a/erpnext/healthcare/doctype/healthcare_practitioner/test_healthcare_practitioner.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Healthcare Practitioner", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Healthcare Practitioner
-		() => frappe.tests.make('Healthcare Practitioner', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/test_healthcare_service_unit.js b/erpnext/healthcare/doctype/healthcare_service_unit/test_healthcare_service_unit.js
deleted file mode 100644
index a67a411..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit/test_healthcare_service_unit.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Healthcare Service Unit", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Healthcare Service Unit
-		() => frappe.tests.make('Healthcare Service Unit', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.js b/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.js
deleted file mode 100644
index 6db8f9e..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Healthcare Service Unit Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Healthcare Service Unit Type
-		() => frappe.tests.make('Healthcare Service Unit Type', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.js
deleted file mode 100644
index ca10925..0000000
--- a/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Healthcare Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Healthcare Settings
-		() => frappe.tests.make('Healthcare Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.js b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.js
deleted file mode 100644
index 1ce9afa..0000000
--- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Inpatient Record", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Inpatient Record
-		() => frappe.tests.make('Inpatient Record', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.js b/erpnext/healthcare/doctype/lab_test/test_lab_test.js
deleted file mode 100644
index 57cb22b..0000000
--- a/erpnext/healthcare/doctype/lab_test/test_lab_test.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Lab Test", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Lab Test
-		() => frappe.tests.make('Lab Test', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.js b/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.js
deleted file mode 100644
index ace60de..0000000
--- a/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Lab Test Sample", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Lab Test Sample
-		() => frappe.tests.make('Lab Test Sample', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.js b/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.js
deleted file mode 100644
index 7c2ec8c..0000000
--- a/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Lab Test Template", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Lab Test Template
-		() => frappe.tests.make('Lab Test Template', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.js b/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.js
deleted file mode 100644
index 1328dda..0000000
--- a/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Lab Test UOM", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Lab Test UOM
-		() => frappe.tests.make('Lab Test UOM', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/medical_code/test_medical_code.js b/erpnext/healthcare/doctype/medical_code/test_medical_code.js
deleted file mode 100644
index 8cc7c40..0000000
--- a/erpnext/healthcare/doctype/medical_code/test_medical_code.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Medical Code", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Medical Code
-		() => frappe.tests.make('Medical Code', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.js b/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.js
deleted file mode 100644
index 6ab6d53..0000000
--- a/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Medical Code Standard", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Medical Code Standard
-		() => frappe.tests.make('Medical Code Standard', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/medical_department/test_medical_department.js b/erpnext/healthcare/doctype/medical_department/test_medical_department.js
deleted file mode 100644
index fdf4971..0000000
--- a/erpnext/healthcare/doctype/medical_department/test_medical_department.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Medical Department", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Medical Department
-		() => frappe.tests.make('Medical Department', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/organism/test_organism.js b/erpnext/healthcare/doctype/organism/test_organism.js
deleted file mode 100644
index d57e553..0000000
--- a/erpnext/healthcare/doctype/organism/test_organism.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Organism", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Organism
-		() => frappe.tests.make('Organism', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py
index 56a3400..f77ad70 100644
--- a/erpnext/healthcare/doctype/patient/patient.py
+++ b/erpnext/healthcare/doctype/patient/patient.py
@@ -102,12 +102,19 @@
 
 		return name
 
+	@property
+	def age(self):
+		if not self.dob:
+			return
+		dob = getdate(self.dob)
+		age = dateutil.relativedelta.relativedelta(getdate(), dob)
+		return age
+
 	def get_age(self):
-		age_str = ''
-		if self.dob:
-			dob = getdate(self.dob)
-			age = dateutil.relativedelta.relativedelta(getdate(), dob)
-			age_str = str(age.years) + ' ' + _("Years(s)") + ' ' + str(age.months) + ' ' + _("Month(s)") + ' ' + str(age.days) + ' ' + _("Day(s)")
+		age = self.age
+		if not age:
+			return
+		age_str = str(age.years) + ' ' + _("Years(s)") + ' ' + str(age.months) + ' ' + _("Month(s)") + ' ' + str(age.days) + ' ' + _("Day(s)")
 		return age_str
 
 	@frappe.whitelist()
diff --git a/erpnext/healthcare/doctype/patient/test_patient.js b/erpnext/healthcare/doctype/patient/test_patient.js
deleted file mode 100644
index e1d9ecb..0000000
--- a/erpnext/healthcare/doctype/patient/test_patient.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Patient", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially('Patient', [
-		// insert a new Patient
-		() => frappe.tests.make([
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
index 05e2cd3..7db4fa6 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -109,9 +109,13 @@
 					frappe.db.set_value('Patient Appointment', self.name, 'notes', comments)
 
 	def update_fee_validity(self):
+		if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'):
+			return
+
 		fee_validity = manage_fee_validity(self)
 		if fee_validity:
-			frappe.msgprint(_('{0} has fee validity till {1}').format(self.patient, fee_validity.valid_till))
+			frappe.msgprint(_('{0}: {1} has fee validity till {2}').format(self.patient,
+				frappe.bold(self.patient_name), fee_validity.valid_till))
 
 	@frappe.whitelist()
 	def get_therapy_types(self):
diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.js
deleted file mode 100644
index 71fc177..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Patient Appointment", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Patient Appointment
-		() => frappe.tests.make('Patient Appointment', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
index 9c3392c..157b3e1 100644
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
@@ -107,14 +107,17 @@
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1)
 		appointment = create_appointment(patient, practitioner, nowdate())
-		fee_validity = frappe.db.get_value('Fee Validity Reference', {'appointment': appointment.name}, 'parent')
+		fee_validity = frappe.db.get_value('Fee Validity', {'patient': patient, 'practitioner': practitioner})
 		# fee validity created
 		self.assertTrue(fee_validity)
 
-		visited = frappe.db.get_value('Fee Validity', fee_validity, 'visited')
+		# first follow up appointment
+		appointment = create_appointment(patient, practitioner, nowdate())
+		self.assertEqual(frappe.db.get_value('Fee Validity', fee_validity, 'visited'), 1)
+
 		update_status(appointment.name, 'Cancelled')
 		# check fee validity updated
-		self.assertEqual(frappe.db.get_value('Fee Validity', fee_validity, 'visited'), visited - 1)
+		self.assertEqual(frappe.db.get_value('Fee Validity', fee_validity, 'visited'), 0)
 
 		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
 		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js
index aaeaa69..c346626 100644
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js
+++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js
@@ -185,7 +185,42 @@
 			};
 			frm.set_value(values);
 		}
-	}
+	},
+
+	get_applicable_treatment_plans: function(frm) {
+		frappe.call({
+			method: 'get_applicable_treatment_plans',
+			doc: frm.doc,
+			args: {'encounter': frm.doc},
+			freeze: true,
+			freeze_message: __('Fetching Treatment Plans'),
+			callback: function() {
+				new frappe.ui.form.MultiSelectDialog({
+					doctype: "Treatment Plan Template",
+					target: this.cur_frm,
+					setters: {
+						medical_department: "",
+					},
+					action(selections) {
+						frappe.call({
+							method: 'set_treatment_plans',
+							doc: frm.doc,
+							args: selections,
+						}).then(() => {
+							frm.refresh_field('drug_prescription');
+							frm.refresh_field('procedure_prescription');
+							frm.refresh_field('lab_test_prescription');
+							frm.refresh_field('therapies');
+						});
+						cur_dialog.hide();
+					}
+				});
+
+
+			}
+		});
+	},
+
 });
 
 var schedule_inpatient = function(frm) {
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
index b646ff9..994597d 100644
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
+++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
@@ -31,6 +31,7 @@
   "sb_symptoms",
   "symptoms",
   "symptoms_in_print",
+  "get_applicable_treatment_plans",
   "physical_examination",
   "diagnosis",
   "diagnosis_in_print",
@@ -324,11 +325,17 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.patient",
+   "fieldname": "get_applicable_treatment_plans",
+   "fieldtype": "Button",
+   "label": "Get Applicable Treatment Plans"
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-30 10:39:00.783119",
+ "modified": "2021-07-27 11:39:12.347704",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Patient Encounter",
@@ -358,4 +365,4 @@
  "title_field": "title",
  "track_changes": 1,
  "track_seen": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
index 2b3029e..7a745ae 100644
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
+++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
@@ -10,6 +10,7 @@
 from frappe import _
 from frappe.model.mapper import get_mapped_doc
 
+
 class PatientEncounter(Document):
 	def validate(self):
 		self.set_title()
@@ -33,6 +34,85 @@
 		self.title = _('{0} with {1}').format(self.patient_name or self.patient,
 			self.practitioner_name or self.practitioner)[:100]
 
+	@frappe.whitelist()
+	@staticmethod
+	def get_applicable_treatment_plans(encounter):
+		patient = frappe.get_doc('Patient', encounter['patient'])
+
+		plan_filters = {}
+		plan_filters['name'] = ['in', []]
+
+		age = patient.age
+		if age:
+			plan_filters['patient_age_from'] = ['<=', age.years]
+			plan_filters['patient_age_to'] = ['>=', age.years]
+
+		gender = patient.sex
+		if gender:
+			plan_filters['gender'] = ['in', [gender, None]]
+
+		diagnosis = encounter.get('diagnosis')
+		if diagnosis:
+			diagnosis = [_diagnosis['diagnosis'] for _diagnosis in encounter['diagnosis']]
+			filters = [
+				['diagnosis', 'in', diagnosis],
+				['parenttype', '=', 'Treatment Plan Template'],
+			]
+			diagnosis = frappe.get_list('Patient Encounter Diagnosis', filters=filters, fields='*')
+			plan_names = [_diagnosis['parent'] for _diagnosis in diagnosis]
+			plan_filters['name'][1].extend(plan_names)
+
+		symptoms = encounter.get('symptoms')
+		if symptoms:
+			symptoms = [symptom['complaint'] for symptom in encounter['symptoms']]
+			filters = [
+				['complaint', 'in', symptoms],
+				['parenttype', '=', 'Treatment Plan Template'],
+			]
+			symptoms = frappe.get_list('Patient Encounter Symptom', filters=filters, fields='*')
+			plan_names = [symptom['parent'] for symptom in symptoms]
+			plan_filters['name'][1].extend(plan_names)
+
+		if not plan_filters['name'][1]:
+			plan_filters.pop('name')
+
+		plans = frappe.get_list('Treatment Plan Template', fields='*', filters=plan_filters)
+
+		return plans
+
+	@frappe.whitelist()
+	def set_treatment_plans(self, treatment_plans=None):
+		for treatment_plan in treatment_plans:
+			self.set_treatment_plan(treatment_plan)
+
+	def set_treatment_plan(self, plan):
+		plan_items = frappe.get_list('Treatment Plan Template Item', filters={'parent': plan}, fields='*')
+		for plan_item in plan_items:
+			self.set_treatment_plan_item(plan_item)
+
+		drugs = frappe.get_list('Drug Prescription', filters={'parent': plan}, fields='*')
+		for drug in drugs:
+			self.append('drug_prescription', drug)
+
+		self.save()
+
+	def set_treatment_plan_item(self, plan_item):
+		if plan_item.type == 'Clinical Procedure Template':
+			self.append('procedure_prescription', {
+				'procedure': plan_item.template
+			})
+
+		if plan_item.type == 'Lab Test Template':
+			self.append('lab_test_prescription', {
+				'lab_test_code': plan_item.template
+			})
+
+		if plan_item.type == 'Therapy Type':
+			self.append('therapies', {
+				'therapy_type': plan_item.template
+			})
+
+
 @frappe.whitelist()
 def make_ip_medication_order(source_name, target_doc=None):
 	def set_missing_values(source, target):
diff --git a/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.js
deleted file mode 100644
index 1baabf7..0000000
--- a/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Patient Encounter", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Patient Encounter
-		() => frappe.tests.make('Patient Encounter', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.py
index f5df152..9697682 100644
--- a/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.py
+++ b/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.py
@@ -4,5 +4,82 @@
 from __future__ import unicode_literals
 import unittest
 
+import frappe
+from erpnext.healthcare.doctype.patient_encounter.patient_encounter import PatientEncounter
+
+
 class TestPatientEncounter(unittest.TestCase):
-	pass
+	def setUp(self):
+		try:
+			gender_m = frappe.get_doc({
+				'doctype': 'Gender',
+				'gender': 'MALE'
+			}).insert()
+			gender_f = frappe.get_doc({
+				'doctype': 'Gender',
+				'gender': 'FEMALE'
+			}).insert()
+		except frappe.exceptions.DuplicateEntryError:
+			gender_m = frappe.get_doc({
+				'doctype': 'Gender',
+				'gender': 'MALE'
+			})
+			gender_f = frappe.get_doc({
+				'doctype': 'Gender',
+				'gender': 'FEMALE'
+			})
+
+		self.patient_male = frappe.get_doc({
+			'doctype': 'Patient',
+			'first_name': 'John',
+			'sex': gender_m.gender,
+		}).insert()
+		self.patient_female = frappe.get_doc({
+			'doctype': 'Patient',
+			'first_name': 'Curie',
+			'sex': gender_f.gender,
+		}).insert()
+		self.practitioner = frappe.get_doc({
+			'doctype': 'Healthcare Practitioner',
+			'first_name': 'Doc',
+			'sex': 'MALE',
+		}).insert()
+		try:
+			self.care_plan_male = frappe.get_doc({
+				'doctype': 'Treatment Plan Template',
+				'template_name': 'test plan - m',
+				'gender': gender_m.gender,
+			}).insert()
+			self.care_plan_female = frappe.get_doc({
+				'doctype': 'Treatment Plan Template',
+				'template_name': 'test plan - f',
+				'gender': gender_f.gender,
+			}).insert()
+		except frappe.exceptions.DuplicateEntryError:
+			self.care_plan_male = frappe.get_doc({
+				'doctype': 'Treatment Plan Template',
+				'template_name': 'test plan - m',
+				'gender': gender_m.gender,
+			})
+			self.care_plan_female = frappe.get_doc({
+				'doctype': 'Treatment Plan Template',
+				'template_name': 'test plan - f',
+				'gender': gender_f.gender,
+			})
+
+	def test_treatment_plan_template_filter(self):
+		encounter = frappe.get_doc({
+			'doctype': 'Patient Encounter',
+			'patient': self.patient_male.name,
+			'practitioner': self.practitioner.name,
+		}).insert()
+		plans = PatientEncounter.get_applicable_treatment_plans(encounter.as_dict())
+		self.assertEqual(plans[0]['name'], self.care_plan_male.template_name)
+
+		encounter = frappe.get_doc({
+			'doctype': 'Patient Encounter',
+			'patient': self.patient_female.name,
+			'practitioner': self.practitioner.name,
+		}).insert()
+		plans = PatientEncounter.get_applicable_treatment_plans(encounter.as_dict())
+		self.assertEqual(plans[0]['name'], self.care_plan_female.template_name)
diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.js b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.js
deleted file mode 100644
index 66dda09..0000000
--- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Patient Medical Record", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Patient Medical Record
-		() => frappe.tests.make('Patient Medical Record', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/practitioner_schedule/test_practitioner_schedule.js b/erpnext/healthcare/doctype/practitioner_schedule/test_practitioner_schedule.js
deleted file mode 100644
index 32dac2c..0000000
--- a/erpnext/healthcare/doctype/practitioner_schedule/test_practitioner_schedule.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Practitioner Schedule", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Practitioner Schedule
-		() => frappe.tests.make('Practitioner Schedule', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.js b/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.js
deleted file mode 100644
index 009614f..0000000
--- a/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Prescription Dosage", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Prescription Dosage
-		() => frappe.tests.make('Prescription Dosage', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.js b/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.js
deleted file mode 100644
index 4971e79..0000000
--- a/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Prescription Duration", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Prescription Duration
-		() => frappe.tests.make('Prescription Duration', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/sample_collection/test_sample_collection.js b/erpnext/healthcare/doctype/sample_collection/test_sample_collection.js
deleted file mode 100644
index 2b4aed7..0000000
--- a/erpnext/healthcare/doctype/sample_collection/test_sample_collection.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sample Collection", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Sample Collection
-		() => frappe.tests.make('Sample Collection', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/healthcare/doctype/sensitivity/test_sensitivity.js b/erpnext/healthcare/doctype/sensitivity/test_sensitivity.js
deleted file mode 100644
index c2cf406..0000000
--- a/erpnext/healthcare/doctype/sensitivity/test_sensitivity.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sensitivity", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Sensitivity
-		() => frappe.tests.make('Sensitivity', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/healthcare/doctype/treatment_plan_template/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/healthcare/doctype/treatment_plan_template/__init__.py
diff --git a/erpnext/healthcare/doctype/treatment_plan_template/test_records.json b/erpnext/healthcare/doctype/treatment_plan_template/test_records.json
new file mode 100644
index 0000000..d661b43
--- /dev/null
+++ b/erpnext/healthcare/doctype/treatment_plan_template/test_records.json
@@ -0,0 +1,7 @@
+[
+  {
+    "doctype": "Treatment Plan Template",
+    "template_name": "Chemo",
+    "patient_age_from": 21
+  }
+]
diff --git a/erpnext/healthcare/doctype/treatment_plan_template/test_treatment_plan_template.py b/erpnext/healthcare/doctype/treatment_plan_template/test_treatment_plan_template.py
new file mode 100644
index 0000000..21ede71
--- /dev/null
+++ b/erpnext/healthcare/doctype/treatment_plan_template/test_treatment_plan_template.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestTreatmentPlanTemplate(unittest.TestCase):
+	pass
diff --git a/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template.js b/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template.js
new file mode 100644
index 0000000..986c3cb
--- /dev/null
+++ b/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template.js
@@ -0,0 +1,14 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Treatment Plan Template', {
+	refresh: function (frm) {
+		frm.set_query('type', 'items', function () {
+			return {
+				filters: {
+					'name': ['in', ['Lab Test Template', 'Clinical Procedure Template', 'Therapy Type']],
+				}
+			};
+		});
+	},
+});
diff --git a/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template.json b/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template.json
new file mode 100644
index 0000000..85a312f
--- /dev/null
+++ b/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template.json
@@ -0,0 +1,189 @@
+{
+ "actions": [],
+ "autoname": "field:template_name",
+ "creation": "2021-06-10 10:14:17.901273",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "section_break_1",
+  "template_name",
+  "description",
+  "practitioners",
+  "disabled",
+  "column_break_1",
+  "medical_department",
+  "goal",
+  "order_group",
+  "section_break_8",
+  "patient_age_from",
+  "complaints",
+  "gender",
+  "column_break_12",
+  "patient_age_to",
+  "diagnosis",
+  "plan_items_section",
+  "items",
+  "drugs"
+ ],
+ "fields": [
+  {
+   "fieldname": "section_break_1",
+   "fieldtype": "Section Break",
+   "label": "Plan Details"
+  },
+  {
+   "fieldname": "medical_department",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Medical Department",
+   "options": "Medical Department"
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "label": "Description"
+  },
+  {
+   "fieldname": "goal",
+   "fieldtype": "Small Text",
+   "label": "Goal"
+  },
+  {
+   "fieldname": "practitioners",
+   "fieldtype": "Table MultiSelect",
+   "label": "Practitioners",
+   "options": "Treatment Plan Template Practitioner"
+  },
+  {
+   "fieldname": "order_group",
+   "fieldtype": "Link",
+   "label": "Order Group",
+   "options": "Patient Encounter",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_8",
+   "fieldtype": "Section Break",
+   "label": "Plan Conditions"
+  },
+  {
+   "fieldname": "template_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Template Name",
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "patient_age_from",
+   "fieldtype": "Int",
+   "label": "Patient Age From",
+   "non_negative": 1
+  },
+  {
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "patient_age_to",
+   "fieldtype": "Int",
+   "label": "Patient Age To",
+   "non_negative": 1
+  },
+  {
+   "fieldname": "gender",
+   "fieldtype": "Link",
+   "label": "Gender",
+   "options": "Gender"
+  },
+  {
+   "fieldname": "complaints",
+   "fieldtype": "Table MultiSelect",
+   "label": "Complaints",
+   "options": "Patient Encounter Symptom"
+  },
+  {
+   "fieldname": "diagnosis",
+   "fieldtype": "Table MultiSelect",
+   "label": "Diagnosis",
+   "options": "Patient Encounter Diagnosis"
+  },
+  {
+   "fieldname": "plan_items_section",
+   "fieldtype": "Section Break",
+   "label": "Plan Items"
+  },
+  {
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "label": "Items",
+   "options": "Treatment Plan Template Item"
+  },
+  {
+   "fieldname": "drugs",
+   "fieldtype": "Table",
+   "label": "Drugs",
+   "options": "Drug Prescription"
+  },
+  {
+   "default": "0",
+   "fieldname": "disabled",
+   "fieldtype": "Check",
+   "label": "Disabled"
+  },
+  {
+   "fieldname": "column_break_1",
+   "fieldtype": "Column Break"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-08-18 02:41:58.354296",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Treatment Plan Template",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Physician",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Healthcare Administrator",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "template_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template.py b/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template.py
new file mode 100644
index 0000000..a92e266
--- /dev/null
+++ b/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+
+class TreatmentPlanTemplate(Document):
+	def validate(self):
+		self.validate_age()
+
+	def validate_age(self):
+		if self.patient_age_from and self.patient_age_from < 0:
+			frappe.throw(_('Patient Age From cannot be less than 0'))
+		if self.patient_age_to and self.patient_age_to < 0:
+			frappe.throw(_('Patient Age To cannot be less than 0'))
+		if self.patient_age_to and self.patient_age_from and \
+			self.patient_age_to < self.patient_age_from:
+			frappe.throw(_('Patient Age To cannot be less than Patient Age From'))
diff --git a/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template_list.js b/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template_list.js
new file mode 100644
index 0000000..7ab31df
--- /dev/null
+++ b/erpnext/healthcare/doctype/treatment_plan_template/treatment_plan_template_list.js
@@ -0,0 +1,10 @@
+frappe.listview_settings['Treatment Plan Template'] = {
+	get_indicator: function(doc) {
+		var colors = {
+			1: 'gray',
+			0: 'blue',
+		};
+		let label  = doc.disabled == 1 ? 'Disabled' : 'Enabled';
+		return [__(label), colors[doc.disabled], 'disable,=,' + doc.disabled];
+	}
+};
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/healthcare/doctype/treatment_plan_template_item/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/healthcare/doctype/treatment_plan_template_item/__init__.py
diff --git a/erpnext/healthcare/doctype/treatment_plan_template_item/treatment_plan_template_item.json b/erpnext/healthcare/doctype/treatment_plan_template_item/treatment_plan_template_item.json
new file mode 100644
index 0000000..20a9d67
--- /dev/null
+++ b/erpnext/healthcare/doctype/treatment_plan_template_item/treatment_plan_template_item.json
@@ -0,0 +1,55 @@
+{
+ "actions": [],
+ "creation": "2021-06-10 11:47:29.194795",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "type",
+  "template",
+  "qty",
+  "instructions"
+ ],
+ "fields": [
+  {
+   "fieldname": "type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Type",
+   "options": "DocType",
+   "reqd": 1
+  },
+  {
+   "fieldname": "template",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "label": "Template",
+   "options": "type",
+   "reqd": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "qty",
+   "fieldtype": "Int",
+   "label": "Qty"
+  },
+  {
+   "fieldname": "instructions",
+   "fieldtype": "Small Text",
+   "in_list_view": 1,
+   "label": "Instructions"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-08-17 11:19:03.515441",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Treatment Plan Template Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/treatment_plan_template_item/treatment_plan_template_item.py b/erpnext/healthcare/doctype/treatment_plan_template_item/treatment_plan_template_item.py
new file mode 100644
index 0000000..5f58b06
--- /dev/null
+++ b/erpnext/healthcare/doctype/treatment_plan_template_item/treatment_plan_template_item.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+class TreatmentPlanTemplateItem(Document):
+	pass
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/healthcare/doctype/treatment_plan_template_practitioner/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/healthcare/doctype/treatment_plan_template_practitioner/__init__.py
diff --git a/erpnext/healthcare/doctype/treatment_plan_template_practitioner/treatment_plan_template_practitioner.json b/erpnext/healthcare/doctype/treatment_plan_template_practitioner/treatment_plan_template_practitioner.json
new file mode 100644
index 0000000..04da387
--- /dev/null
+++ b/erpnext/healthcare/doctype/treatment_plan_template_practitioner/treatment_plan_template_practitioner.json
@@ -0,0 +1,32 @@
+{
+ "actions": [],
+ "creation": "2021-06-10 10:37:56.669416",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "practitioner"
+ ],
+ "fields": [
+  {
+   "fieldname": "practitioner",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Practitioner",
+   "options": "Healthcare Practitioner",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-06-11 16:05:06.733299",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Treatment Plan Template Practitioner",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/treatment_plan_template_practitioner/treatment_plan_template_practitioner.py b/erpnext/healthcare/doctype/treatment_plan_template_practitioner/treatment_plan_template_practitioner.py
new file mode 100644
index 0000000..6d34568
--- /dev/null
+++ b/erpnext/healthcare/doctype/treatment_plan_template_practitioner/treatment_plan_template_practitioner.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+class TreatmentPlanTemplatePractitioner(Document):
+	pass
diff --git a/erpnext/healthcare/doctype/vital_signs/test_vital_signs.js b/erpnext/healthcare/doctype/vital_signs/test_vital_signs.js
deleted file mode 100644
index f4ab446..0000000
--- a/erpnext/healthcare/doctype/vital_signs/test_vital_signs.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Vital Signs", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Vital Signs
-		() => frappe.tests.make('Vital Signs', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room/test_hotel_room.js b/erpnext/hotels/doctype/hotel_room/test_hotel_room.js
deleted file mode 100644
index 8b2b833..0000000
--- a/erpnext/hotels/doctype/hotel_room/test_hotel_room.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Hotel Room
-		() => frappe.tests.make('Hotel Room', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.js b/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.js
deleted file mode 100644
index f1ebad4..0000000
--- a/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room Package", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Hotel Room Package
-		() => frappe.tests.make('Hotel Room Package', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.js b/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.js
deleted file mode 100644
index ba0d1fd..0000000
--- a/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room Pricing", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Hotel Room Pricing
-		() => frappe.tests.make('Hotel Room Pricing', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.js b/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.js
deleted file mode 100644
index 73a561c..0000000
--- a/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room Pricing Package", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Hotel Room Pricing Package
-		() => frappe.tests.make('Hotel Room Pricing Package', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.js b/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.js
deleted file mode 100644
index 2897139..0000000
--- a/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room Reservation", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Hotel Room Reservation
-		() => frappe.tests.make('Hotel Room Reservation', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.js b/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.js
deleted file mode 100644
index e2dd578..0000000
--- a/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Hotel Room Type
-		() => frappe.tests.make('Hotel Room Type', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.js b/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.js
deleted file mode 100644
index bc0b7f8..0000000
--- a/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Hotel Settings
-		() => frappe.tests.make('Hotel Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.js b/erpnext/hr/doctype/attendance_request/test_attendance_request.js
deleted file mode 100644
index d40ec61..0000000
--- a/erpnext/hr/doctype/attendance_request/test_attendance_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Attendance Request", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Attendance Request
-		() => frappe.tests.make('Attendance Request', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/branch/test_branch.js b/erpnext/hr/doctype/branch/test_branch.js
deleted file mode 100644
index 82a6ae1..0000000
--- a/erpnext/hr/doctype/branch/test_branch.js
+++ /dev/null
@@ -1,23 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Branch [HR]", function (assert) {
-	assert.expect(1);
-	let done = assert.async();
-
-	frappe.run_serially([
-		// test branch creation
-		() => frappe.set_route("List", "Branch", "List"),
-		() => frappe.new_doc("Branch"),
-		() => frappe.timeout(1),
-		() => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
-		() => frappe.timeout(1),
-		() => cur_frm.set_value("branch", "Test Branch"),
-
-		// save form
-		() => cur_frm.save(),
-		() => frappe.timeout(1),
-		() => assert.equal("Test Branch", cur_frm.doc.branch,
-			'name of branch correctly saved'),
-		() => done()
-	]);
-});
diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.js b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.js
deleted file mode 100644
index bebcaac..0000000
--- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Compensatory Leave Request", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Compensatory Leave Request
-		() => frappe.tests.make('Compensatory Leave Request', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js
deleted file mode 100644
index 1533517..0000000
--- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Daily Work Summary", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Daily Work Summary
-		() => frappe.tests.make('Daily Work Summary', [
-			// values to be set
-			{ key: 'value' }
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/department/test_department.js b/erpnext/hr/doctype/department/test_department.js
deleted file mode 100644
index e73779c..0000000
--- a/erpnext/hr/doctype/department/test_department.js
+++ /dev/null
@@ -1,23 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Department [HR]", function (assert) {
-	assert.expect(1);
-	let done = assert.async();
-
-	frappe.run_serially([
-		// test department creation
-		() => frappe.set_route("List", "Department", "List"),
-		() => frappe.new_doc("Department"),
-		() => frappe.timeout(1),
-		() => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
-		() => frappe.timeout(1),
-		() => cur_frm.set_value("department_name", "Test Department"),
-		() => cur_frm.set_value("leave_block_list", "Test Leave block list"),
-		// save form
-		() => cur_frm.save(),
-		() => frappe.timeout(1),
-		() => assert.equal("Test Department", cur_frm.doc.department_name,
-			'name of department correctly saved'),
-		() => done()
-	]);
-});
diff --git a/erpnext/hr/doctype/designation/test_designation.js b/erpnext/hr/doctype/designation/test_designation.js
deleted file mode 100644
index 00adf82..0000000
--- a/erpnext/hr/doctype/designation/test_designation.js
+++ /dev/null
@@ -1,23 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Designation [HR]", function (assert) {
-	assert.expect(1);
-	let done = assert.async();
-
-	frappe.run_serially([
-		// test designation creation
-		() => frappe.set_route("List", "Designation", "List"),
-		() => frappe.new_doc("Designation"),
-		() => frappe.timeout(1),
-		() => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
-		() => frappe.timeout(1),
-		() => cur_frm.set_value("designation_name", "Test Designation"),
-		() => cur_frm.set_value("description", "This designation is just for testing."),
-		// save form
-		() => cur_frm.save(),
-		() => frappe.timeout(1),
-		() => assert.equal("Test Designation", cur_frm.doc.designation_name,
-			'name of designation correctly saved'),
-		() => done()
-	]);
-});
diff --git a/erpnext/hr/doctype/driver/test_driver.js b/erpnext/hr/doctype/driver/test_driver.js
deleted file mode 100644
index ff9f61e..0000000
--- a/erpnext/hr/doctype/driver/test_driver.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Driver", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Driver
-		() => frappe.tests.make('Driver', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.js b/erpnext/hr/doctype/employee_advance/test_employee_advance.js
deleted file mode 100644
index 1b9ec6f..0000000
--- a/erpnext/hr/doctype/employee_advance/test_employee_advance.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Advance", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Advance
-		() => frappe.tests.make('Employee Advance', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json b/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json
index 65792b4..044a5a9 100644
--- a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json
+++ b/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2018-05-09 05:37:18.439763",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -7,6 +8,8 @@
   "activity_name",
   "user",
   "role",
+  "begin_on",
+  "duration",
   "column_break_3",
   "task",
   "task_weight",
@@ -16,12 +19,16 @@
  ],
  "fields": [
   {
+   "columns": 3,
    "fieldname": "activity_name",
    "fieldtype": "Data",
    "in_list_view": 1,
-   "label": "Activity Name"
+   "label": "Activity Name",
+   "reqd": 1
   },
   {
+   "columns": 2,
+   "depends_on": "eval:!doc.role",
    "fieldname": "user",
    "fieldtype": "Link",
    "in_list_view": 1,
@@ -29,9 +36,10 @@
    "options": "User"
   },
   {
+   "columns": 1,
+   "depends_on": "eval:!doc.user",
    "fieldname": "role",
    "fieldtype": "Link",
-   "in_list_view": 1,
    "label": "Role",
    "options": "Role"
   },
@@ -67,10 +75,25 @@
    "fieldname": "description",
    "fieldtype": "Text Editor",
    "label": "Description"
+  },
+  {
+   "columns": 2,
+   "fieldname": "duration",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Duration (Days)"
+  },
+  {
+   "columns": 2,
+   "fieldname": "begin_on",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Begin On (Days)"
   }
  ],
  "istable": 1,
- "modified": "2019-06-03 19:22:42.965762",
+ "links": [],
+ "modified": "2021-07-30 15:55:22.470102",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee Boarding Activity",
diff --git a/erpnext/hr/doctype/employee_grade/test_employee_grade.js b/erpnext/hr/doctype/employee_grade/test_employee_grade.js
deleted file mode 100644
index d684fb2..0000000
--- a/erpnext/hr/doctype/employee_grade/test_employee_grade.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Grade", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Grade
-		() => frappe.tests.make('Employee Grade', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.js b/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.js
deleted file mode 100644
index 245cb32..0000000
--- a/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Health Insurance", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Health Insurance
-		() => frappe.tests.make('Employee Health Insurance', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json
index 673e228..fd877a6 100644
--- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json
+++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json
@@ -8,20 +8,24 @@
  "field_order": [
   "job_applicant",
   "job_offer",
-  "employee_name",
-  "employee",
-  "date_of_joining",
-  "boarding_status",
-  "notify_users_by_email",
-  "column_break_7",
   "employee_onboarding_template",
+  "column_break_7",
   "company",
+  "boarding_status",
+  "project",
+  "details_section",
+  "employee",
+  "employee_name",
   "department",
   "designation",
   "employee_grade",
-  "project",
+  "holiday_list",
+  "column_break_13",
+  "date_of_joining",
+  "boarding_begins_on",
   "table_for_activity",
   "activities",
+  "notify_users_by_email",
   "amended_from"
  ],
  "fields": [
@@ -58,7 +62,8 @@
    "fieldname": "date_of_joining",
    "fieldtype": "Date",
    "in_list_view": 1,
-   "label": "Date of Joining"
+   "label": "Date of Joining",
+   "reqd": 1
   },
   {
    "allow_on_submit": 1,
@@ -90,7 +95,8 @@
    "fieldname": "company",
    "fieldtype": "Link",
    "label": "Company",
-   "options": "Company"
+   "options": "Company",
+   "reqd": 1
   },
   {
    "fieldname": "department",
@@ -121,7 +127,8 @@
   },
   {
    "fieldname": "table_for_activity",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Onboarding Activities"
   },
   {
    "allow_on_submit": 1,
@@ -138,11 +145,32 @@
    "options": "Employee Onboarding",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "details_section",
+   "fieldtype": "Section Break",
+   "label": "Employee Details"
+  },
+  {
+   "fieldname": "column_break_13",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "boarding_begins_on",
+   "fieldtype": "Date",
+   "label": "Onboarding Begins On",
+   "reqd": 1
+  },
+  {
+   "fieldname": "holiday_list",
+   "fieldtype": "Link",
+   "label": "Holiday List",
+   "options": "Holiday List"
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-06-03 18:01:51.097927",
+ "modified": "2021-07-30 14:55:04.560683",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee Onboarding",
diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.js b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.js
deleted file mode 100644
index d15cef7..0000000
--- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Onboarding", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Onboarding
-		() => frappe.tests.make('Employee Onboarding', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
index 0445270..ea46aa2 100644
--- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
@@ -5,8 +5,9 @@
 
 import frappe
 import unittest
-from frappe.utils import nowdate
+from frappe.utils import getdate
 from erpnext.hr.doctype.employee_onboarding.employee_onboarding import make_employee
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
 from erpnext.hr.doctype.employee_onboarding.employee_onboarding import IncompleteTaskError
 from erpnext.hr.doctype.job_offer.test_job_offer import create_job_offer
 
@@ -46,7 +47,7 @@
 		onboarding.reload()
 		employee = make_employee(onboarding.name)
 		employee.first_name = employee.employee_name
-		employee.date_of_joining = nowdate()
+		employee.date_of_joining = getdate()
 		employee.date_of_birth = '1990-05-08'
 		employee.gender = 'Female'
 		employee.insert()
@@ -82,11 +83,14 @@
 def create_employee_onboarding():
 	applicant = get_job_applicant()
 	job_offer = get_job_offer(applicant.name)
+	holiday_list = make_holiday_list()
 
 	onboarding = frappe.new_doc('Employee Onboarding')
 	onboarding.job_applicant = applicant.name
 	onboarding.job_offer = job_offer.name
+	onboarding.date_of_joining = onboarding.boarding_begins_on = getdate()
 	onboarding.company = '_Test Company'
+	onboarding.holiday_list = holiday_list
 	onboarding.designation = 'Researcher'
 	onboarding.append('activities', {
 		'activity_name': 'Assign ID Card',
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.json b/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.json
deleted file mode 100644
index 4e91b72..0000000
--- a/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.json
+++ /dev/null
@@ -1,290 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-05-09 05:37:18.439763", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "activity_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Activity Name", 
-   "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_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "user", 
-   "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": "User", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "User", 
-   "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_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "role", 
-   "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": "Role", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Role", 
-   "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_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_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
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval: doc.parenttype == \"Employee Onboarding\"", 
-   "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, 
-   "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_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "required_for_employee_creation", 
-   "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": "Required for Employee Creation", 
-   "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_bulk_edit": 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
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "description", 
-   "fieldtype": "Text Editor", 
-   "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": "Description", 
-   "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
-  }
- ], 
- "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": "2018-05-09 06:15:41.768236", 
- "modified_by": "Administrator", 
- "module": "HR", 
- "name": "Employee Onboarding Activity", 
- "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
-}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.py b/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.py
deleted file mode 100644
index d170631..0000000
--- a/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, 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 EmployeeOnboardingActivity(Document):
-	pass
diff --git a/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.js b/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.js
deleted file mode 100644
index 10912ed..0000000
--- a/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Onboarding Template", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Onboarding Template
-		() => frappe.tests.make('Employee Onboarding Template', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.js b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.js
deleted file mode 100644
index 5f0a5ba..0000000
--- a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Promotion", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Promotion
-		() => frappe.tests.make('Employee Promotion', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.json b/erpnext/hr/doctype/employee_separation/employee_separation.json
index c10da5c..c240493 100644
--- a/erpnext/hr/doctype/employee_separation/employee_separation.json
+++ b/erpnext/hr/doctype/employee_separation/employee_separation.json
@@ -15,6 +15,7 @@
   "company",
   "boarding_status",
   "resignation_letter_date",
+  "boarding_begins_on",
   "project",
   "table_for_activity",
   "employee_separation_template",
@@ -144,11 +145,17 @@
    "options": "Employee Separation",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "boarding_begins_on",
+   "fieldtype": "Date",
+   "label": "Separation Begins On",
+   "reqd": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-06-03 18:02:54.007313",
+ "modified": "2021-07-30 14:03:51.218791",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee Separation",
diff --git a/erpnext/hr/doctype/employee_separation/test_employee_separation.js b/erpnext/hr/doctype/employee_separation/test_employee_separation.js
deleted file mode 100644
index d6c6359..0000000
--- a/erpnext/hr/doctype/employee_separation/test_employee_separation.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Separation", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Separation
-		() => frappe.tests.make('Employee Separation', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_separation/test_employee_separation.py b/erpnext/hr/doctype/employee_separation/test_employee_separation.py
index d63501a..2c11cbb 100644
--- a/erpnext/hr/doctype/employee_separation/test_employee_separation.py
+++ b/erpnext/hr/doctype/employee_separation/test_employee_separation.py
@@ -4,6 +4,7 @@
 from __future__ import unicode_literals
 
 import frappe
+from frappe.utils import getdate
 import unittest
 
 test_dependencies = ['Employee Onboarding']
@@ -34,9 +35,10 @@
 			doc.delete()
 
 def create_employee_separation():
-	employee = frappe.db.get_value('Employee', {'status': 'Active'})
+	employee = frappe.db.get_value('Employee', {'status': 'Active', 'company': '_Test Company'})
 	separation = frappe.new_doc('Employee Separation')
 	separation.employee = employee
+	separation.boarding_begins_on = getdate()
 	separation.company = '_Test Company'
 	separation.append('activities', {
 		'activity_name': 'Deactivate Employee',
diff --git a/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.js b/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.js
deleted file mode 100644
index 66fd450..0000000
--- a/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Separation Template", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Separation Template
-		() => frappe.tests.make('Employee Separation Template', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.js b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.js
deleted file mode 100644
index 05a3e1a..0000000
--- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Transfer", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Transfer
-		() => frappe.tests.make('Employee Transfer', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.js b/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.js
deleted file mode 100644
index 00a334a..0000000
--- a/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Transfer Property", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Transfer Property
-		() => frappe.tests.make('Employee Transfer Property', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/hr_settings/test_hr_settings.js b/erpnext/hr/doctype/hr_settings/test_hr_settings.js
deleted file mode 100644
index f32640b..0000000
--- a/erpnext/hr/doctype/hr_settings/test_hr_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: HR Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new HR Settings
-		() => frappe.tests.make('HR Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/identification_document_type/test_identification_document_type.js b/erpnext/hr/doctype/identification_document_type/test_identification_document_type.js
deleted file mode 100644
index 6587909..0000000
--- a/erpnext/hr/doctype/identification_document_type/test_identification_document_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Identification Document Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Identification Document Type
-		() => frappe.tests.make('Identification Document Type', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.js b/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.js
deleted file mode 100644
index c093928..0000000
--- a/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Job Applicant Source", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Job Applicant Source
-		() => frappe.tests.make('Job Applicant Source', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.js b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.js
deleted file mode 100644
index cafd960..0000000
--- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Leave Encashment", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Leave Encashment
-		() => frappe.tests.make('Leave Encashment', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.js b/erpnext/hr/doctype/leave_period/test_leave_period.js
deleted file mode 100644
index ec0a809..0000000
--- a/erpnext/hr/doctype/leave_period/test_leave_period.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Leave Period", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Leave Period
-		() => frappe.tests.make('Leave Period', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/leave_policy/test_leave_policy.js b/erpnext/hr/doctype/leave_policy/test_leave_policy.js
deleted file mode 100644
index 5404a63..0000000
--- a/erpnext/hr/doctype/leave_policy/test_leave_policy.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Leave Policy", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Leave Policy
-		() => frappe.tests.make('Leave Policy', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.js b/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.js
deleted file mode 100644
index 1c8995b..0000000
--- a/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Leave Policy Detail", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Leave Policy Detail
-		() => frappe.tests.make('Leave Policy Detail', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.js b/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.js
deleted file mode 100644
index 936c21c..0000000
--- a/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Purpose of Travel", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Purpose of Travel
-		() => frappe.tests.make('Purpose of Travel', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.js b/erpnext/hr/doctype/shift_assignment/test_shift_assignment.js
deleted file mode 100644
index 7727287..0000000
--- a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shift Assignment", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Shift Assignment
-		() => frappe.tests.make('Shift Assignment', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/shift_request/test_shift_request.js b/erpnext/hr/doctype/shift_request/test_shift_request.js
deleted file mode 100644
index 9c8cd70..0000000
--- a/erpnext/hr/doctype/shift_request/test_shift_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shift Request", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Shift Request
-		() => frappe.tests.make('Shift Request', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/shift_type/test_shift_type.js b/erpnext/hr/doctype/shift_type/test_shift_type.js
deleted file mode 100644
index 846f931..0000000
--- a/erpnext/hr/doctype/shift_type/test_shift_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shift Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Shift Type
-		() => frappe.tests.make('Shift Type', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.js b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.js
deleted file mode 100644
index 64320bc..0000000
--- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Staffing Plan", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Staffing Plan
-		() => frappe.tests.make('Staffing Plan', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/training_program/test_training_program.js b/erpnext/hr/doctype/training_program/test_training_program.js
deleted file mode 100644
index 3a62b2f..0000000
--- a/erpnext/hr/doctype/training_program/test_training_program.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Training Program", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Training Program
-		() => frappe.tests.make('Training Program', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/training_result/test_training_result.js b/erpnext/hr/doctype/training_result/test_training_result.js
deleted file mode 100644
index cb1d7fb..0000000
--- a/erpnext/hr/doctype/training_result/test_training_result.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Training Result", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Training Result
-		() => frappe.tests.make('Training Result', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/travel_request/test_travel_request.js b/erpnext/hr/doctype/travel_request/test_travel_request.js
deleted file mode 100644
index 7e64591..0000000
--- a/erpnext/hr/doctype/travel_request/test_travel_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Travel Request", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Travel Request
-		() => frappe.tests.make('Travel Request', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hr/doctype/vehicle/test_vehicle.js b/erpnext/hr/doctype/vehicle/test_vehicle.js
deleted file mode 100644
index 4d40cce..0000000
--- a/erpnext/hr/doctype/vehicle/test_vehicle.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Vehicle", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Vehicle
-		() => frappe.tests.make('Vehicle', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.js b/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.js
deleted file mode 100644
index fba3e09..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Marketplace Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Marketplace Settings
-		() => frappe.tests.make('Marketplace Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.js
deleted file mode 100644
index e0f05b1..0000000
--- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Maintenance Schedule", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Maintenance Schedule
-		() => frappe.tests.make('Maintenance Schedule', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
index 53ecdf5..6b3f184 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
@@ -73,12 +73,16 @@
 		if (this.frm.doc.docstatus === 0) {
 			this.frm.add_custom_button(__('Maintenance Schedule'),
 				function () {
+					if (!me.frm.doc.customer) {
+						frappe.msgprint(__('Please select Customer first'));
+						return;
+					}
 					erpnext.utils.map_current_doc({
 						method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.make_maintenance_visit",
 						source_doctype: "Maintenance Schedule",
 						target: me.frm,
 						setters: {
-							customer: me.frm.doc.customer || undefined,
+							customer: me.frm.doc.customer,
 						},
 						get_query_filters: {
 							docstatus: 1,
@@ -104,12 +108,16 @@
 				}, __("Get Items From"));
 			this.frm.add_custom_button(__('Sales Order'),
 				function () {
+					if (!me.frm.doc.customer) {
+						frappe.msgprint(__('Please select Customer first'));
+						return;
+					}
 					erpnext.utils.map_current_doc({
 						method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_visit",
 						source_doctype: "Sales Order",
 						target: me.frm,
 						setters: {
-							customer: me.frm.doc.customer || undefined,
+							customer: me.frm.doc.customer,
 						},
 						get_query_filters: {
 							docstatus: 1,
diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.js
deleted file mode 100644
index 51a0d94..0000000
--- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Blanket Order", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Blanket Order
-		() => frappe.tests.make('Blanket Order', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index e72c8eb..05123d5 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -446,6 +446,11 @@
 			},
 			callback: function(r) {
 				d = locals[cdt][cdn];
+				if (d.is_process_loss) {
+					r.message.rate = 0;
+					r.message.base_rate = 0;
+				}
+
 				$.extend(d, r.message);
 				refresh_field("items");
 				refresh_field("scrap_items");
@@ -655,3 +660,58 @@
 		frm.set_value("operations", []);
 	}
 });
+
+frappe.tour['BOM'] = [
+	{
+		fieldname: "item",
+		title: "Item",
+		description: __("Select the Item to be manufactured. The Item name, UoM, Company, and Currency will be fetched automatically.")
+	},
+	{
+		fieldname: "quantity",
+		title: "Quantity",
+		description: __("Enter the quantity of the Item that will be manufactured from this Bill of Materials.")
+	},
+	{
+		fieldname: "with_operations",
+		title: "With Operations",
+		description: __("To add Operations tick the 'With Operations' checkbox.")
+	},
+	{
+		fieldname: "items",
+		title: "Raw Materials",
+		description: __("Select the raw materials (Items) required to manufacture the Item")
+	}
+];
+
+frappe.ui.form.on("BOM Scrap Item", {
+	item_code(frm, cdt, cdn) {
+		const { item_code } = locals[cdt][cdn];
+		if (item_code === frm.doc.item) {
+			locals[cdt][cdn].is_process_loss = 1;
+			trigger_process_loss_qty_prompt(frm, cdt, cdn, item_code);
+		}
+	},
+});
+
+function trigger_process_loss_qty_prompt(frm, cdt, cdn, item_code) {
+	frappe.prompt(
+		{
+			fieldname: "percent",
+			fieldtype: "Percent",
+			label: __("% Finished Item Quantity"),
+			description:
+				__("Set quantity of process loss item:") +
+				` ${item_code} ` +
+				__("as a percentage of finished item quantity"),
+		},
+		(data) => {
+			const row = locals[cdt][cdn];
+			row.stock_qty = (frm.doc.quantity * data.percent) / 100;
+			row.qty = row.stock_qty / (row.conversion_factor || 1);
+			refresh_field("scrap_items");
+		},
+		__("Set Process Loss Item Quantity"),
+		__("Set Quantity")
+	);
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index eb1dfc8..6e1c7dd 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -156,6 +156,7 @@
 		self.update_stock_qty()
 		self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate = False, save=False)
 		self.set_bom_level()
+		self.validate_scrap_items()
 
 	def get_context(self, context):
 		context.parents = [{'name': 'boms', 'title': _('All BOMs') }]
@@ -230,7 +231,7 @@
 			}
 			ret = self.get_bom_material_detail(args)
 			for key, value in ret.items():
-				if not item.get(key):
+				if item.get(key) is None:
 					item.set(key, value)
 
 	@frappe.whitelist()
@@ -446,25 +447,29 @@
 				frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx))
 			check_list.append(m)
 
-	def check_recursion(self, bom_list=[]):
+	def check_recursion(self, bom_list=None):
 		""" Check whether recursion occurs in any bom"""
+		def _throw_error(bom_name):
+			frappe.throw(_("BOM recursion: {0} cannot be parent or child of {0}").format(bom_name))
+
 		bom_list = self.traverse_tree()
-		bom_nos = frappe.get_all('BOM Item', fields=["bom_no"],
-			filters={'parent': ('in', bom_list), 'parenttype': 'BOM'})
+		child_items = frappe.get_all('BOM Item', fields=["bom_no", "item_code"],
+			filters={'parent': ('in', bom_list), 'parenttype': 'BOM'}) or []
 
-		raise_exception = False
-		if bom_nos and self.name in [d.bom_no for d in bom_nos]:
-			raise_exception = True
+		child_bom = {d.bom_no for d in child_items}
+		child_items_codes = {d.item_code for d in child_items}
 
-		if not raise_exception:
-			bom_nos = frappe.get_all('BOM Item', fields=["parent"],
-				filters={'bom_no': self.name, 'parenttype': 'BOM'})
+		if self.name in child_bom:
+			_throw_error(self.name)
 
-			if self.name in [d.parent for d in bom_nos]:
-				raise_exception = True
+		if self.item in child_items_codes:
+			_throw_error(self.item)
 
-		if raise_exception:
-			frappe.throw(_("BOM recursion: {0} cannot be parent or child of {1}").format(self.name, self.name))
+		bom_nos = frappe.get_all('BOM Item', fields=["parent"],
+			filters={'bom_no': self.name, 'parenttype': 'BOM'}) or []
+
+		if self.name in {d.parent for d in bom_nos}:
+			_throw_error(self.name)
 
 	def traverse_tree(self, bom_list=None):
 		def _get_children(bom_no):
@@ -512,17 +517,21 @@
 	def update_rate_and_time(self, row, update_hour_rate = False):
 		if not row.hour_rate or update_hour_rate:
 			hour_rate = flt(frappe.get_cached_value("Workstation", row.workstation, "hour_rate"))
-			row.hour_rate = (hour_rate / flt(self.conversion_rate)
-				if self.conversion_rate and hour_rate else hour_rate)
+
+			if hour_rate:
+				row.hour_rate = (hour_rate / flt(self.conversion_rate)
+					if self.conversion_rate and hour_rate else hour_rate)
 
 			if self.routing:
-				row.time_in_mins = flt(frappe.db.get_value("BOM Operation", {
+				time_in_mins = flt(frappe.db.get_value("BOM Operation", {
 						"workstation": row.workstation,
 						"operation": row.operation,
-						"sequence_id": row.sequence_id,
 						"parent": self.routing
 				}, ["time_in_mins"]))
 
+				if time_in_mins:
+					row.time_in_mins = time_in_mins
+
 		if row.hour_rate and row.time_in_mins:
 			row.base_hour_rate = flt(row.hour_rate) * flt(self.conversion_rate)
 			row.operating_cost = flt(row.hour_rate) * flt(row.time_in_mins) / 60.0
@@ -705,6 +714,32 @@
 		if update:
 			self.db_set("bom_level", self.bom_level)
 
+	def validate_scrap_items(self):
+		for item in self.scrap_items:
+			msg = ""
+			if item.item_code == self.item and not item.is_process_loss:
+				msg = _('Scrap/Loss Item: {0} should have Is Process Loss checked as it is the same as the item to be manufactured or repacked.') \
+					.format(frappe.bold(item.item_code))
+			elif item.item_code != self.item and item.is_process_loss:
+				msg = _('Scrap/Loss Item: {0} should not have Is Process Loss checked as it is different from  the item to be manufactured or repacked') \
+					.format(frappe.bold(item.item_code))
+
+			must_be_whole_number = frappe.get_value("UOM", item.stock_uom, "must_be_whole_number")
+			if item.is_process_loss and must_be_whole_number:
+				msg = _("Item: {0} with Stock UOM: {1} cannot be a Scrap/Loss Item as {1} is a whole UOM.") \
+					.format(frappe.bold(item.item_code), frappe.bold(item.stock_uom))
+
+			if item.is_process_loss and (item.stock_qty >= self.quantity):
+				msg = _("Scrap/Loss Item: {0} should have Qty less than finished goods Quantity.") \
+					.format(frappe.bold(item.item_code))
+
+			if item.is_process_loss and (item.rate > 0):
+				msg = _("Scrap/Loss Item: {0} should have Rate set to 0 because Is Process Loss is checked.") \
+					.format(frappe.bold(item.item_code))
+
+			if msg:
+				frappe.throw(msg, title=_("Note"))
+
 def get_bom_item_rate(args, bom_doc):
 	if bom_doc.rm_cost_as_per == 'Valuation Rate':
 		rate = get_valuation_rate(args) * (args.get("conversion_factor") or 1)
@@ -822,8 +857,11 @@
 
 		items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True)
 	elif fetch_scrap_items:
-		query = query.format(table="BOM Scrap Item", where_conditions="",
-			select_columns=", bom_item.idx, item.description", is_stock_item=is_stock_item, qty_field="stock_qty")
+		query = query.format(
+			table="BOM Scrap Item", where_conditions="",
+			select_columns=", bom_item.idx, item.description, is_process_loss",
+			is_stock_item=is_stock_item, qty_field="stock_qty"
+		)
 
 		items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True)
 	else:
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index c89f7d6..8408f10 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -280,13 +280,82 @@
 			self.assertEqual(reqd_item.qty, created_item.qty)
 			self.assertEqual(reqd_item.exploded_qty, created_item.exploded_qty)
 
+	def test_bom_recursion_1st_level(self):
+		"""BOM should not allow BOM item again in child"""
+		item_code = "_Test BOM Recursion"
+		make_item(item_code, {'is_stock_item': 1})
+
+		bom = frappe.new_doc("BOM")
+		bom.item = item_code
+		bom.append("items", frappe._dict(item_code=item_code))
+		with self.assertRaises(frappe.ValidationError) as err:
+			bom.save()
+
+		self.assertTrue("recursion" in str(err.exception).lower())
+		frappe.delete_doc("BOM", bom.name, ignore_missing=True)
+
+	def test_bom_recursion_transitive(self):
+		item1 = "_Test BOM Recursion"
+		item2 = "_Test BOM Recursion 2"
+		make_item(item1, {'is_stock_item': 1})
+		make_item(item2, {'is_stock_item': 1})
+
+		bom1 = frappe.new_doc("BOM")
+		bom1.item = item1
+		bom1.append("items", frappe._dict(item_code=item2))
+		bom1.save()
+		bom1.submit()
+
+		bom2 = frappe.new_doc("BOM")
+		bom2.item = item2
+		bom2.append("items", frappe._dict(item_code=item1))
+
+		with self.assertRaises(frappe.ValidationError) as err:
+			bom2.save()
+			bom2.submit()
+
+		self.assertTrue("recursion" in str(err.exception).lower())
+
+		bom1.cancel()
+		frappe.delete_doc("BOM", bom1.name, ignore_missing=True, force=True)
+		frappe.delete_doc("BOM", bom2.name, ignore_missing=True, force=True)
+
+	def test_bom_with_process_loss_item(self):
+		fg_item_non_whole, fg_item_whole, bom_item = create_process_loss_bom_items()
+
+		if not frappe.db.exists("BOM", f"BOM-{fg_item_non_whole.item_code}-001"):
+			bom_doc = create_bom_with_process_loss_item(
+				fg_item_non_whole, bom_item, scrap_qty=0.25, scrap_rate=0, fg_qty=1
+			)
+			bom_doc.submit()
+
+		bom_doc = create_bom_with_process_loss_item(
+			fg_item_non_whole, bom_item, scrap_qty=2, scrap_rate=0
+		)
+		#  PL Item qty can't be >= FG Item qty
+		self.assertRaises(frappe.ValidationError, bom_doc.submit)
+
+		bom_doc = create_bom_with_process_loss_item(
+			fg_item_non_whole, bom_item, scrap_qty=1, scrap_rate=100
+		)
+		# PL Item rate has to be 0
+		self.assertRaises(frappe.ValidationError, bom_doc.submit)
+
+		bom_doc = create_bom_with_process_loss_item(
+			fg_item_whole, bom_item, scrap_qty=0.25, scrap_rate=0
+		)
+		#  Items with whole UOMs can't be PL Items
+		self.assertRaises(frappe.ValidationError, bom_doc.submit)
+
+		bom_doc = create_bom_with_process_loss_item(
+			fg_item_non_whole, bom_item, scrap_qty=0.25, scrap_rate=0, is_process_loss=0
+		)
+		# FG Items in Scrap/Loss Table should have Is Process Loss set
+		self.assertRaises(frappe.ValidationError, bom_doc.submit)
 
 def get_default_bom(item_code="_Test FG Item 2"):
 	return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
 
-
-
-
 def level_order_traversal(node):
 	traversal = []
 	q = deque()
@@ -332,6 +401,7 @@
 			bom = frappe.get_doc(doctype="BOM", item=bom_item_code)
 			for child_item in child_items.keys():
 				bom.append("items", {"item_code": prefix + child_item})
+			bom.currency = "INR"
 			bom.insert()
 			bom.submit()
 
@@ -353,3 +423,45 @@
 
 	for warehouse in warehouse_list:
 		create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=qty, rate=rate)
+
+def create_bom_with_process_loss_item(
+		fg_item, bom_item, scrap_qty, scrap_rate, fg_qty=2, is_process_loss=1):
+	bom_doc = frappe.new_doc("BOM")
+	bom_doc.item = fg_item.item_code
+	bom_doc.quantity = fg_qty
+	bom_doc.append("items", {
+		"item_code": bom_item.item_code,
+		"qty": 1,
+		"uom": bom_item.stock_uom,
+		"stock_uom": bom_item.stock_uom,
+		"rate": 100.0
+	})
+	bom_doc.append("scrap_items", {
+		"item_code": fg_item.item_code,
+		"qty": scrap_qty,
+		"stock_qty": scrap_qty,
+		"uom": fg_item.stock_uom,
+		"stock_uom": fg_item.stock_uom,
+		"rate": scrap_rate,
+		"is_process_loss": is_process_loss
+	})
+	bom_doc.currency = "INR"
+	return bom_doc
+
+def create_process_loss_bom_items():
+	item_list = [
+		("_Test Item - Non Whole UOM", "Kg"),
+		("_Test Item - Whole UOM", "Unit"),
+		("_Test PL BOM Item", "Unit")
+	]
+	return [create_process_loss_bom_item(it) for it in item_list]
+
+def create_process_loss_bom_item(item_tuple):
+	item_code, stock_uom = item_tuple
+	if frappe.db.exists("Item", item_code) is None:
+		return make_item(
+			item_code,
+			{'stock_uom':stock_uom, 'valuation_rate':100}
+		)
+	else:
+		return frappe.get_doc("Item", item_code)
diff --git a/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json b/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
index 9f7091d..7018082 100644
--- a/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
+++ b/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
@@ -1,345 +1,112 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-09-26 02:19:21.642081", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2016-09-26 02:19:21.642081",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item_code",
+  "column_break_2",
+  "item_name",
+  "is_process_loss",
+  "quantity_and_rate",
+  "stock_qty",
+  "rate",
+  "amount",
+  "column_break_6",
+  "stock_uom",
+  "base_rate",
+  "base_amount"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_code", 
-   "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": "Item Code", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Code",
+   "options": "Item",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Item Name", 
-   "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, 
-   "unique": 0
-  }, 
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Item Name"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "quantity_and_rate", 
-   "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": "Quantity and Rate", 
-   "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, 
-   "unique": 0
-  }, 
+   "fieldname": "quantity_and_rate",
+   "fieldtype": "Section Break",
+   "label": "Quantity and Rate"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "stock_qty", 
-   "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": "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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "stock_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Qty",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "rate", 
-   "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": "Rate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "currency", 
-   "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, 
-   "unique": 0
-  }, 
+   "fieldname": "rate",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Rate",
+   "options": "currency"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "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": "Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "currency", 
-   "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, 
-   "unique": 0
-  }, 
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "label": "Amount",
+   "options": "currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_6", 
-   "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, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_6",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "stock_uom", 
-   "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": "Stock UOM", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "UOM", 
-   "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, 
-   "unique": 0
-  }, 
+   "fieldname": "stock_uom",
+   "fieldtype": "Link",
+   "label": "Stock UOM",
+   "options": "UOM",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "base_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": "Basic Rate (Company Currency)", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company:company:default_currency", 
-   "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, 
-   "unique": 0
-  }, 
+   "fieldname": "base_rate",
+   "fieldtype": "Currency",
+   "label": "Basic Rate (Company Currency)",
+   "options": "Company:company:default_currency",
+   "print_hide": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "base_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": "Basic Amount (Company Currency)", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company:company:default_currency", 
-   "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, 
-   "unique": 0
+   "fieldname": "base_amount",
+   "fieldtype": "Currency",
+   "label": "Basic Amount (Company Currency)",
+   "options": "Company:company:default_currency",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_process_loss",
+   "fieldtype": "Check",
+   "label": "Is Process Loss"
   }
- ], 
- "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": "2017-07-04 16:04:32.442287", 
- "modified_by": "Administrator", 
- "module": "Manufacturing", 
- "name": "BOM Scrap Item", 
- "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
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2021-06-22 16:46:12.153311",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "BOM Scrap Item",
+ "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/manufacturing/doctype/bom_update_tool/test_bom_update_tool.js b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.js
deleted file mode 100644
index d220df2..0000000
--- a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: BOM Update Tool", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially('BOM Update Tool', [
-		// insert a new BOM Update Tool
-		() => frappe.tests.make([
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.js b/erpnext/manufacturing/doctype/job_card/test_job_card.js
deleted file mode 100644
index 5dc7805..0000000
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Job Card", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Job Card
-		() => frappe.tests.make('Job Card', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.js b/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.js
deleted file mode 100644
index 2b2589e..0000000
--- a/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Manufacturing Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially('Manufacturing Settings', [
-		// insert a new Manufacturing Settings
-		() => frappe.tests.make([
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.js b/erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.js
deleted file mode 100644
index 14c6e39..0000000
--- a/erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Material Request Plan Item", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Material Request Plan Item
-		() => frappe.tests.make('Material Request Plan Item', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/manufacturing/doctype/operation/operation.js b/erpnext/manufacturing/doctype/operation/operation.js
index 2936e33..95643bf 100644
--- a/erpnext/manufacturing/doctype/operation/operation.js
+++ b/erpnext/manufacturing/doctype/operation/operation.js
@@ -12,3 +12,21 @@
 		});
 	}
 });
+
+frappe.tour['Operation'] = [
+	{
+		fieldname: "__newname",
+		title: "Operation Name",
+		description: __("Enter a name for the Operation, for example, Cutting.")
+	},
+	{
+		fieldname: "workstation",
+		title: "Default Workstation",
+		description: __("Select the Default Workstation where the Operation will be performed. This will be fetched in BOMs and Work Orders.")
+	},
+	{
+		fieldname: "sub_operations",
+		title: "Sub Operations",
+		description: __("If an operation is divided into sub operations, they can be added here.")
+	}
+];
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index d198a69..847004f 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -428,3 +428,36 @@
 		]
 	}
 };
+
+frappe.tour['Production Plan'] = [
+	{
+		fieldname: "get_items_from",
+		title: "Get Items From",
+		description: __("Select whether to get items from a Sales Order or a Material Request. For now select <b>Sales Order</b>.\n A Production Plan can also be created manually where you can select the Items to manufacture.")
+	},
+	{
+		fieldname: "get_sales_orders",
+		title: "Get Sales Orders",
+		description: __("Click on Get Sales Orders to fetch sales orders based on the above filters.")
+	},
+	{
+		fieldname: "get_items",
+		title: "Get Finished Goods for Manufacture",
+		description: __("Click on 'Get Finished Goods for Manufacture' to fetch the items from the above Sales Orders. Items only for which a BOM is present will be fetched.")
+	},
+	{
+		fieldname: "po_items",
+		title: "Finished Goods",
+		description: __("On expanding a row in the Items to Manufacture table, you'll see an option to 'Include Exploded Items'. Ticking this includes raw materials of the sub-assembly items in the production process.")
+	},
+	{
+		fieldname: "include_non_stock_items",
+		title: "Include Non Stock Items",
+		description: __("To include non-stock items in the material request planning. i.e. Items for which 'Maintain Stock' checkbox is unticked.")
+	},
+	{
+		fieldname: "include_subcontracted_items",
+		title: "Include Subcontracted Items",
+		description: __("To add subcontracted Item's raw materials if include exploded items is disabled.")
+	}
+];
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index b4c6635..6b61c6d 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -213,7 +213,6 @@
 					})
 
 			pi = self.append('po_items', {
-				'include_exploded_items': 1,
 				'warehouse': data.warehouse,
 				'item_code': data.item_code,
 				'description': data.description or item_details.description,
@@ -224,6 +223,7 @@
 				'planned_start_date': now_datetime(),
 				'product_bundle_item': data.parent_item
 			})
+			pi._set_defaults()
 
 			if self.get_items_from == "Sales Order":
 				pi.sales_order = data.parent
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
index c2e3e6d..8f94686 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
@@ -1,4 +1,5 @@
 frappe.listview_settings['Production Plan'] = {
+	hide_name_column: true,
 	add_fields: ["status"],
 	filters: [["status", "!=", "Closed"]],
 	get_indicator: function (doc) {
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.js b/erpnext/manufacturing/doctype/production_plan/test_production_plan.js
deleted file mode 100644
index ef7d64c..0000000
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Production Plan", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Production Plan
-		() => frappe.tests.make('Production Plan', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index a5b9ff8..7802803 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -288,6 +288,7 @@
 		self.assertEqual(warehouses, expected_warehouses)
 
 	def test_get_sales_order_with_variant(self):
+		rm_item = create_item('PIV_RM', valuation_rate = 100)
 		if not frappe.db.exists('Item', {"item_code": 'PIV'}):
 			item = create_item('PIV', valuation_rate = 100)
 			variant_settings = {
@@ -300,20 +301,20 @@
 			}
 			item.update(variant_settings)
 			item.save()
-			parent_bom = make_bom(item = 'PIV', raw_materials = ['PIV'])
+			parent_bom = make_bom(item = 'PIV', raw_materials = [rm_item.item_code])
 		if not frappe.db.exists('BOM', {"item": 'PIV'}):
-			parent_bom = make_bom(item = 'PIV', raw_materials = ['PIV'])
+			parent_bom = make_bom(item = 'PIV', raw_materials = [rm_item.item_code])
 		else:
 			parent_bom = frappe.get_doc('BOM', {"item": 'PIV'})
 
 		if not frappe.db.exists('Item', {"item_code": 'PIV-RED'}):
 			variant = create_variant("PIV", {"Colour": "Red"})
 			variant.save()
-			variant_bom = make_bom(item = variant.item_code, raw_materials = [variant.item_code])
+			variant_bom = make_bom(item = variant.item_code, raw_materials = [rm_item.item_code])
 		else:
 			variant = frappe.get_doc('Item', 'PIV-RED')
 		if not frappe.db.exists('BOM', {"item": 'PIV-RED'}):
-			variant_bom = make_bom(item = variant.item_code, raw_materials = [variant.item_code])
+			variant_bom = make_bom(item = variant.item_code, raw_materials = [rm_item.item_code])
 
 		"""Testing when item variant has a BOM"""
 		so = make_sales_order(item_code="PIV-RED", qty=5)
diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js
index 032c9cd..c17a8e9 100644
--- a/erpnext/manufacturing/doctype/routing/routing.js
+++ b/erpnext/manufacturing/doctype/routing/routing.js
@@ -69,3 +69,17 @@
 		frm.events.calculate_operating_cost(frm, d);
 	}
 });
+
+frappe.tour['Routing'] = [
+	{
+		fieldname: "routing_name",
+		title: "Routing Name",
+		description: __("Enter a name for Routing.")
+	},
+	{
+		fieldname: "operations",
+		title: "BOM Operations",
+		description: __("Enter the Operation, the table will fetch the Operation details like Hourly Rate, Workstation automatically.\n\n After that, set the Operation Time in minutes and the table will calculate the Operation Costs based on the Hourly Rate and Operation Time.")
+	}
+];
+
diff --git a/erpnext/manufacturing/doctype/routing/test_routing.js b/erpnext/manufacturing/doctype/routing/test_routing.js
deleted file mode 100644
index 6cb6549..0000000
--- a/erpnext/manufacturing/doctype/routing/test_routing.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Routing", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Routing
-		() => frappe.tests.make('Routing', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index bf1ccb7..c0ed611 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -675,13 +675,18 @@
 
 	def test_valuation_rate_missing_on_make_stock_entry(self):
 		item_name = 'Test Valuation Rate Missing'
+		rm_item = '_Test raw material item'
 		make_item(item_name, {
 			"is_stock_item": 1,
 			"include_item_in_manufacturing": 1,
 		})
+		make_item('_Test raw material item', {
+			"is_stock_item": 1,
+			"include_item_in_manufacturing": 1,
+		})
 
 		if not frappe.db.get_value('BOM', {'item': item_name}):
-			make_bom(item=item_name, raw_materials=[item_name], rm_qty=1)
+			make_bom(item=item_name, raw_materials=[rm_item], rm_qty=1)
 
 		company = "_Test Company with perpetual inventory"
 		source_warehouse = create_warehouse("Test Valuation Rate Missing Warehouse", company=company)
@@ -690,6 +695,71 @@
 
 		self.assertRaises(frappe.ValidationError, make_stock_entry, wo.name, 'Material Transfer for Manufacture')
 
+	def test_wo_completion_with_pl_bom(self):
+		from erpnext.manufacturing.doctype.bom.test_bom import create_process_loss_bom_items
+		from erpnext.manufacturing.doctype.bom.test_bom import create_bom_with_process_loss_item
+
+		qty = 4
+		scrap_qty = 0.25 # bom item qty = 1, consider as 25% of FG
+		source_warehouse = "Stores - _TC"
+		wip_warehouse = "_Test Warehouse - _TC"
+		fg_item_non_whole, _, bom_item = create_process_loss_bom_items()
+
+		test_stock_entry.make_stock_entry(item_code=bom_item.item_code,
+			target=source_warehouse, qty=4, basic_rate=100)
+
+		bom_no = f"BOM-{fg_item_non_whole.item_code}-001"
+		if not frappe.db.exists("BOM", bom_no):
+			bom_doc = create_bom_with_process_loss_item(
+				fg_item_non_whole, bom_item, scrap_qty=scrap_qty,
+				scrap_rate=0, fg_qty=1, is_process_loss=1
+			)
+			bom_doc.submit()
+
+		wo = make_wo_order_test_record(
+			production_item=fg_item_non_whole.item_code,
+			bom_no=bom_no,
+			wip_warehouse=wip_warehouse,
+			qty=qty,
+			skip_transfer=1,
+			stock_uom=fg_item_non_whole.stock_uom,
+		)
+
+		se = frappe.get_doc(
+			make_stock_entry(wo.name, "Material Transfer for Manufacture", qty)
+		)
+		se.get("items")[0].s_warehouse = "Stores - _TC"
+		se.insert()
+		se.submit()
+
+		se = frappe.get_doc(
+			make_stock_entry(wo.name, "Manufacture", qty)
+		)
+		se.insert()
+		se.submit()
+
+		# Testing stock entry values
+		items = se.get("items")
+		self.assertEqual(len(items), 3, "There should be 3 items including process loss.")
+
+		source_item, fg_item, pl_item = items
+
+		total_pl_qty = qty * scrap_qty
+		actual_fg_qty = qty - total_pl_qty
+
+		self.assertEqual(pl_item.qty, total_pl_qty)
+		self.assertEqual(fg_item.qty, actual_fg_qty)
+
+		# Testing Work Order values
+		self.assertEqual(
+			frappe.db.get_value("Work Order", wo.name, "produced_qty"),
+			qty
+		)
+		self.assertEqual(
+			frappe.db.get_value("Work Order", wo.name, "process_loss_qty"),
+			total_pl_qty
+		)
+
 def get_scrap_item_details(bom_no):
 	scrap_items = {}
 	for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item`
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 5120485..51c46f6 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -731,3 +731,63 @@
 		});
 	}
 };
+
+frappe.tour['Work Order'] = [
+	{
+		fieldname: "production_item",
+		title: "Item to Manufacture",
+		description: __("Select the Item to be manufactured.")
+	},
+	{
+		fieldname: "bom_no",
+		title: "BOM No",
+		description: __("The default BOM for that item will be fetched by the system. You can also change the BOM.")
+	},
+	{
+		fieldname: "qty",
+		title: "Qty to Manufacture",
+		description: __("Enter the quantity to manufacture. Raw material Items will be fetched only when this is set.")
+	},
+	{
+		fieldname: "use_multi_level_bom",
+		title: "Use Multi-Level BOM",
+		description: __("This is enabled by default. If you want to plan materials for sub-assemblies of the Item you're manufacturing leave this enabled. If you plan and manufacture the sub-assemblies separately, you can disable this checkbox.")
+	},
+	{
+		fieldname: "source_warehouse",
+		title: "Source Warehouse",
+		description: __("The warehouse where you store your raw materials. Each required item can have a separate source warehouse. Group warehouse also can be selected as source warehouse. On submission of the Work Order, the raw materials will be reserved in these warehouses for production usage.")
+	},
+	{
+		fieldname: "fg_warehouse",
+		title: "Target Warehouse",
+		description: __("The warehouse where you store finished Items before they are shipped.")
+	},
+	{
+		fieldname: "wip_warehouse",
+		title: "Work-in-Progress Warehouse",
+		description: __("The warehouse where your Items will be transferred when you begin production. Group Warehouse can also be selected as a Work in Progress warehouse.")
+	},
+	{
+		fieldname: "scrap_warehouse",
+		title: "Scrap Warehouse",
+		description: __("If the BOM results in Scrap material, the Scrap Warehouse needs to be selected.")
+	},
+	{
+		fieldname: "required_items",
+		title: "Required Items",
+		description: __("All the required items (raw materials) will be fetched from BOM and populated in this table. Here you can also change the Source Warehouse for any item. And during the production, you can track transferred raw materials from this table.")
+	},
+	{
+		fieldname: "planned_start_date",
+		title: "Planned Start Date",
+		description: __("Set the Planned Start Date (an Estimated Date at which you want the Production to begin)")
+	},
+	{
+		fieldname: "operations",
+		title: "Operations",
+		description: __("If the selected BOM has Operations mentioned in it, the system will fetch all Operations from BOM, these values can be changed.")
+	},
+
+
+];
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index 3b56854..913fc85 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -19,6 +19,7 @@
   "qty",
   "material_transferred_for_manufacturing",
   "produced_qty",
+  "process_loss_qty",
   "sales_order",
   "project",
   "serial_no_and_batch_for_finished_good_section",
@@ -64,16 +65,12 @@
   "description",
   "stock_uom",
   "column_break2",
-  "references_section",
   "material_request",
   "material_request_item",
   "sales_order_item",
-  "column_break_61",
   "production_plan",
   "production_plan_item",
   "production_plan_sub_assembly_item",
-  "parent_work_order",
-  "bom_level",
   "product_bundle_item",
   "amended_from"
  ],
@@ -553,20 +550,29 @@
    "read_only": 1
   },
   {
-    "fieldname": "production_plan_sub_assembly_item",
-    "fieldtype": "Data",
-    "label": "Production Plan Sub-assembly Item",
-    "no_copy": 1,
-    "print_hide": 1,
-    "read_only": 1
-   }
+   "fieldname": "production_plan_sub_assembly_item",
+   "fieldtype": "Data",
+   "label": "Production Plan Sub-assembly Item",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.process_loss_qty",
+   "fieldname": "process_loss_qty",
+   "fieldtype": "Float",
+   "label": "Process Loss Qty",
+   "no_copy": 1,
+   "non_negative": 1,
+   "read_only": 1
+  }
  ],
  "icon": "fa fa-cogs",
  "idx": 1,
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2021-06-28 16:19:14.902699",
+ "modified": "2021-08-24 15:14:03.844937",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order",
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 5fe9fec..24b33d5 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -214,6 +214,7 @@
 					self.meta.get_label(fieldname), qty, completed_qty, self.name), StockOverProductionError)
 
 			self.db_set(fieldname, qty)
+			self.set_process_loss_qty()
 
 			from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
 
@@ -223,6 +224,22 @@
 		if self.production_plan:
 			self.update_production_plan_status()
 
+	def set_process_loss_qty(self):
+		process_loss_qty = flt(frappe.db.sql("""
+				SELECT sum(qty) FROM `tabStock Entry Detail`
+				WHERE
+					is_process_loss=1
+					AND parent IN (
+						SELECT name FROM `tabStock Entry`
+						WHERE
+							work_order=%s
+							AND purpose='Manufacture'
+							AND docstatus=1
+					)
+			""", (self.name, ))[0][0])
+		if process_loss_qty is not None:
+			self.db_set('process_loss_qty', process_loss_qty)
+
 	def update_production_plan_status(self):
 		production_plan = frappe.get_doc('Production Plan', self.production_plan)
 		produced_qty = 0
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.js b/erpnext/manufacturing/doctype/workstation/workstation.js
index d8d25fc..4f5231e 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.js
+++ b/erpnext/manufacturing/doctype/workstation/workstation.js
@@ -16,4 +16,29 @@
 			})
 		}
 	}
-})
+});
+
+frappe.tour['Workstation'] = [
+	{
+		fieldname: "workstation_name",
+		title: "Workstation Name",
+		description: __("You can set it as a machine name or operation type. For example, stiching machine 12")
+	},
+	{
+		fieldname: "production_capacity",
+		title: "Production Capacity",
+		description: __("No. of parallel job cards which can be allowed on this workstation. Example: 2 would mean this workstation can process production for two Work Orders at a time.")
+	},
+	{
+		fieldname: "holiday_list",
+		title: "Holiday List",
+		description: __("A Holiday List can be added to exclude counting these days for the Workstation.")
+	},
+	{
+		fieldname: "working_hours",
+		title: "Working Hours",
+		description: __("Under Working Hours table, you can add start and end times for a Workstation. For example, a Workstation may be active from 9 am to 1 pm, then 2 pm to 5 pm. You can also specify the working hours based on shifts. While scheduling a Work Order, the system will check for the availability of the Workstation based on the working hours specified.")
+	},
+
+
+];
\ No newline at end of file
diff --git a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json
index 7317152..032091f 100644
--- a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json
+++ b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json
@@ -19,14 +19,14 @@
  "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing",
  "idx": 0,
  "is_complete": 0,
- "modified": "2020-06-29 20:25:36.899106",
+ "modified": "2021-08-13 16:04:34.333812",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Manufacturing",
  "owner": "Administrator",
  "steps": [
   {
-   "step": "Warehouse"
+   "step": "Explore Manufacturing Settings"
   },
   {
    "step": "Workstation"
@@ -35,22 +35,22 @@
    "step": "Operation"
   },
   {
-   "step": "Create Product"
+   "step": "Routing"
   },
   {
-   "step": "Create Raw Materials"
+   "step": "Create Product"
   },
   {
    "step": "Create BOM"
   },
   {
-   "step": "Work Order"
+   "step": "Production Planning"
   },
   {
-   "step": "Explore Manufacturing Settings"
+   "step": "Work Order"
   }
  ],
  "subtitle": "Products, Raw Materials, BOM, Work Order, and more.",
  "success_message": "Manufacturing module is all set up!",
  "title": "Let's Set Up the Manufacturing Module."
-}
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json
index 84b4088..9d7a58c 100644
--- a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json
+++ b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json
@@ -1,19 +1,21 @@
 {
  "action": "Create Entry",
+ "action_label": "Create your first Bill of Materials",
  "creation": "2020-05-05 16:41:20.239696",
+ "description": "# Create a Bill of Materials\n\nA Bill of Materials (BOM) is a list of items and sub-assemblies with quantities required to manufacture an Item.\n\nBOM also provides cost estimation for the production of the item. It takes raw-materials cost based on valuation and operations to cost based on routing, which gives total costing for a BOM.",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 1,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-19 12:51:31.315686",
+ "modified": "2021-08-13 16:00:51.092671",
  "modified_by": "Administrator",
  "name": "Create BOM",
  "owner": "Administrator",
  "reference_document": "BOM",
+ "show_form_tour": 1,
  "show_full_form": 1,
- "title": "Create a BOM (Bill of Material)",
+ "title": "Bill of Materials",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/create_product/create_product.json b/erpnext/manufacturing/onboarding_step/create_product/create_product.json
index 0ffa301..ad73452 100644
--- a/erpnext/manufacturing/onboarding_step/create_product/create_product.json
+++ b/erpnext/manufacturing/onboarding_step/create_product/create_product.json
@@ -1,19 +1,21 @@
 {
  "action": "Create Entry",
+ "action_label": "Create an Item",
  "creation": "2020-05-05 16:42:31.476275",
+ "description": "# Create Items for Bill of Materials\n\nOne of the prerequisites of a BOM is the creation of raw materials, sub-assembly, and finished items. Once these items are created, you will be able to proceed to the Bill of Materials master, which is composed of items and routing.\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 1,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-19 12:50:59.010439",
+ "modified": "2021-08-13 16:00:22.407811",
  "modified_by": "Administrator",
  "name": "Create Product",
  "owner": "Administrator",
  "reference_document": "Item",
- "show_full_form": 0,
- "title": "Create a Finished Good",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Finished Items",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json b/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json
index 0764f2e..3f94764 100644
--- a/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json
+++ b/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json
@@ -5,7 +5,6 @@
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
  "modified": "2020-05-19 11:53:25.147837",
@@ -13,6 +12,7 @@
  "name": "Create Raw Materials",
  "owner": "Administrator",
  "reference_document": "Item",
+ "show_form_tour": 0,
  "show_full_form": 0,
  "title": "Create Raw Materials",
  "validate_action": 1
diff --git a/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json
index 7ef202e..1d2c27e 100644
--- a/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json
+++ b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json
@@ -1,20 +1,22 @@
 {
  "action": "Show Form Tour",
+ "action_label": "Take a walk-through of Manufacturing Settings",
  "creation": "2020-05-19 11:55:11.378374",
+ "description": "# Review Manufacturing Settings\n\nIn ERPNext, the Manufacturing module\u2019s features are configurable as per your business needs. Manufacturing Settings is the place where you can set your preferences for:\n\n- Capacity planning for allocating jobs to workstations\n- Raw-material consumption based on BOM or actual\n- Default values and over-production allowance\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 1,
  "is_skipped": 0,
- "modified": "2020-05-26 20:28:03.558199",
+ "modified": "2021-08-13 15:59:32.145655",
  "modified_by": "Administrator",
  "name": "Explore Manufacturing Settings",
  "owner": "Administrator",
  "reference_document": "Manufacturing Settings",
+ "show_form_tour": 0,
  "show_full_form": 0,
- "title": "Explore Manufacturing Settings",
+ "title": "Manufacturing Settings",
  "validate_action": 0,
  "video_url": "https://www.youtube.com/watch?v=UVGfzwOOZC4"
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/operation/operation.json b/erpnext/manufacturing/onboarding_step/operation/operation.json
index b532e67..2e95921 100644
--- a/erpnext/manufacturing/onboarding_step/operation/operation.json
+++ b/erpnext/manufacturing/onboarding_step/operation/operation.json
@@ -1,19 +1,21 @@
 {
  "action": "Create Entry",
+ "action_label": "Let\u2019s create an Operation",
  "creation": "2020-05-12 16:15:31.706756",
+ "description": "# Create Operations\n\nAn Operation refers to any manufacturing operation performed on the raw materials to process it further in the manufacturing path. As an example, if you are into garments manufacturing, you will create Operations like fabric cutting, stitching, and washing as some of the operations.",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-19 12:50:41.642754",
+ "modified": "2021-08-13 15:59:53.313532",
  "modified_by": "Administrator",
  "name": "Operation",
  "owner": "Administrator",
  "reference_document": "Operation",
- "show_full_form": 0,
- "title": "Create a Operation",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Operation",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/production_planning/production_planning.json b/erpnext/manufacturing/onboarding_step/production_planning/production_planning.json
new file mode 100644
index 0000000..ff9fdb9
--- /dev/null
+++ b/erpnext/manufacturing/onboarding_step/production_planning/production_planning.json
@@ -0,0 +1,21 @@
+{
+ "action": "Create Entry",
+ "action_label": "Learn more about Production Planning",
+ "creation": "2021-08-04 17:33:06.551077",
+ "description": "# How Production Planning Works\n\nProduction Plan helps in production and material planning for the Items planned for manufacturing. These production items can be committed via Sales Order (to Customers) or Material Requests (internally).\n",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 16:04:15.491414",
+ "modified_by": "Administrator",
+ "name": "Production Planning",
+ "owner": "Administrator",
+ "reference_document": "Production Plan",
+ "show_form_tour": 0,
+ "show_full_form": 1,
+ "title": "Production Planning",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/routing/routing.json b/erpnext/manufacturing/onboarding_step/routing/routing.json
new file mode 100644
index 0000000..be4fbff
--- /dev/null
+++ b/erpnext/manufacturing/onboarding_step/routing/routing.json
@@ -0,0 +1,21 @@
+{
+ "action": "Create Entry",
+ "action_label": "Check help to setup Routing",
+ "creation": "2021-08-04 11:56:42.455758",
+ "description": "# Setup Routing\n\nA Routing stores all Operations along with the description, hourly rate, operation time, batch size, etc. Click below to learn how the Routing template can be created, for quick selection in the BOM.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 16:00:07.391563",
+ "modified_by": "Administrator",
+ "name": "Routing",
+ "owner": "Administrator",
+ "reference_document": "Routing",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Routing",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/work_order/work_order.json b/erpnext/manufacturing/onboarding_step/work_order/work_order.json
index c63363e..d2756c9 100644
--- a/erpnext/manufacturing/onboarding_step/work_order/work_order.json
+++ b/erpnext/manufacturing/onboarding_step/work_order/work_order.json
@@ -1,19 +1,21 @@
 {
  "action": "Create Entry",
+ "action_label": "Create your first Work Order",
  "creation": "2020-05-12 16:15:56.084682",
+ "description": "# Create a Work Order\n\nA Work Order or a Job order is given to the manufacturing shop floor by the Production Manager to initiate the manufacturing of a certain quantity of an item. Work Order carriers details of production Item, its BOM, quantities to be manufactured, and operations.\n\nThrough Work Order, you can track various production status like:\n\n- Issue of raw-material to shop material\n- Progress on each Workstation via Job Card\n- Manufactured Quantity against Work Order\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-19 12:51:38.133150",
+ "modified": "2021-08-13 16:02:04.223536",
  "modified_by": "Administrator",
  "name": "Work Order",
  "owner": "Administrator",
  "reference_document": "Work Order",
+ "show_form_tour": 1,
  "show_full_form": 1,
- "title": "Create a Work Order",
+ "title": "Work Order",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/workstation/workstation.json b/erpnext/manufacturing/onboarding_step/workstation/workstation.json
index df244bb..67dd52e 100644
--- a/erpnext/manufacturing/onboarding_step/workstation/workstation.json
+++ b/erpnext/manufacturing/onboarding_step/workstation/workstation.json
@@ -1,19 +1,21 @@
 {
  "action": "Create Entry",
+ "action_label": "Let\u2019s create a Workstation",
  "creation": "2020-05-12 16:14:14.930214",
+ "description": "# Create Workstations\n\nA Workstation stores information regarding the place where the workstation operations are performed. As an example, if you have ten sewing machines doing stitching jobs, each machine will be added as a workstation.",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-19 12:50:33.938176",
+ "modified": "2021-08-13 15:59:59.634802",
  "modified_by": "Administrator",
  "name": "Workstation",
  "owner": "Administrator",
  "reference_document": "Workstation",
- "show_full_form": 0,
- "title": "Create a Workstation / Machine",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Workstation",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/certification_application/test_certification_application.js b/erpnext/non_profit/doctype/certification_application/test_certification_application.js
deleted file mode 100644
index 40e9486..0000000
--- a/erpnext/non_profit/doctype/certification_application/test_certification_application.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Certification Application", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Certification Application
-		() => frappe.tests.make('Certification Application', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.js b/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.js
deleted file mode 100644
index f6a72a4..0000000
--- a/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Certified Consultant", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Certified Consultant
-		() => frappe.tests.make('Certified Consultant', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/non_profit/doctype/chapter/test_chapter.js b/erpnext/non_profit/doctype/chapter/test_chapter.js
deleted file mode 100644
index e30d6a5..0000000
--- a/erpnext/non_profit/doctype/chapter/test_chapter.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Chapter", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Chapter
-		() => frappe.tests.make('Chapter', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/non_profit/doctype/donor_type/test_donor_type.js b/erpnext/non_profit/doctype/donor_type/test_donor_type.js
deleted file mode 100644
index 22dc18e..0000000
--- a/erpnext/non_profit/doctype/donor_type/test_donor_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Donor Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Member
-		() => frappe.tests.make('Donor Type', [
-			// values to be set
-			{donor_type: 'Test Organization'},
-		]),
-		() => {
-			assert.equal(cur_frm.doc.donor_type, 'Test Organization');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/non_profit/doctype/membership/test_membership.js b/erpnext/non_profit/doctype/membership/test_membership.js
deleted file mode 100644
index 24c85c6..0000000
--- a/erpnext/non_profit/doctype/membership/test_membership.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Membership", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Membership
-		() => frappe.tests.make('Membership', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 0a6a8bd..0f6a606 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -295,6 +295,8 @@
 erpnext.patches.v13_0.add_custom_field_for_south_africa #2
 erpnext.patches.v13_0.update_recipient_email_digest
 erpnext.patches.v13_0.shopify_deprecation_warning
+erpnext.patches.v13_0.migrate_stripe_api
 erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries
 erpnext.patches.v13_0.einvoicing_deprecation_warning
 erpnext.patches.v14_0.delete_einvoicing_doctypes
+erpnext.patches.v13_0.set_operation_time_based_on_operating_cost
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/migrate_stripe_api.py b/erpnext/patches/v13_0/migrate_stripe_api.py
new file mode 100644
index 0000000..355421a
--- /dev/null
+++ b/erpnext/patches/v13_0/migrate_stripe_api.py
@@ -0,0 +1,7 @@
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+
+def execute():
+	frappe.reload_doc("accounts", "doctype", "subscription_plan")
+	rename_field("Subscription Plan", "payment_plan_id", "product_price_id")
diff --git a/erpnext/patches/v13_0/set_operation_time_based_on_operating_cost.py b/erpnext/patches/v13_0/set_operation_time_based_on_operating_cost.py
new file mode 100644
index 0000000..4acbdd6
--- /dev/null
+++ b/erpnext/patches/v13_0/set_operation_time_based_on_operating_cost.py
@@ -0,0 +1,15 @@
+import frappe
+
+def execute():
+	frappe.reload_doc('manufacturing', 'doctype', 'bom')
+	frappe.reload_doc('manufacturing', 'doctype', 'bom_operation')
+
+	frappe.db.sql('''
+		UPDATE
+			`tabBOM Operation`
+		SET
+			time_in_mins = (operating_cost * 60) / hour_rate
+		WHERE
+			time_in_mins = 0 AND operating_cost > 0
+			AND hour_rate > 0 AND docstatus = 1 AND parenttype = "BOM"
+	''')
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/additional_salary/test_additional_salary.js b/erpnext/payroll/doctype/additional_salary/test_additional_salary.js
deleted file mode 100644
index c18e187..0000000
--- a/erpnext/payroll/doctype/additional_salary/test_additional_salary.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Additional Salary", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Additional Salary
-		() => frappe.tests.make('Additional Salary', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js
deleted file mode 100644
index b355e1c..0000000
--- a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Benefit Application", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Benefit Application
-		() => frappe.tests.make('Employee Benefit Application', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js
deleted file mode 100644
index 3c808c0..0000000
--- a/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Benefit Claim", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Benefit Claim
-		() => frappe.tests.make('Employee Benefit Claim', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js
deleted file mode 100644
index 10bc037..0000000
--- a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Incentive", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Incentive
-		() => frappe.tests.make('Employee Incentive', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js
deleted file mode 100644
index e0e43c3..0000000
--- a/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Tax Exemption Category", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Tax Exemption Category
-		() => frappe.tests.make('Employee Tax Exemption Category', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js
deleted file mode 100644
index 274a3a3..0000000
--- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Tax Exemption Declaration", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Tax Exemption Declaration
-		() => frappe.tests.make('Employee Tax Exemption Declaration', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js
deleted file mode 100644
index cec7508..0000000
--- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Tax Exemption Proof Submission", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Tax Exemption Proof Submission
-		() => frappe.tests.make('Employee Tax Exemption Proof Submission', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js
deleted file mode 100644
index 8a1a6d1..0000000
--- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Tax Exemption Sub Category", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Employee Tax Exemption Sub Category
-		() => frappe.tests.make('Employee Tax Exemption Sub Category', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/payroll_period/test_payroll_period.js b/erpnext/payroll/doctype/payroll_period/test_payroll_period.js
deleted file mode 100644
index 8c4ded9..0000000
--- a/erpnext/payroll/doctype/payroll_period/test_payroll_period.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Payroll Period", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Payroll Period
-		() => frappe.tests.make('Payroll Period', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js
deleted file mode 100644
index a4b95d3..0000000
--- a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Retention Bonus", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Retention Bonus
-		() => frappe.tests.make('Retention Bonus', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/salary_component/test_salary_component.js b/erpnext/payroll/doctype/salary_component/test_salary_component.js
deleted file mode 100644
index c47d32d..0000000
--- a/erpnext/payroll/doctype/salary_component/test_salary_component.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Salary Component", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Salary Component
-		() => frappe.tests.make('Salary Component', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index d730fcf..636ec0b 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -828,7 +828,8 @@
 
 def make_holiday_list():
 	fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())
-	if not frappe.db.get_value("Holiday List", "Salary Slip Test Holiday List"):
+	holiday_list = frappe.db.exists("Holiday List", "Salary Slip Test Holiday List")
+	if not holiday_list:
 		holiday_list = frappe.get_doc({
 			"doctype": "Holiday List",
 			"holiday_list_name": "Salary Slip Test Holiday List",
@@ -838,3 +839,6 @@
 		}).insert()
 		holiday_list.get_weekly_off_dates()
 		holiday_list.save()
+		holiday_list = holiday_list.name
+
+	return holiday_list
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js
deleted file mode 100644
index 2f52576..0000000
--- a/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Salary Structure Assignment", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Salary Structure Assignment
-		() => frappe.tests.make('Salary Structure Assignment', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/portal/doctype/products_settings/test_products_settings.js b/erpnext/portal/doctype/products_settings/test_products_settings.js
deleted file mode 100644
index b7049b3..0000000
--- a/erpnext/portal/doctype/products_settings/test_products_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Products Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Products Settings
-		() => frappe.tests.make('Products Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/projects/doctype/project/test_project.js b/erpnext/projects/doctype/project/test_project.js
deleted file mode 100644
index 16494f6..0000000
--- a/erpnext/projects/doctype/project/test_project.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Project", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Project
-		() => frappe.tests.make('Project', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/projects/doctype/project_type/test_project_type.js b/erpnext/projects/doctype/project_type/test_project_type.js
deleted file mode 100644
index c2198c4..0000000
--- a/erpnext/projects/doctype/project_type/test_project_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Project Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially('Project Type', [
-		// insert a new Project Type
-		() => frappe.tests.make([
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/projects/doctype/project_update/test_project_update.js b/erpnext/projects/doctype/project_update/test_project_update.js
deleted file mode 100644
index bda510b..0000000
--- a/erpnext/projects/doctype/project_update/test_project_update.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Project Update", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Project Update
-		() => frappe.tests.make('Project Update', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/projects/doctype/projects_settings/test_projects_settings.js b/erpnext/projects/doctype/projects_settings/test_projects_settings.js
deleted file mode 100644
index f6feaa4..0000000
--- a/erpnext/projects/doctype/projects_settings/test_projects_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Projects Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Projects Settings
-		() => frappe.tests.make('Projects Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.js b/erpnext/projects/doctype/timesheet/test_timesheet.js
deleted file mode 100644
index c081d6f..0000000
--- a/erpnext/projects/doctype/timesheet/test_timesheet.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Timesheet", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Timesheet
-		() => frappe.tests.make('Timesheet', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.js b/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.js
deleted file mode 100644
index 24c5fd3..0000000
--- a/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: GST HSN Code", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new GST HSN Code
-		() => frappe.tests.make('GST HSN Code', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/regional/doctype/gst_settings/test_gst_settings.js b/erpnext/regional/doctype/gst_settings/test_gst_settings.js
deleted file mode 100644
index 00fcca6..0000000
--- a/erpnext/regional/doctype/gst_settings/test_gst_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: GST Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new GST Settings
-		() => frappe.tests.make('GST Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
deleted file mode 100644
index 765b51f..0000000
--- a/erpnext/regional/india/e_invoice/utils.py
+++ /dev/null
@@ -1,1131 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import os
-import re
-import jwt
-import sys
-import json
-import base64
-import frappe
-import six
-import traceback
-import io
-from frappe import _, bold
-from pyqrcode import create as qrcreate
-from frappe.utils.background_jobs import enqueue
-from frappe.utils.scheduler import is_scheduler_inactive
-from frappe.core.page.background_jobs.background_jobs import get_info
-from frappe.integrations.utils import make_post_request, make_get_request
-from erpnext.regional.india.utils import get_gst_accounts, get_place_of_supply
-from frappe.utils.data import cstr, cint, format_date, flt, time_diff_in_seconds, now_datetime, add_to_date, get_link_to_form, getdate, time_diff_in_hours
-
-@frappe.whitelist()
-def validate_eligibility(doc):
-	if isinstance(doc, six.string_types):
-		doc = json.loads(doc)
-
-	invalid_doctype = doc.get('doctype') != 'Sales Invoice'
-	if invalid_doctype:
-		return False
-
-	einvoicing_enabled = cint(frappe.db.get_single_value('E Invoice Settings', 'enable'))
-	if not einvoicing_enabled:
-		return False
-
-	einvoicing_eligible_from = frappe.db.get_single_value('E Invoice Settings', 'applicable_from') or '2021-04-01'
-	if getdate(doc.get('posting_date')) < getdate(einvoicing_eligible_from):
-		return False
-
-	invalid_company = not frappe.db.get_value('E Invoice User', { 'company': doc.get('company') })
-	invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
-	company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
-
-	# if export invoice, then taxes can be empty
-	# invoice can only be ineligible if no taxes applied and is not an export invoice
-	no_taxes_applied = not doc.get('taxes') and not doc.get('gst_category') == 'Overseas'
-	has_non_gst_item = any(d for d in doc.get('items', []) if d.get('is_non_gst'))
-
-	if invalid_company or invalid_supply_type or company_transaction or no_taxes_applied or has_non_gst_item:
-		return False
-
-	return True
-
-def validate_einvoice_fields(doc):
-	invoice_eligible = validate_eligibility(doc)
-
-	if not invoice_eligible:
-		return
-
-	if doc.docstatus == 0 and doc._action == 'save':
-		if doc.irn:
-			frappe.throw(_('You cannot edit the invoice after generating IRN'), title=_('Edit Not Allowed'))
-		if len(doc.name) > 16:
-			raise_document_name_too_long_error()
-
-		doc.einvoice_status = 'Pending'
-
-	elif doc.docstatus == 1 and doc._action == 'submit' and not doc.irn:
-		frappe.throw(_('You must generate IRN before submitting the document.'), title=_('Missing IRN'))
-
-	elif doc.irn and doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
-		frappe.throw(_('You must cancel IRN before cancelling the document.'), title=_('Cancel Not Allowed'))
-
-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 += ', '
-	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(
-		bold(_('modify')), bold(_('naming series')), bold(_('maximum'))
-	)
-	msg += _('Please account for ammended documents too.')
-	frappe.throw(msg, title=title)
-
-def read_json(name):
-	file_path = os.path.join(os.path.dirname(__file__), '{name}.json'.format(name=name))
-	with open(file_path, 'r') as f:
-		return cstr(f.read())
-
-def get_transaction_details(invoice):
-	supply_type = ''
-	if invoice.gst_category == 'Registered Regular': supply_type = 'B2B'
-	elif invoice.gst_category == 'SEZ': supply_type = 'SEZWOP'
-	elif invoice.gst_category == 'Overseas': supply_type = 'EXPWOP'
-	elif invoice.gst_category == 'Deemed Export': supply_type = 'DEXP'
-
-	if not supply_type:
-		rr, sez, overseas, export = bold('Registered Regular'), bold('SEZ'), bold('Overseas'), bold('Deemed Export')
-		frappe.throw(_('GST category should be one of {}, {}, {}, {}').format(rr, sez, overseas, export),
-			title=_('Invalid Supply Type'))
-
-	return frappe._dict(dict(
-		tax_scheme='GST',
-		supply_type=supply_type,
-		reverse_charge=invoice.reverse_charge
-	))
-
-def get_doc_details(invoice):
-	if getdate(invoice.posting_date) < getdate('2021-01-01'):
-		frappe.throw(_('IRN generation is not allowed for invoices dated before 1st Jan 2021'), title=_('Not Allowed'))
-
-	invoice_type = 'CRN' if invoice.is_return else 'INV'
-
-	invoice_name = invoice.name
-	invoice_date = format_date(invoice.posting_date, 'dd/mm/yyyy')
-
-	return frappe._dict(dict(
-		invoice_type=invoice_type,
-		invoice_name=invoice_name,
-		invoice_date=invoice_date
-	))
-
-def validate_address_fields(address, is_shipping_address):
-	if ((not address.gstin and not is_shipping_address)
-		or not address.city
-		or not address.pincode
-		or not address.address_title
-		or not address.address_line1
-		or not address.gst_state_number):
-
-		frappe.throw(
-			msg=_('Address Lines, City, Pincode, GSTIN are mandatory for address {}. Please set them and try again.').format(address.name),
-			title=_('Missing Address Fields')
-		)
-
-def get_party_details(address_name, is_shipping_address=False):
-	addr = frappe.get_doc('Address', address_name)
-
-	validate_address_fields(addr, is_shipping_address)
-
-	if addr.gst_state_number == 97:
-		# according to einvoice standard
-		addr.pincode = 999999
-
-	party_address_details = frappe._dict(dict(
-		legal_name=sanitize_for_json(addr.address_title),
-		location=sanitize_for_json(addr.city),
-		pincode=addr.pincode, gstin=addr.gstin,
-		state_code=addr.gst_state_number,
-		address_line1=sanitize_for_json(addr.address_line1),
-		address_line2=sanitize_for_json(addr.address_line2)
-	))
-
-	return party_address_details
-
-def get_overseas_address_details(address_name):
-	address_title, address_line1, address_line2, city = frappe.db.get_value(
-		'Address', address_name, ['address_title', 'address_line1', 'address_line2', 'city']
-	)
-
-	if not address_title or not address_line1 or not city:
-		frappe.throw(
-			msg=_('Address lines and city is mandatory for address {}. Please set them and try again.').format(
-				get_link_to_form('Address', address_name)
-			),
-			title=_('Missing Address Fields')
-		)
-
-	return frappe._dict(dict(
-		gstin='URP',
-		legal_name=sanitize_for_json(address_title),
-		location=city,
-		address_line1=sanitize_for_json(address_line1),
-		address_line2=sanitize_for_json(address_line2),
-		pincode=999999, state_code=96, place_of_supply=96
-	))
-
-def get_item_list(invoice):
-	item_list = []
-
-	for d in invoice.items:
-		einvoice_item_schema = read_json('einv_item_template')
-		item = frappe._dict({})
-		item.update(d.as_dict())
-
-		item.sr_no = d.idx
-		item.description = sanitize_for_json(d.item_name)
-
-		item.qty = abs(item.qty)
-		if flt(item.qty) != 0.0:
-			item.unit_rate = abs(item.taxable_value / item.qty)
-		else:
-			item.unit_rate = abs(item.taxable_value)
-		item.gross_amount = abs(item.taxable_value)
-		item.taxable_value = abs(item.taxable_value)
-		item.discount_amount = 0
-
-		item.batch_expiry_date = frappe.db.get_value('Batch', d.batch_no, 'expiry_date') if d.batch_no else None
-		item.batch_expiry_date = format_date(item.batch_expiry_date, 'dd/mm/yyyy') if item.batch_expiry_date else None
-		item.is_service_item = 'Y' if item.gst_hsn_code and item.gst_hsn_code[:2] == "99" else 'N'
-		item.serial_no = ""
-
-		item = update_item_taxes(invoice, item)
-
-		item.total_value = abs(
-			item.taxable_value + item.igst_amount + item.sgst_amount +
-			item.cgst_amount + item.cess_amount + item.cess_nadv_amount + item.other_charges
-		)
-		einv_item = einvoice_item_schema.format(item=item)
-		item_list.append(einv_item)
-
-	return ', '.join(item_list)
-
-def update_item_taxes(invoice, item):
-	gst_accounts = get_gst_accounts(invoice.company)
-	gst_accounts_list = [d for accounts in gst_accounts.values() for d in accounts if d]
-
-	for attr in [
-		'tax_rate', 'cess_rate', 'cess_nadv_amount',
-		'cgst_amount',  'sgst_amount', 'igst_amount',
-		'cess_amount', 'cess_nadv_amount', 'other_charges'
-		]:
-		item[attr] = 0
-
-	for t in invoice.taxes:
-		is_applicable = t.tax_amount and t.account_head in gst_accounts_list
-		if is_applicable:
-			# this contains item wise tax rate & tax amount (incl. discount)
-			item_tax_detail = json.loads(t.item_wise_tax_detail).get(item.item_code or item.item_name)
-
-			item_tax_rate = item_tax_detail[0]
-			# item tax amount excluding discount amount
-			item_tax_amount = (item_tax_rate / 100) * item.taxable_value
-
-			if t.account_head in gst_accounts.cess_account:
-				item_tax_amount_after_discount = item_tax_detail[1]
-				if t.charge_type == 'On Item Quantity':
-					item.cess_nadv_amount += abs(item_tax_amount_after_discount)
-				else:
-					item.cess_rate += item_tax_rate
-					item.cess_amount += abs(item_tax_amount_after_discount)
-
-			for tax_type in ['igst', 'cgst', 'sgst']:
-				if t.account_head in gst_accounts[f'{tax_type}_account']:
-					item.tax_rate += item_tax_rate
-					item[f'{tax_type}_amount'] += abs(item_tax_amount)
-		else:
-			# TODO: other charges per item
-			pass
-
-	return item
-
-def get_invoice_value_details(invoice):
-	invoice_value_details = frappe._dict(dict())
-	invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
-	invoice_value_details.invoice_discount_amt = 0
-
-	invoice_value_details.round_off = invoice.base_rounding_adjustment
-	invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
-	invoice_value_details.grand_total = abs(invoice.rounded_total) or abs(invoice.grand_total)
-
-	invoice_value_details = update_invoice_taxes(invoice, invoice_value_details)
-
-	return invoice_value_details
-
-def update_invoice_taxes(invoice, invoice_value_details):
-	gst_accounts = get_gst_accounts(invoice.company)
-	gst_accounts_list = [d for accounts in gst_accounts.values() for d in accounts if d]
-
-	invoice_value_details.total_cgst_amt = 0
-	invoice_value_details.total_sgst_amt = 0
-	invoice_value_details.total_igst_amt = 0
-	invoice_value_details.total_cess_amt = 0
-	invoice_value_details.total_other_charges = 0
-	considered_rows = []
-
-	for t in invoice.taxes:
-		tax_amount = t.base_tax_amount_after_discount_amount
-		if t.account_head in gst_accounts_list:
-			if t.account_head in gst_accounts.cess_account:
-				# using after discount amt since item also uses after discount amt for cess calc
-				invoice_value_details.total_cess_amt += abs(t.base_tax_amount_after_discount_amount)
-
-			for tax_type in ['igst', 'cgst', 'sgst']:
-				if t.account_head in gst_accounts[f'{tax_type}_account']:
-
-					invoice_value_details[f'total_{tax_type}_amt'] += abs(tax_amount)
-				update_other_charges(t, invoice_value_details, gst_accounts_list, invoice, considered_rows)
-		else:
-			invoice_value_details.total_other_charges += abs(tax_amount)
-
-	return invoice_value_details
-
-def update_other_charges(tax_row, invoice_value_details, gst_accounts_list, invoice, considered_rows):
-	prev_row_id = cint(tax_row.row_id) - 1
-	if tax_row.account_head in gst_accounts_list and prev_row_id not in considered_rows:
-		if tax_row.charge_type == 'On Previous Row Amount':
-			amount = invoice.get('taxes')[prev_row_id].tax_amount_after_discount_amount
-			invoice_value_details.total_other_charges -= abs(amount)
-			considered_rows.append(prev_row_id)
-		if tax_row.charge_type == 'On Previous Row Total':
-			amount = invoice.get('taxes')[prev_row_id].base_total - invoice.base_net_total
-			invoice_value_details.total_other_charges -= abs(amount)
-			considered_rows.append(prev_row_id)
-
-def get_payment_details(invoice):
-	payee_name = invoice.company
-	mode_of_payment = ', '.join([d.mode_of_payment for d in invoice.payments])
-	paid_amount = invoice.base_paid_amount
-	outstanding_amount = invoice.outstanding_amount
-
-	return frappe._dict(dict(
-		payee_name=payee_name, mode_of_payment=mode_of_payment,
-		paid_amount=paid_amount, outstanding_amount=outstanding_amount
-	))
-
-def get_return_doc_reference(invoice):
-	invoice_date = frappe.db.get_value('Sales Invoice', invoice.return_against, 'posting_date')
-	return frappe._dict(dict(
-		invoice_name=invoice.return_against, invoice_date=format_date(invoice_date, 'dd/mm/yyyy')
-	))
-
-def get_eway_bill_details(invoice):
-	if invoice.is_return:
-		frappe.throw(_('E-Way Bill cannot be generated for Credit Notes & Debit Notes. Please clear fields in the Transporter Section of the invoice.'),
-			title=_('Invalid Fields'))
-
-
-	mode_of_transport = { '': '', 'Road': '1', 'Air': '2', 'Rail': '3', 'Ship': '4' }
-	vehicle_type = { 'Regular': 'R', 'Over Dimensional Cargo (ODC)': 'O' }
-
-	return frappe._dict(dict(
-		gstin=invoice.gst_transporter_id,
-		name=invoice.transporter_name,
-		mode_of_transport=mode_of_transport[invoice.mode_of_transport],
-		distance=invoice.distance or 0,
-		document_name=invoice.lr_no,
-		document_date=format_date(invoice.lr_date, 'dd/mm/yyyy'),
-		vehicle_no=invoice.vehicle_no,
-		vehicle_type=vehicle_type[invoice.gst_vehicle_type]
-	))
-
-def validate_mandatory_fields(invoice):
-	if not invoice.company_address:
-		frappe.throw(
-			_('Company Address is mandatory to fetch company GSTIN details. Please set Company Address and try again.'),
-			title=_('Missing Fields')
-		)
-	if not invoice.customer_address:
-		frappe.throw(
-			_('Customer Address is mandatory to fetch customer GSTIN details. Please set Company Address and try again.'),
-			title=_('Missing Fields')
-		)
-	if not frappe.db.get_value('Address', invoice.company_address, 'gstin'):
-		frappe.throw(
-			_('GSTIN is mandatory to fetch company GSTIN details. Please enter GSTIN in selected company address.'),
-			title=_('Missing Fields')
-		)
-	if invoice.gst_category != 'Overseas' and not frappe.db.get_value('Address', invoice.customer_address, 'gstin'):
-		frappe.throw(
-			_('GSTIN is mandatory to fetch customer GSTIN details. Please enter GSTIN in selected customer address.'),
-			title=_('Missing Fields')
-		)
-
-def validate_totals(einvoice):
-	item_list = einvoice['ItemList']
-	value_details = einvoice['ValDtls']
-
-	total_item_ass_value = 0
-	total_item_cgst_value = 0
-	total_item_sgst_value = 0
-	total_item_igst_value = 0
-	total_item_value = 0
-	for item in item_list:
-		total_item_ass_value += flt(item['AssAmt'])
-		total_item_cgst_value += flt(item['CgstAmt'])
-		total_item_sgst_value += flt(item['SgstAmt'])
-		total_item_igst_value += flt(item['IgstAmt'])
-		total_item_value += flt(item['TotItemVal'])
-
-		if abs(flt(item['AssAmt']) * flt(item['GstRt']) / 100) - (flt(item['CgstAmt']) + flt(item['SgstAmt']) + flt(item['IgstAmt'])) > 1:
-			frappe.throw(_('Row #{}: GST rate is invalid. Please remove tax rows with zero tax amount from taxes table.').format(item.idx))
-
-	if abs(flt(value_details['AssVal']) - total_item_ass_value) > 1:
-		frappe.throw(_('Total Taxable Value of the items is not equal to the Invoice Net Total. Please check item taxes / discounts for any correction.'))
-
-	if abs(
-		flt(value_details['TotInvVal']) + flt(value_details['Discount']) -
-		flt(value_details['OthChrg']) - flt(value_details['RndOffAmt']) -
-		total_item_value) > 1:
-		frappe.throw(_('Total Value of the items is not equal to the Invoice Grand Total. Please check item taxes / discounts for any correction.'))
-
-	calculated_invoice_value = \
-			flt(value_details['AssVal']) + flt(value_details['CgstVal']) \
-			+ flt(value_details['SgstVal']) + flt(value_details['IgstVal']) \
-			+ flt(value_details['OthChrg']) + flt(value_details['RndOffAmt']) - flt(value_details['Discount'])
-
-	if abs(flt(value_details['TotInvVal']) - calculated_invoice_value) > 1:
-		frappe.throw(_('Total Item Value + Taxes - Discount is not equal to the Invoice Grand Total. Please check taxes / discounts for any correction.'))
-
-def make_einvoice(invoice):
-	validate_mandatory_fields(invoice)
-
-	schema = read_json('einv_template')
-
-	transaction_details = get_transaction_details(invoice)
-	item_list = get_item_list(invoice)
-	doc_details = get_doc_details(invoice)
-	invoice_value_details = get_invoice_value_details(invoice)
-	seller_details = get_party_details(invoice.company_address)
-
-	if invoice.gst_category == 'Overseas':
-		buyer_details = get_overseas_address_details(invoice.customer_address)
-	else:
-		buyer_details = get_party_details(invoice.customer_address)
-		place_of_supply = get_place_of_supply(invoice, invoice.doctype)
-		if place_of_supply:
-			place_of_supply = place_of_supply.split('-')[0]
-		else:
-			place_of_supply = sanitize_for_json(invoice.billing_address_gstin)[:2]
-		buyer_details.update(dict(place_of_supply=place_of_supply))
-
-	seller_details.update(dict(legal_name=invoice.company))
-	buyer_details.update(dict(legal_name=invoice.customer_name or invoice.customer))
-
-	shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
-	if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name:
-		if invoice.gst_category == 'Overseas':
-			shipping_details = get_overseas_address_details(invoice.shipping_address_name)
-		else:
-			shipping_details = get_party_details(invoice.shipping_address_name, is_shipping_address=True)
-
-	if invoice.is_pos and invoice.base_paid_amount:
-		payment_details = get_payment_details(invoice)
-
-	if invoice.is_return and invoice.return_against:
-		prev_doc_details = get_return_doc_reference(invoice)
-
-	if invoice.transporter and not invoice.is_return:
-		eway_bill_details = get_eway_bill_details(invoice)
-
-	# not yet implemented
-	dispatch_details = period_details = export_details = frappe._dict({})
-
-	einvoice = schema.format(
-		transaction_details=transaction_details, doc_details=doc_details, dispatch_details=dispatch_details,
-		seller_details=seller_details, buyer_details=buyer_details, shipping_details=shipping_details,
-		item_list=item_list, invoice_value_details=invoice_value_details, payment_details=payment_details,
-		period_details=period_details, prev_doc_details=prev_doc_details,
-		export_details=export_details, eway_bill_details=eway_bill_details
-	)
-
-	try:
-		einvoice = safe_json_load(einvoice)
-		einvoice = santize_einvoice_fields(einvoice)
-	except Exception:
-		show_link_to_error_log(invoice, einvoice)
-
-	validate_totals(einvoice)
-
-	return einvoice
-
-def show_link_to_error_log(invoice, einvoice):
-	err_log = log_error(einvoice)
-	link_to_error_log = get_link_to_form('Error Log', err_log.name, 'Error Log')
-	frappe.throw(
-		_('An error occurred while creating e-invoice for {}. Please check {} for more information.').format(
-			invoice.name, link_to_error_log),
-		title=_('E Invoice Creation Failed')
-	)
-
-def log_error(data=None):
-	if isinstance(data, six.string_types):
-		data = json.loads(data)
-
-	seperator = "--" * 50
-	err_tb = traceback.format_exc()
-	err_msg = str(sys.exc_info()[1])
-	data = json.dumps(data, indent=4)
-
-	message = "\n".join([
-		"Error", err_msg, seperator,
-		"Data:", data, seperator,
-		"Exception:", err_tb
-	])
-	frappe.log_error(title=_('E Invoice Request Failed'), message=message)
-
-def santize_einvoice_fields(einvoice):
-	int_fields = ["Pin","Distance","CrDay"]
-	float_fields = ["Qty","FreeQty","UnitPrice","TotAmt","Discount","PreTaxVal","AssAmt","GstRt","IgstAmt","CgstAmt","SgstAmt","CesRt","CesAmt","CesNonAdvlAmt","StateCesRt","StateCesAmt","StateCesNonAdvlAmt","OthChrg","TotItemVal","AssVal","CgstVal","SgstVal","IgstVal","CesVal","StCesVal","Discount","OthChrg","RndOffAmt","TotInvVal","TotInvValFc","PaidAmt","PaymtDue","ExpDuty",]
-	copy = einvoice.copy()
-	for key, value in copy.items():
-		if isinstance(value, list):
-			for idx, d in enumerate(value):
-				santized_dict = santize_einvoice_fields(d)
-				if santized_dict:
-					einvoice[key][idx] = santized_dict
-				else:
-					einvoice[key].pop(idx)
-
-			if not einvoice[key]:
-				einvoice.pop(key, None)
-
-		elif isinstance(value, dict):
-			santized_dict = santize_einvoice_fields(value)
-			if santized_dict:
-				einvoice[key] = santized_dict
-			else:
-				einvoice.pop(key, None)
-
-		elif not value or value == "None":
-			einvoice.pop(key, None)
-
-		elif key in float_fields:
-			einvoice[key] = flt(value, 2)
-
-		elif key in int_fields:
-			einvoice[key] = cint(value)
-
-	return einvoice
-
-def safe_json_load(json_string):
-	try:
-		return json.loads(json_string)
-	except json.JSONDecodeError as e:
-		# print a snippet of 40 characters around the location where error occured
-		pos = e.pos
-		start, end = max(0, pos-20), min(len(json_string)-1, pos+20)
-		snippet = json_string[start:end]
-		frappe.throw(_("Error in input data. Please check for any special characters near following input: <br> {}").format(snippet))
-
-class RequestFailed(Exception):
-	pass
-class CancellationNotAllowed(Exception):
-	pass
-
-class GSPConnector():
-	def __init__(self, doctype=None, docname=None):
-		self.doctype = doctype
-		self.docname = docname
-
-		self.set_invoice()
-		self.set_credentials()
-
-		# authenticate url is same for sandbox & live
-		self.authenticate_url = 'https://gsp.adaequare.com/gsp/authenticate?grant_type=token'
-		self.base_url = 'https://gsp.adaequare.com' if not self.e_invoice_settings.sandbox_mode else 'https://gsp.adaequare.com/test'
-
-		self.cancel_irn_url = self.base_url + '/enriched/ei/api/invoice/cancel'
-		self.irn_details_url = self.base_url + '/enriched/ei/api/invoice/irn'
-		self.generate_irn_url = self.base_url + '/enriched/ei/api/invoice'
-		self.gstin_details_url = self.base_url + '/enriched/ei/api/master/gstin'
-		self.cancel_ewaybill_url = self.base_url + '/enriched/ewb/ewayapi?action=CANEWB'
-		self.generate_ewaybill_url = self.base_url + '/enriched/ei/api/ewaybill'
-
-	def set_invoice(self):
-		self.invoice = None
-		if self.doctype and self.docname:
-			self.invoice = frappe.get_cached_doc(self.doctype, self.docname)
-
-	def set_credentials(self):
-		self.e_invoice_settings = frappe.get_cached_doc('E Invoice Settings')
-
-		if not self.e_invoice_settings.enable:
-			frappe.throw(_("E-Invoicing is disabled. Please enable it from {} to generate e-invoices.").format(get_link_to_form("E Invoice Settings", "E Invoice Settings")))
-
-		if self.invoice:
-			gstin = self.get_seller_gstin()
-			credentials_for_gstin = [d for d in self.e_invoice_settings.credentials if d.gstin == gstin]
-			if credentials_for_gstin:
-				self.credentials = credentials_for_gstin[0]
-			else:
-				frappe.throw(_('Cannot find e-invoicing credentials for selected Company GSTIN. Please check E-Invoice Settings'))
-		else:
-			self.credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None
-
-	def get_seller_gstin(self):
-		gstin = frappe.db.get_value('Address', self.invoice.company_address, 'gstin')
-		if not gstin:
-			frappe.throw(_('Cannot retrieve Company GSTIN. Please select company address with valid GSTIN.'))
-		return gstin
-
-	def get_auth_token(self):
-		if time_diff_in_seconds(self.e_invoice_settings.token_expiry, now_datetime()) < 150.0:
-			self.fetch_auth_token()
-
-		return self.e_invoice_settings.auth_token
-
-	def make_request(self, request_type, url, headers=None, data=None):
-		if request_type == 'post':
-			res = make_post_request(url, headers=headers, data=data)
-		else:
-			res = make_get_request(url, headers=headers, data=data)
-
-		self.log_request(url, headers, data, res)
-		return res
-
-	def log_request(self, url, headers, data, res):
-		headers.update({ 'password': self.credentials.password })
-		request_log = frappe.get_doc({
-			"doctype": "E Invoice Request Log",
-			"user": frappe.session.user,
-			"reference_invoice": self.invoice.name if self.invoice else None,
-			"url": url,
-			"headers": json.dumps(headers, indent=4) if headers else None,
-			"data": json.dumps(data, indent=4) if isinstance(data, dict) else data,
-			"response": json.dumps(res, indent=4) if res else None
-		})
-		request_log.save(ignore_permissions=True)
-		frappe.db.commit()
-
-	def fetch_auth_token(self):
-		headers = {
-			'gspappid': frappe.conf.einvoice_client_id,
-			'gspappsecret': frappe.conf.einvoice_client_secret
-		}
-		res = {}
-		try:
-			res = self.make_request('post', self.authenticate_url, headers)
-			self.e_invoice_settings.auth_token = "{} {}".format(res.get('token_type'), res.get('access_token'))
-			self.e_invoice_settings.token_expiry = add_to_date(None, seconds=res.get('expires_in'))
-			self.e_invoice_settings.save(ignore_permissions=True)
-			self.e_invoice_settings.reload()
-
-		except Exception:
-			log_error(res)
-			self.raise_error(True)
-
-	def get_headers(self):
-		return {
-			'content-type': 'application/json',
-			'user_name': self.credentials.username,
-			'password': self.credentials.get_password(),
-			'gstin': self.credentials.gstin,
-			'authorization': self.get_auth_token(),
-			'requestid': str(base64.b64encode(os.urandom(18))),
-		}
-
-	def fetch_gstin_details(self, gstin):
-		headers = self.get_headers()
-
-		try:
-			params = '?gstin={gstin}'.format(gstin=gstin)
-			res = self.make_request('get', self.gstin_details_url + params, headers)
-			if res.get('success'):
-				return res.get('result')
-			else:
-				log_error(res)
-				raise RequestFailed
-
-		except RequestFailed:
-			self.raise_error()
-
-		except Exception:
-			log_error()
-			self.raise_error(True)
-	@staticmethod
-	def get_gstin_details(gstin):
-		'''fetch and cache GSTIN details'''
-		if not hasattr(frappe.local, 'gstin_cache'):
-			frappe.local.gstin_cache = {}
-
-		key = gstin
-		gsp_connector = GSPConnector()
-		details = gsp_connector.fetch_gstin_details(gstin)
-
-		frappe.local.gstin_cache[key] = details
-		frappe.cache().hset('gstin_cache', key, details)
-		return details
-
-	def generate_irn(self):
-		data = {}
-		try:
-			headers = self.get_headers()
-			einvoice = make_einvoice(self.invoice)
-			data = json.dumps(einvoice, indent=4)
-			res = self.make_request('post', self.generate_irn_url, headers, data)
-
-			if res.get('success'):
-				self.set_einvoice_data(res.get('result'))
-
-			elif '2150' in res.get('message'):
-				# IRN already generated but not updated in invoice
-				# Extract the IRN from the response description and fetch irn details
-				irn = res.get('result')[0].get('Desc').get('Irn')
-				irn_details = self.get_irn_details(irn)
-				if irn_details:
-					self.set_einvoice_data(irn_details)
-				else:
-					raise RequestFailed('IRN has already been generated for the invoice but cannot fetch details for the it. \
-						Contact ERPNext support to resolve the issue.')
-
-			else:
-				raise RequestFailed
-
-		except RequestFailed:
-			errors = self.sanitize_error_message(res.get('message'))
-			self.set_failed_status(errors=errors)
-			self.raise_error(errors=errors)
-
-		except Exception as e:
-			self.set_failed_status(errors=str(e))
-			log_error(data)
-			self.raise_error(True)
-
-	@staticmethod
-	def bulk_generate_irn(invoices):
-		gsp_connector = GSPConnector()
-		gsp_connector.doctype = 'Sales Invoice'
-
-		failed = []
-
-		for invoice in invoices:
-			try:
-				gsp_connector.docname = invoice
-				gsp_connector.set_invoice()
-				gsp_connector.set_credentials()
-				gsp_connector.generate_irn()
-
-			except Exception as e:
-				failed.append({
-					'docname': invoice,
-					'message': str(e)
-				})
-
-		return failed
-
-	def get_irn_details(self, irn):
-		headers = self.get_headers()
-
-		try:
-			params = '?irn={irn}'.format(irn=irn)
-			res = self.make_request('get', self.irn_details_url + params, headers)
-			if res.get('success'):
-				return res.get('result')
-			else:
-				raise RequestFailed
-
-		except RequestFailed:
-			errors = self.sanitize_error_message(res.get('message'))
-			self.raise_error(errors=errors)
-
-		except Exception:
-			log_error()
-			self.raise_error(True)
-
-	def cancel_irn(self, irn, reason, remark):
-		data, res = {}, {}
-		try:
-			# validate cancellation
-			if time_diff_in_hours(now_datetime(), self.invoice.ack_date) > 24:
-				frappe.throw(_('E-Invoice cannot be cancelled after 24 hours of IRN generation.'), title=_('Not Allowed'), exc=CancellationNotAllowed)
-			if not irn:
-				frappe.throw(_('IRN not found. You must generate IRN before cancelling.'), title=_('Not Allowed'), exc=CancellationNotAllowed)
-
-			headers = self.get_headers()
-			data = json.dumps({
-				'Irn': irn,
-				'Cnlrsn': reason,
-				'Cnlrem': remark
-			}, indent=4)
-
-			res = self.make_request('post', self.cancel_irn_url, headers, data)
-			if res.get('success') or '9999' in res.get('message'):
-				self.invoice.irn_cancelled = 1
-				self.invoice.irn_cancel_date = res.get('result')['CancelDate'] if res.get('result') else ""
-				self.invoice.einvoice_status = 'Cancelled'
-				self.invoice.flags.updater_reference = {
-					'doctype': self.invoice.doctype,
-					'docname': self.invoice.name,
-					'label': _('IRN Cancelled - {}').format(remark)
-				}
-				self.update_invoice()
-
-			else:
-				raise RequestFailed
-
-		except RequestFailed:
-			errors = self.sanitize_error_message(res.get('message'))
-			self.set_failed_status(errors=errors)
-			self.raise_error(errors=errors)
-
-		except CancellationNotAllowed as e:
-			self.set_failed_status(errors=str(e))
-			self.raise_error(errors=str(e))
-
-		except Exception as e:
-			self.set_failed_status(errors=str(e))
-			log_error(data)
-			self.raise_error(True)
-
-	@staticmethod
-	def bulk_cancel_irn(invoices, reason, remark):
-		gsp_connector = GSPConnector()
-		gsp_connector.doctype = 'Sales Invoice'
-
-		failed = []
-
-		for invoice in invoices:
-			try:
-				gsp_connector.docname = invoice
-				gsp_connector.set_invoice()
-				gsp_connector.set_credentials()
-				irn = gsp_connector.invoice.irn
-				gsp_connector.cancel_irn(irn, reason, remark)
-
-			except Exception as e:
-				failed.append({
-					'docname': invoice,
-					'message': str(e)
-				})
-
-		return failed
-
-	def generate_eway_bill(self, **kwargs):
-		args = frappe._dict(kwargs)
-
-		headers = self.get_headers()
-		eway_bill_details = get_eway_bill_details(args)
-		data = json.dumps({
-			'Irn': args.irn,
-			'Distance': cint(eway_bill_details.distance),
-			'TransMode': eway_bill_details.mode_of_transport,
-			'TransId': eway_bill_details.gstin,
-			'TransName': eway_bill_details.transporter,
-			'TrnDocDt': eway_bill_details.document_date,
-			'TrnDocNo': eway_bill_details.document_name,
-			'VehNo': eway_bill_details.vehicle_no,
-			'VehType': eway_bill_details.vehicle_type
-		}, indent=4)
-
-		try:
-			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 = {
-					'doctype': self.invoice.doctype,
-					'docname': self.invoice.name,
-					'label': _('E-Way Bill Generated')
-				}
-				self.update_invoice()
-
-			else:
-				raise RequestFailed
-
-		except RequestFailed:
-			errors = self.sanitize_error_message(res.get('message'))
-			self.raise_error(errors=errors)
-
-		except Exception:
-			log_error(data)
-			self.raise_error(True)
-
-	def cancel_eway_bill(self, eway_bill, reason, remark):
-		headers = self.get_headers()
-		data = json.dumps({
-			'ewbNo': eway_bill,
-			'cancelRsnCode': reason,
-			'cancelRmrk': remark
-		}, indent=4)
-		headers["username"] = headers["user_name"]
-		del headers["user_name"]
-		try:
-			res = self.make_request('post', self.cancel_ewaybill_url, headers, data)
-			if res.get('success'):
-				self.invoice.ewaybill = ''
-				self.invoice.eway_bill_cancelled = 1
-				self.invoice.flags.updater_reference = {
-					'doctype': self.invoice.doctype,
-					'docname': self.invoice.name,
-					'label': _('E-Way Bill Cancelled - {}').format(remark)
-				}
-				self.update_invoice()
-
-			else:
-				raise RequestFailed
-
-		except RequestFailed:
-			errors = self.sanitize_error_message(res.get('message'))
-			self.raise_error(errors=errors)
-
-		except Exception:
-			log_error(data)
-			self.raise_error(True)
-
-	def sanitize_error_message(self, message):
-		'''
-			On validation errors, response message looks something like this:
-			message = '2174 : For inter-state transaction, CGST and SGST amounts are not applicable; only IGST amount is applicable,
-						3095 : Supplier GSTIN is inactive'
-			we search for string between ':' to extract the error messages
-			errors = [
-				': For inter-state transaction, CGST and SGST amounts are not applicable; only IGST amount is applicable, 3095 ',
-				': Test'
-			]
-			then we trim down the message by looping over errors
-		'''
-		if not message:
-			return []
-
-		errors = re.findall(': [^:]+', message)
-		for idx, e in enumerate(errors):
-			# remove colons
-			errors[idx] = errors[idx].replace(':', '').strip()
-			# if not last
-			if idx != len(errors) - 1:
-				# remove last 7 chars eg: ', 3095 '
-				errors[idx] = errors[idx][:-6]
-
-		return errors
-
-	def raise_error(self, raise_exception=False, errors=[]):
-		title = _('E Invoice Request Failed')
-		if errors:
-			frappe.throw(errors, title=title, as_list=1)
-		else:
-			link_to_error_list = '<a href="desk#List/Error Log/List?method=E Invoice Request Failed">Error Log</a>'
-			frappe.msgprint(
-				_('An error occurred while making e-invoicing request. Please check {} for more information.').format(link_to_error_list),
-				title=title,
-				raise_exception=raise_exception,
-				indicator='red'
-			)
-
-	def set_einvoice_data(self, res):
-		enc_signed_invoice = res.get('SignedInvoice')
-		dec_signed_invoice = jwt.decode(enc_signed_invoice, options={"verify_signature": False})['data']
-
-		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
-		self.invoice.ack_no = res.get('AckNo')
-		self.invoice.ack_date = res.get('AckDt')
-		self.invoice.signed_qr_code = res.get('SignedQRCode')
-		self.invoice.einvoice_status = 'Generated'
-
-		self.attach_qrcode_image()
-
-		self.invoice.flags.updater_reference = {
-			'doctype': self.invoice.doctype,
-			'docname': self.invoice.name,
-			'label': _('IRN Generated')
-		}
-		self.update_invoice()
-
-	def attach_qrcode_image(self):
-		qrcode = self.invoice.signed_qr_code
-		doctype = self.invoice.doctype
-		docname = self.invoice.name
-		filename = 'QRCode_{}.png'.format(docname).replace(os.path.sep, "__")
-
-		qr_image = io.BytesIO()
-		url = qrcreate(qrcode, error='L')
-		url.png(qr_image, scale=2, quiet_zone=1)
-		_file = frappe.get_doc({
-			"doctype": "File",
-			"file_name": filename,
-			"attached_to_doctype": doctype,
-			"attached_to_name": docname,
-			"attached_to_field": "qrcode_image",
-			"is_private": 0,
-			"content": qr_image.getvalue()})
-		_file.save()
-		frappe.db.commit()
-		self.invoice.qrcode_image = _file.file_url
-
-	def update_invoice(self):
-		self.invoice.flags.ignore_validate_update_after_submit = True
-		self.invoice.flags.ignore_validate = True
-		self.invoice.save()
-
-	def set_failed_status(self, errors=None):
-		frappe.db.rollback()
-		self.invoice.einvoice_status = 'Failed'
-		self.invoice.failure_description = self.get_failure_message(errors) if errors else ""
-		self.update_invoice()
-		frappe.db.commit()
-
-	def get_failure_message(self, errors):
-		if isinstance(errors, list):
-			errors = ', '.join(errors)
-		return errors
-
-def sanitize_for_json(string):
-	"""Escape JSON specific characters from a string."""
-
-	# json.dumps adds double-quotes to the string. Indexing to remove them.
-	return json.dumps(string)[1:-1]
-
-@frappe.whitelist()
-def get_einvoice(doctype, docname):
-	invoice = frappe.get_doc(doctype, docname)
-	return make_einvoice(invoice)
-
-@frappe.whitelist()
-def generate_irn(doctype, docname):
-	gsp_connector = GSPConnector(doctype, docname)
-	gsp_connector.generate_irn()
-
-@frappe.whitelist()
-def cancel_irn(doctype, docname, irn, reason, remark):
-	gsp_connector = GSPConnector(doctype, docname)
-	gsp_connector.cancel_irn(irn, reason, remark)
-
-@frappe.whitelist()
-def generate_eway_bill(doctype, docname, **kwargs):
-	gsp_connector = GSPConnector(doctype, docname)
-	gsp_connector.generate_eway_bill(**kwargs)
-
-@frappe.whitelist()
-def cancel_eway_bill(doctype, docname):
-	# TODO: uncomment when eway_bill api from Adequare is enabled
-	# gsp_connector = GSPConnector(doctype, docname)
-	# gsp_connector.cancel_eway_bill(eway_bill, reason, remark)
-
-	frappe.db.set_value(doctype, docname, 'ewaybill', '')
-	frappe.db.set_value(doctype, docname, 'eway_bill_cancelled', 1)
-
-@frappe.whitelist()
-def generate_einvoices(docnames):
-	docnames = json.loads(docnames) or []
-
-	if len(docnames) < 10:
-		failures = GSPConnector.bulk_generate_irn(docnames)
-		frappe.local.message_log = []
-
-		if failures:
-			show_bulk_action_failure_message(failures)
-
-		success = len(docnames) - len(failures)
-		frappe.msgprint(
-			_('{} e-invoices generated successfully').format(success),
-			title=_('Bulk E-Invoice Generation Complete')
-		)
-
-	else:
-		enqueue_bulk_action(schedule_bulk_generate_irn, docnames=docnames)
-
-def schedule_bulk_generate_irn(docnames):
-	failures = GSPConnector.bulk_generate_irn(docnames)
-	frappe.local.message_log = []
-
-	frappe.publish_realtime("bulk_einvoice_generation_complete", {
-		"user": frappe.session.user,
-		"failures": failures,
-		"invoices": docnames
-	})
-
-def show_bulk_action_failure_message(failures):
-	for doc in failures:
-		docname = '<a href="sales-invoice/{0}">{0}</a>'.format(doc.get('docname'))
-		message = doc.get('message').replace("'", '"')
-		if message[0] == '[':
-			errors = json.loads(message)
-			error_list = ''.join(['<li>{}</li>'.format(err) for err in errors])
-			message = '''{} has following errors:<br>
-				<ul style="padding-left: 20px; padding-top: 5px">{}</ul>'''.format(docname, error_list)
-		else:
-			message = '{} - {}'.format(docname, message)
-
-		frappe.msgprint(
-			message,
-			title=_('Bulk E-Invoice Generation Complete'),
-			indicator='red'
-		)
-
-@frappe.whitelist()
-def cancel_irns(docnames, reason, remark):
-	docnames = json.loads(docnames) or []
-
-	if len(docnames) < 10:
-		failures = GSPConnector.bulk_cancel_irn(docnames, reason, remark)
-		frappe.local.message_log = []
-
-		if failures:
-			show_bulk_action_failure_message(failures)
-
-		success = len(docnames) - len(failures)
-		frappe.msgprint(
-			_('{} e-invoices cancelled successfully').format(success),
-			title=_('Bulk E-Invoice Cancellation Complete')
-		)
-	else:
-		enqueue_bulk_action(schedule_bulk_cancel_irn, docnames=docnames, reason=reason, remark=remark)
-
-def schedule_bulk_cancel_irn(docnames, reason, remark):
-	failures = GSPConnector.bulk_cancel_irn(docnames, reason, remark)
-	frappe.local.message_log = []
-
-	frappe.publish_realtime("bulk_einvoice_cancellation_complete", {
-		"user": frappe.session.user,
-		"failures": failures,
-		"invoices": docnames
-	})
-
-def enqueue_bulk_action(job, **kwargs):
-	check_scheduler_status()
-
-	enqueue(
-		job,
-		**kwargs,
-		queue="long",
-		timeout=10000,
-		event="processing_bulk_einvoice_action",
-		now=frappe.conf.developer_mode or frappe.flags.in_test,
-	)
-
-	if job == schedule_bulk_generate_irn:
-		msg = _('E-Invoices will be generated in a background process.')
-	else:
-		msg = _('E-Invoices will be cancelled in a background process.')
-
-	frappe.msgprint(msg, alert=1)
-
-def check_scheduler_status():
-	if is_scheduler_inactive() and not frappe.flags.in_test:
-		frappe.throw(_("Scheduler is inactive. Cannot enqueue job."), title=_("Scheduler Inactive"))
-
-def job_already_enqueued(job_name):
-	enqueued_jobs = [d.get("job_name") for d in get_info()]
-	if job_name in enqueued_jobs:
-		return True
diff --git a/erpnext/regional/report/vat_audit_report/test_vat_audit_report.py b/erpnext/regional/report/vat_audit_report/test_vat_audit_report.py
new file mode 100644
index 0000000..dea17a6
--- /dev/null
+++ b/erpnext/regional/report/vat_audit_report/test_vat_audit_report.py
@@ -0,0 +1,193 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from unittest import TestCase
+from frappe.utils import today
+
+from erpnext.accounts.doctype.account.test_account import create_account
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+
+from erpnext.regional.report.vat_audit_report.vat_audit_report import execute
+
+class TestVATAuditReport(TestCase):
+	def setUp(self):
+		frappe.set_user("Administrator")
+		make_company("_Test Company SA VAT", "_TCSV")
+
+		create_account(account_name="VAT - 0%", account_type="Tax",
+			parent_account="Duties and Taxes - _TCSV", company="_Test Company SA VAT")
+		create_account(account_name="VAT - 15%", account_type="Tax",
+			parent_account="Duties and Taxes - _TCSV", company="_Test Company SA VAT")
+		set_sa_vat_accounts()
+
+		make_item("_Test SA VAT Item")
+		make_item("_Test SA VAT Zero Rated Item", properties = {"is_zero_rated": 1})
+
+		make_customer()
+		make_supplier()
+
+		make_sales_invoices()
+		create_purchase_invoices()
+
+	def tearDown(self):
+		frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company SA VAT'")
+		frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company SA VAT'")
+
+	def test_vat_audit_report(self):
+		filters = {
+			"company": "_Test Company SA VAT",
+			"from_date": today(),
+			"to_date": today()
+		}
+		columns, data = execute(filters)
+		total_tax_amount = 0
+		total_row_tax = 0
+		for row in data:
+			keys = row.keys()
+			# skips total row tax_amount in if.. and skips section header in elif..
+			if 'voucher_no' in keys:
+				total_tax_amount = total_tax_amount + row['tax_amount']
+			elif 'tax_amount' in keys:
+				total_row_tax = total_row_tax + row['tax_amount']
+
+		self.assertEqual(total_tax_amount, total_row_tax)
+
+def make_company(company_name, abbr):
+	if not frappe.db.exists("Company", company_name):
+		company = frappe.get_doc({
+			"doctype": "Company",
+			"company_name": company_name,
+			"abbr": abbr,
+			"default_currency": "ZAR",
+			"country": "South Africa",
+			"create_chart_of_accounts_based_on": "Standard Template"
+		})
+		company.insert()
+	else:
+		company = frappe.get_doc("Company", company_name)
+
+	company.create_default_warehouses()
+
+	if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": company.name}):
+		company.create_default_cost_center()
+
+	company.save()
+
+	return company
+
+def set_sa_vat_accounts():
+	if not frappe.db.exists("South Africa VAT Settings", "_Test Company SA VAT"):
+		vat_accounts = frappe.get_all(
+			"Account",
+			fields=["name"],
+			filters = {
+				"company": "_Test Company SA VAT",
+				"is_group": 0,
+				"account_type": "Tax"
+			}
+		)
+
+		sa_vat_accounts = []
+		for account in vat_accounts:
+			sa_vat_accounts.append({
+				"doctype": "South Africa VAT Account",
+				"account": account.name
+			})
+
+		frappe.get_doc({
+			"company": "_Test Company SA VAT",
+			"vat_accounts": sa_vat_accounts,
+			"doctype": "South Africa VAT Settings",
+		}).insert()
+
+def make_customer():
+	if not frappe.db.exists("Customer", "_Test SA Customer"):
+		frappe.get_doc({
+			"doctype": "Customer",
+			"customer_name": "_Test SA Customer",
+			"customer_type": "Company",
+		}).insert()
+
+def make_supplier():
+	if not frappe.db.exists("Supplier", "_Test SA Supplier"):
+		frappe.get_doc({
+			"doctype": "Supplier",
+			"supplier_name": "_Test SA Supplier",
+			"supplier_type": "Company",
+			"supplier_group":"All Supplier Groups"
+		}).insert()
+
+def make_item(item_code, properties=None):
+	if not frappe.db.exists("Item", item_code):
+		item = frappe.get_doc({
+			"doctype": "Item",
+			"item_code": item_code,
+			"item_name": item_code,
+			"description": item_code,
+			"item_group": "Products"
+		})
+
+		if properties:
+			item.update(properties)
+
+		item.insert()
+
+def make_sales_invoices():
+	def make_sales_invoices_wrapper(item, rate, tax_account, tax_rate, tax=True):
+		si = create_sales_invoice(
+			company="_Test Company SA VAT",
+			customer = "_Test SA Customer",
+			currency = "ZAR",
+			item=item,
+			rate=rate,
+			warehouse = "Finished Goods - _TCSV",
+			debit_to = "Debtors - _TCSV",
+			income_account = "Sales - _TCSV",
+			expense_account = "Cost of Goods Sold - _TCSV",
+			cost_center = "Main - _TCSV",
+			do_not_save=1
+		)
+		if tax:
+			si.append("taxes", {
+					"charge_type": "On Net Total",
+					"account_head": tax_account,
+					"cost_center": "Main - _TCSV",
+					"description": "VAT 15% @ 15.0",
+					"rate": tax_rate
+				})
+
+		si.submit()
+
+	test_item = "_Test SA VAT Item"
+	test_zero_rated_item = "_Test SA VAT Zero Rated Item"
+
+	make_sales_invoices_wrapper(test_item, 100.0, "VAT - 15% - _TCSV", 15.0)
+	make_sales_invoices_wrapper(test_zero_rated_item, 100.0, "VAT - 0% - _TCSV", 0.0)
+
+def create_purchase_invoices():
+	pi = make_purchase_invoice(
+		company = "_Test Company SA VAT",
+		supplier = "_Test SA Supplier",
+		supplier_warehouse = "Finished Goods - _TCSV",
+		warehouse = "Finished Goods - _TCSV",
+		currency = "ZAR",
+		cost_center = "Main - _TCSV",
+		expense_account = "Cost of Goods Sold - _TCSV",
+		item = "_Test SA VAT Item",
+		qty = 1,
+		rate = 100,
+		uom = "Nos",
+		do_not_save = 1
+	)
+	pi.append("taxes", {
+		"charge_type": "On Net Total",
+		"account_head": "VAT - 15% - _TCSV",
+		"cost_center": "Main - _TCSV",
+		"description": "VAT 15% @ 15.0",
+		"rate": 15.0
+	})
+
+	pi.submit()
diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py
index 17aca17..88f6b92 100644
--- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py
+++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py
@@ -1,11 +1,11 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
 import frappe
 import json
 from frappe import _
-from frappe.utils import formatdate
+from frappe.utils import formatdate, get_link_to_form
 
 def execute(filters=None):
 	return VATAuditReport(filters).run()
@@ -42,7 +42,8 @@
 		self.sa_vat_accounts = frappe.get_list("South Africa VAT Account",
 			filters = {"parent": self.filters.company}, pluck="account")
 		if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate:
-			frappe.throw(_("Please set VAT Accounts in South Africa VAT Settings"))
+			link_to_settings = get_link_to_form("South Africa VAT Settings", "", label="South Africa VAT Settings")
+			frappe.throw(_("Please set VAT Accounts in {0}").format(link_to_settings))
 
 	def get_invoice_data(self, doctype):
 		conditions = self.get_conditions()
@@ -69,7 +70,7 @@
 
 		items = frappe.db.sql("""
 			SELECT
-				item_code, parent, taxable_value, base_net_amount, is_zero_rated
+				item_code, parent, base_net_amount, is_zero_rated
 			FROM
 				`tab%s Item`
 			WHERE
@@ -79,7 +80,7 @@
 			if d.item_code not in self.invoice_items.get(d.parent, {}):
 				self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, {
 					'net_amount': 0.0})
-				self.invoice_items[d.parent][d.item_code]['net_amount'] += d.get('taxable_value', 0) or d.get('base_net_amount', 0)
+				self.invoice_items[d.parent][d.item_code]['net_amount'] += d.get('base_net_amount', 0)
 				self.invoice_items[d.parent][d.item_code]['is_zero_rated'] = d.is_zero_rated
 
 	def get_items_based_on_tax_rate(self, doctype):
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index 2f06e98..cb00019 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -111,7 +111,6 @@
 		}
 
 		frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Customer'}
-		frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
 
 		if(!frm.doc.__islocal) {
 			frappe.contacts.render_address_and_contact(frm);
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 9175b71..5913b84 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -268,6 +268,7 @@
    "options": "fa fa-map-marker"
   },
   {
+   "depends_on": "eval: !doc.__islocal",
    "fieldname": "address_html",
    "fieldtype": "HTML",
    "label": "Address HTML",
@@ -284,6 +285,7 @@
    "width": "50%"
   },
   {
+   "depends_on": "eval: !doc.__islocal",
    "fieldname": "contact_html",
    "fieldtype": "HTML",
    "label": "Contact HTML",
diff --git a/erpnext/selling/doctype/customer/test_customer.js b/erpnext/selling/doctype/customer/test_customer.js
deleted file mode 100644
index 65b81af..0000000
--- a/erpnext/selling/doctype/customer/test_customer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Customer", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Customer
-		() => frappe.tests.make('Customer', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py
index b1a5b52..5b33731 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -352,3 +352,26 @@
 			'credit_limit': credit_limit
 		})
 		customer.credit_limits[-1].db_insert()
+
+def create_internal_customer(customer_name, represents_company, allowed_to_interact_with):
+	if not frappe.db.exists("Customer", customer_name):
+		customer = frappe.get_doc({
+			"doctype": "Customer",
+			"customer_group": "_Test Customer Group",
+			"customer_name": customer_name,
+			"customer_type": "Individual",
+			"territory": "_Test Territory",
+			"is_internal_customer": 1,
+			"represents_company": represents_company
+		})
+
+		customer.append("companies", {
+			"company": allowed_to_interact_with
+		})
+
+		customer.insert()
+		customer_name = customer.name
+	else:
+		customer_name = frappe.db.get_value("Customer", customer_name)
+
+	return customer_name
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index bba5401..5d44582 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -718,8 +718,7 @@
 				"doctype": "Maintenance Schedule Item",
 				"field_map": {
 					"parent": "sales_order"
-				},
-				"add_if_empty": True
+				}
 			}
 		}, target_doc)
 
@@ -745,8 +744,7 @@
 				"field_map": {
 					"parent": "prevdoc_docname",
 					"parenttype": "prevdoc_doctype"
-				},
-				"add_if_empty": True
+				}
 			}
 		}, target_doc)
 
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.js b/erpnext/selling/doctype/sales_order/test_sales_order.js
deleted file mode 100644
index 57ed19b..0000000
--- a/erpnext/selling/doctype/sales_order/test_sales_order.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sales Order", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially('Sales Order', [
-		// insert a new Sales Order
-		() => frappe.tests.make([
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.js b/erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.js
deleted file mode 100644
index 3ed7b46..0000000
--- a/erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sales Partner Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Sales Partner Type
-		() => frappe.tests.make('Sales Partner Type', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.js b/erpnext/selling/doctype/selling_settings/selling_settings.js
index d8d3051..cf6fb28 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.js
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.js
@@ -6,26 +6,3 @@
 
 	}
 });
-
-frappe.tour['Selling Settings'] = [
-	{
-		fieldname: "cust_master_name",
-		title: "Customer Naming By",
-		description: __("By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
-	},
-	{
-		fieldname: "selling_price_list",
-		title: "Default Selling Price List",
-		description: __("Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.")
-	},
-	{
-		fieldname: "so_required",
-		title: "Sales Order Required for Sales Invoice & Delivery Note Creation",
-		description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice or Delivery Note without creating a Sales Order first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Sales Order' checkbox in the Customer master.")
-	},
-	{
-		fieldname: "dn_required",
-		title: "Delivery Note Required for Sales Invoice Creation",
-		description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice without creating a Delivery Note first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Delivery Note' checkbox in the Customer master.")
-	}
-];
diff --git a/erpnext/selling/form_tour/sales_order/sales_order.json b/erpnext/selling/form_tour/sales_order/sales_order.json
new file mode 100644
index 0000000..a81eb4a
--- /dev/null
+++ b/erpnext/selling/form_tour/sales_order/sales_order.json
@@ -0,0 +1,97 @@
+{
+ "creation": "2021-06-29 21:13:36.089054",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-06-29 21:13:36.089054",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Order",
+ "owner": "Administrator",
+ "reference_doctype": "Sales Order",
+ "save_on_complete": 1,
+ "steps": [
+  {
+   "description": "Select a customer.",
+   "field": "",
+   "fieldname": "customer",
+   "fieldtype": "Link",
+   "has_next_condition": 1,
+   "is_table_field": 0,
+   "label": "Customer",
+   "next_step_condition": "customer",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Select Customer"
+  },
+  {
+   "description": "You can add items here.",
+   "field": "",
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Items",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "List of items"
+  },
+  {
+   "child_doctype": "Sales Order Item",
+   "description": "Select an item.",
+   "field": "",
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "has_next_condition": 1,
+   "is_table_field": 1,
+   "label": "Item Code",
+   "next_step_condition": "eval: doc.item_code",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Right",
+   "title": "Select Item"
+  },
+  {
+   "child_doctype": "Sales Order Item",
+   "description": "Enter quantity.",
+   "field": "",
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "has_next_condition": 0,
+   "is_table_field": 1,
+   "label": "Quantity",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Right",
+   "title": "Enter Quantity"
+  },
+  {
+   "child_doctype": "Sales Order Item",
+   "description": "Enter rate of the item.",
+   "field": "",
+   "fieldname": "rate",
+   "fieldtype": "Currency",
+   "has_next_condition": 0,
+   "is_table_field": 1,
+   "label": "Rate",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Right",
+   "title": "Enter Rate"
+  },
+  {
+   "description": "You can add sales taxes and charges here.",
+   "field": "",
+   "fieldname": "taxes",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Sales Taxes and Charges",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Add Sales Taxes and Charges"
+  }
+ ],
+ "title": "Sales Order"
+}
\ No newline at end of file
diff --git a/erpnext/selling/form_tour/selling_settings/selling_settings.json b/erpnext/selling/form_tour/selling_settings/selling_settings.json
new file mode 100644
index 0000000..20c718f
--- /dev/null
+++ b/erpnext/selling/form_tour/selling_settings/selling_settings.json
@@ -0,0 +1,65 @@
+{
+ "creation": "2021-06-29 20:39:19.408763",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-06-29 20:49:01.359489",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Selling Settings",
+ "owner": "Administrator",
+ "reference_doctype": "Selling Settings",
+ "save_on_complete": 0,
+ "steps": [
+  {
+   "description": "By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a <a href=\"https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series\" target=\"_blank\">Naming Series</a>. Choose the 'Naming Series' option.",
+   "field": "",
+   "fieldname": "cust_master_name",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Customer Naming By",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Customer Naming By"
+  },
+  {
+   "description": "If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice or Delivery Note without creating a Sales Order first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Sales Order' checkbox in the Customer master.",
+   "field": "",
+   "fieldname": "so_required",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Is Sales Order Required for Sales Invoice & Delivery Note Creation?",
+   "parent_field": "",
+   "position": "Left",
+   "title": "Sales Order Required for Sales Invoice & Delivery Note Creation"
+  },
+  {
+   "description": "If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice without creating a Delivery Note first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Delivery Note' checkbox in the Customer master.",
+   "field": "",
+   "fieldname": "dn_required",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Is Delivery Note Required for Sales Invoice Creation?",
+   "parent_field": "",
+   "position": "Left",
+   "title": "Delivery Note Required for Sales Invoice Creation"
+  },
+  {
+   "description": "Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.",
+   "field": "",
+   "fieldname": "selling_price_list",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Default Price List",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Default Selling Price List"
+  }
+ ],
+ "title": "Selling Settings"
+}
\ No newline at end of file
diff --git a/erpnext/selling/module_onboarding/selling/selling.json b/erpnext/selling/module_onboarding/selling/selling.json
index 160208f..c02f444 100644
--- a/erpnext/selling/module_onboarding/selling/selling.json
+++ b/erpnext/selling/module_onboarding/selling/selling.json
@@ -19,32 +19,17 @@
  "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/selling",
  "idx": 0,
  "is_complete": 0,
- "modified": "2020-07-08 14:05:37.669753",
+ "modified": "2021-08-24 18:12:38.052434",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Selling",
  "owner": "Administrator",
  "steps": [
   {
-   "step": "Introduction to Selling"
-  },
-  {
-   "step": "Create a Customer"
-  },
-  {
-   "step": "Setup your Warehouse"
-  },
-  {
-   "step": "Create a Product"
-  },
-  {
-   "step": "Create a Quotation"
-  },
-  {
-   "step": "Create your first Sales Order"
-  },
-  {
    "step": "Selling Settings"
+  },
+  {
+   "step": "Sales Order"
   }
  ],
  "subtitle": "Products, Sales, Analysis, and more.",
diff --git a/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json
index 5a403b0..64defbf 100644
--- a/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json
+++ b/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json
@@ -5,7 +5,6 @@
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
  "modified": "2020-06-01 13:16:19.731719",
@@ -13,6 +12,7 @@
  "name": "Create a Customer",
  "owner": "Administrator",
  "reference_document": "Customer",
+ "show_form_tour": 0,
  "show_full_form": 0,
  "title": "Create a Customer",
  "validate_action": 1
diff --git a/erpnext/selling/onboarding_step/create_a_product/create_a_product.json b/erpnext/selling/onboarding_step/create_a_product/create_a_product.json
index d2068e1..52a5861 100644
--- a/erpnext/selling/onboarding_step/create_a_product/create_a_product.json
+++ b/erpnext/selling/onboarding_step/create_a_product/create_a_product.json
@@ -5,14 +5,14 @@
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-12 18:30:02.489949",
+ "modified": "2020-10-14 14:53:00.133574",
  "modified_by": "Administrator",
  "name": "Create a Product",
  "owner": "Administrator",
  "reference_document": "Item",
+ "show_form_tour": 0,
  "show_full_form": 0,
  "title": "Create a Product",
  "validate_action": 1
diff --git a/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json b/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json
index 27253d1..6f1837e 100644
--- a/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json
+++ b/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json
@@ -5,7 +5,6 @@
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
  "modified": "2020-06-01 13:34:58.958641",
@@ -13,6 +12,7 @@
  "name": "Create a Quotation",
  "owner": "Administrator",
  "reference_document": "Quotation",
+ "show_form_tour": 0,
  "show_full_form": 1,
  "title": "Create a Quotation",
  "validate_action": 1
diff --git a/erpnext/selling/onboarding_step/create_a_sales_order/create_a_sales_order.json b/erpnext/selling/onboarding_step/create_a_sales_order/create_a_sales_order.json
new file mode 100644
index 0000000..378f295
--- /dev/null
+++ b/erpnext/selling/onboarding_step/create_a_sales_order/create_a_sales_order.json
@@ -0,0 +1,21 @@
+{
+ "action": "Create Entry",
+ "action_label": "Create Sales Order",
+ "creation": "2021-06-29 21:22:54.204880",
+ "description": "A Sales Order is a confirmation of an order from your customer. It is also referred to as Proforma Invoice.\n\nSales Order at the heart of your sales and purchase transactions. Sales Orders are linked in Delivery Note, Sales Invoices, Material Request, and Maintenance transactions. Through Sales Order, you can track fulfillment of the overall deal towards the customer.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-06-29 21:22:54.204880",
+ "modified_by": "Administrator",
+ "name": "Create a Sales Order",
+ "owner": "Administrator",
+ "reference_document": "Sales Order",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Create a Sales Order",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json b/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json
index d21c1f4..6364533 100644
--- a/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json
+++ b/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json
@@ -5,13 +5,13 @@
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
  "modified": "2020-06-01 13:29:13.703177",
  "modified_by": "Administrator",
  "name": "Introduction to Selling",
  "owner": "Administrator",
+ "show_form_tour": 0,
  "show_full_form": 0,
  "title": "Introduction to Selling",
  "validate_action": 1,
diff --git a/erpnext/selling/onboarding_step/sales_order/sales_order.json b/erpnext/selling/onboarding_step/sales_order/sales_order.json
new file mode 100644
index 0000000..d616fae
--- /dev/null
+++ b/erpnext/selling/onboarding_step/sales_order/sales_order.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s convert your first Sales Order against a Quotation",
+ "creation": "2021-08-13 14:03:49.217866",
+ "description": "# Sales Order\n\nA Sales Order is a confirmation of an order from your customer. It is also referred to as Proforma Invoice.\n\nSales Order at the heart of your sales and purchase transactions. Sales Orders are linked in Delivery Note, Sales Invoices, Material Request, and Maintenance transactions. Through Sales Order, you can track fulfillment of the overall deal towards the customer.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-24 18:12:32.394992",
+ "modified_by": "Administrator",
+ "name": "Sales Order",
+ "owner": "Administrator",
+ "reference_document": "Sales Order",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Sales Order",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/selling/onboarding_step/selling_settings/selling_settings.json b/erpnext/selling/onboarding_step/selling_settings/selling_settings.json
index 7996d7b..ec30fd3 100644
--- a/erpnext/selling/onboarding_step/selling_settings/selling_settings.json
+++ b/erpnext/selling/onboarding_step/selling_settings/selling_settings.json
@@ -1,19 +1,21 @@
 {
  "action": "Show Form Tour",
+ "action_label": "Let\u2019s walk-through Selling Settings",
  "creation": "2020-06-01 13:01:45.615189",
+ "description": "# Selling Settings\n\nCRM and Selling module\u2019s features are configurable as per your business needs. Selling Settings is the place where you can set your preferences for:\n - Customer naming and default values\n - Billing and shipping preference in sales transactions\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 1,
  "is_skipped": 0,
- "modified": "2020-06-01 13:04:14.980743",
+ "modified": "2021-08-24 18:11:21.264335",
  "modified_by": "Administrator",
  "name": "Selling Settings",
  "owner": "Administrator",
  "reference_document": "Selling Settings",
+ "show_form_tour": 0,
  "show_full_form": 0,
- "title": "Configure Selling Settings.",
+ "title": "Selling Settings",
  "validate_action": 0
 }
\ No newline at end of file
diff --git a/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json
index 9457dee..1e20eb0 100644
--- a/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json
+++ b/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json
@@ -5,15 +5,15 @@
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-07-04 12:33:16.970031",
+ "modified": "2020-10-14 14:53:25.538900",
  "modified_by": "Administrator",
  "name": "Setup your Warehouse",
  "owner": "Administrator",
  "path": "Tree/Warehouse",
  "reference_document": "Warehouse",
+ "show_form_tour": 0,
  "show_full_form": 0,
  "title": "Set up your Warehouse",
  "validate_action": 1
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index e4ee3ec..58cb52c 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -716,6 +716,15 @@
    "options": "Account"
   },
   {
+   "fieldname": "hr_settings_section",
+   "fieldtype": "Section Break",
+   "label": "HR & Payroll Settings"
+  },
+  {
+   "fieldname": "column_break_26",
+   "fieldtype": "Column Break"
+  },
+  {
    "collapsible": 1,
    "fieldname": "fixed_asset_defaults",
    "fieldtype": "Section Break",
@@ -731,15 +740,6 @@
    "fieldname": "section_break_28",
    "fieldtype": "Section Break",
    "label": "Chart of Accounts"
-  },
-  {
-   "fieldname": "hr_settings_section",
-   "fieldtype": "Section Break",
-   "label": "HR & Payroll Settings"
-  },
-  {
-   "fieldname": "column_break_26",
-   "fieldtype": "Column Break"
   }
  ],
  "icon": "fa fa-building",
@@ -808,4 +808,4 @@
  "sort_field": "modified",
  "sort_order": "ASC",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.js b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.js
deleted file mode 100644
index 19fde2e..0000000
--- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Currency Exchange", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Currency Exchange
-		() => frappe.tests.make('Currency Exchange', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/setup/doctype/global_defaults/test_global_defaults.js b/erpnext/setup/doctype/global_defaults/test_global_defaults.js
deleted file mode 100644
index 33634eb..0000000
--- a/erpnext/setup/doctype/global_defaults/test_global_defaults.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Global Defaults", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Global Defaults
-		() => frappe.tests.make('Global Defaults', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/setup/doctype/item_group/test_item_group.js b/erpnext/setup/doctype/item_group/test_item_group.js
deleted file mode 100644
index ea322e2..0000000
--- a/erpnext/setup/doctype/item_group/test_item_group.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Item Group", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Item Group
-		() => frappe.tests.make('Item Group', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/setup/doctype/naming_series/test_naming_series.js b/erpnext/setup/doctype/naming_series/test_naming_series.js
deleted file mode 100644
index 22b664b..0000000
--- a/erpnext/setup/doctype/naming_series/test_naming_series.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Naming Series", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Naming Series
-		() => frappe.tests.make('Naming Series', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/setup/doctype/party_type/test_party_type.js b/erpnext/setup/doctype/party_type/test_party_type.js
deleted file mode 100644
index c97dbc5..0000000
--- a/erpnext/setup/doctype/party_type/test_party_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Party Type", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Party Type
-		() => frappe.tests.make('Party Type', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/setup/doctype/supplier_group/test_supplier_group.js b/erpnext/setup/doctype/supplier_group/test_supplier_group.js
deleted file mode 100644
index 976dd2c..0000000
--- a/erpnext/setup/doctype/supplier_group/test_supplier_group.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Supplier Group", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Supplier Group
-		() => frappe.tests.make('Supplier Group', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.js b/erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.js
deleted file mode 100644
index afcf74c..0000000
--- a/erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: UOM Conversion Factor", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new UOM Conversion Factor
-		() => frappe.tests.make('UOM Conversion Factor', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.js
deleted file mode 100644
index c8485e7..0000000
--- a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shopping Cart Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Shopping Cart Settings
-		() => frappe.tests.make('Shopping Cart Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.js b/erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.js
deleted file mode 100644
index 85812d6..0000000
--- a/erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Customs Tariff Number", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Customs Tariff Number
-		() => frappe.tests.make('Customs Tariff Number', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 91e7c00..b333a6b 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -430,12 +430,19 @@
 		})
 
 	def test_delivery_of_bundled_items_to_target_warehouse(self):
+		from erpnext.selling.doctype.customer.test_customer import create_internal_customer
+
 		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		customer_name = create_internal_customer(
+			customer_name="_Test Internal Customer 2",
+			represents_company="_Test Company with perpetual inventory",
+			allowed_to_interact_with="_Test Company with perpetual inventory"
+		)
 
 		set_valuation_method("_Test Item", "FIFO")
 		set_valuation_method("_Test Item Home Desktop 100", "FIFO")
 
-		target_warehouse=get_warehouse(company=company, abbr="TCP1",
+		target_warehouse = get_warehouse(company=company, abbr="TCP1",
 			warehouse_name="_Test Customer Warehouse").name
 
 		for warehouse in ("Stores - TCP1", target_warehouse):
@@ -444,10 +451,16 @@
 			create_stock_reconciliation(item_code="_Test Item Home Desktop 100", company = company,
 				expense_account = "Stock Adjustment - TCP1", warehouse=warehouse, qty=500, rate=100)
 
-		dn = create_delivery_note(item_code="_Test Product Bundle Item",
-			company='_Test Company with perpetual inventory', cost_center = 'Main - TCP1',
-			expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True, qty=5, rate=500,
-			warehouse="Stores - TCP1", target_warehouse=target_warehouse)
+		dn = create_delivery_note(
+			item_code="_Test Product Bundle Item",
+			company="_Test Company with perpetual inventory",
+			customer=customer_name,
+			cost_center = 'Main - TCP1',
+			expense_account = "Cost of Goods Sold - TCP1",
+			do_not_submit=True,
+			qty=5, rate=500,
+			warehouse="Stores - TCP1",
+			target_warehouse=target_warehouse)
 
 		dn.submit()
 
@@ -487,6 +500,9 @@
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
 
+		# tear down
+		frappe.db.rollback()
+
 	def test_closed_delivery_note(self):
 		from erpnext.stock.doctype.delivery_note.delivery_note import update_delivery_note_status
 
diff --git a/erpnext/stock/doctype/delivery_settings/test_delivery_settings.js b/erpnext/stock/doctype/delivery_settings/test_delivery_settings.js
deleted file mode 100644
index 22977c0..0000000
--- a/erpnext/stock/doctype/delivery_settings/test_delivery_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Delivery Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Delivery Settings
-		() => frappe.tests.make('Delivery Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.js b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.js
deleted file mode 100644
index b6d6d1a..0000000
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Delivery Trip", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Delivery Trip
-		() => frappe.tests.make('Delivery Trip', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index c587dd5..752a1fe 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -141,9 +141,8 @@
 	is_fixed_asset: function(frm) {
 		// set serial no to false & toggles its visibility
 		frm.set_value('has_serial_no', 0);
+		frm.set_value('has_batch_no', 0);
 		frm.toggle_enable(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset);
-		frm.toggle_reqd(['asset_category'], frm.doc.is_fixed_asset);
-		frm.toggle_display(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset);
 
 		frm.call({
 			method: "set_asset_naming_series",
@@ -793,3 +792,48 @@
 		}
 	}
 });
+
+frappe.tour['Item'] = [
+	{
+		fieldname: "item_code",
+		title: "Item Code",
+		description: __("Enter an Item Code, the name will be auto-filled the same as Item Code on clicking inside the Item Name field.")
+	},
+	{
+		fieldname: "item_group",
+		title: "Item Group",
+		description: __("Select an Item Group.")
+	},
+	{
+		fieldname: "is_stock_item",
+		title: "Maintain Stock",
+		description: __("If you are maintaining stock of this Item in your Inventory, ERPNext will make a stock ledger entry for each transaction of this item.")
+	},
+	{
+		fieldname: "include_item_in_manufacturing",
+		title: "Include Item in Manufacturing",
+		description: __("This is for raw material Items that'll be used to create finished goods. If the Item is an additional service like 'washing' that'll be used in the BOM, keep this unchecked.")
+	},
+	{
+		fieldname: "opening_stock",
+		title: "Opening Stock",
+		description: __("Enter the opening stock units.")
+	},
+	{
+		fieldname: "valuation_rate",
+		title: "Valuation Rate",
+		description: __("There are two options to maintain valuation of stock. FIFO (first in - first out) and Moving Average. To understand this topic in detail please visit <a href='https://docs.erpnext.com/docs/v13/user/manual/en/stock/articles/item-valuation-fifo-and-moving-average' target='_blank'>Item Valuation, FIFO and Moving Average.</a>")
+	},
+	{
+		fieldname: "standard_rate",
+		title: "Standard Selling Rate",
+		description: __("When creating an Item, entering a value for this field will automatically create an Item Price at the backend.")
+	},
+	{
+		fieldname: "item_defaults",
+		title: "Item Defaults",
+		description: __("In this section, you can define Company-wide transaction-related defaults for this Item. Eg. Default Warehouse, Default Price List, Supplier, etc.")
+	}
+
+
+];
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index f662bbd..db5caf9 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -204,6 +204,7 @@
   },
   {
    "default": "0",
+   "depends_on": "eval:!doc.is_fixed_asset",
    "fieldname": "is_item_from_hub",
    "fieldtype": "Check",
    "label": "Is Item from Hub",
@@ -238,6 +239,7 @@
   {
    "bold": 1,
    "default": "1",
+   "depends_on": "eval:!doc.is_fixed_asset",
    "fieldname": "is_stock_item",
    "fieldtype": "Check",
    "label": "Maintain Stock",
@@ -246,6 +248,7 @@
   },
   {
    "default": "1",
+   "depends_on": "eval:!doc.is_fixed_asset",
    "fieldname": "include_item_in_manufacturing",
    "fieldtype": "Check",
    "label": "Include Item In Manufacturing"
@@ -282,6 +285,7 @@
    "fieldname": "asset_category",
    "fieldtype": "Link",
    "label": "Asset Category",
+   "mandatory_depends_on": "is_fixed_asset",
    "options": "Asset Category"
   },
   {
@@ -434,8 +438,8 @@
   },
   {
    "collapsible": 1,
-   "collapsible_depends_on": "eval:doc.has_batch_no || doc.has_serial_no || doc.is_fixed_asset",
-   "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
+   "collapsible_depends_on": "eval:doc.has_batch_no || doc.has_serial_no",
+   "depends_on": "eval:doc.is_stock_item",
    "fieldname": "serial_nos_and_batches",
    "fieldtype": "Section Break",
    "label": "Serial Nos and Batches"
@@ -492,7 +496,7 @@
   },
   {
    "default": "0",
-   "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
+   "depends_on": "eval:doc.is_stock_item",
    "fieldname": "has_serial_no",
    "fieldtype": "Check",
    "label": "Has Serial No",
@@ -510,6 +514,7 @@
   {
    "collapsible": 1,
    "collapsible_depends_on": "attributes",
+   "depends_on": "eval:!doc.is_fixed_asset",
    "fieldname": "variants_section",
    "fieldtype": "Section Break",
    "label": "Variants"
@@ -540,6 +545,7 @@
    "options": "Item Variant Attribute"
   },
   {
+   "depends_on": "eval:!doc.is_fixed_asset",
    "fieldname": "defaults",
    "fieldtype": "Section Break",
    "label": "Sales, Purchase, Accounting Defaults"
@@ -621,6 +627,7 @@
   },
   {
    "collapsible": 1,
+   "depends_on": "eval:!doc.is_fixed_asset",
    "fieldname": "supplier_details",
    "fieldtype": "Section Break",
    "label": "Supplier Details"
@@ -668,6 +675,7 @@
   },
   {
    "collapsible": 1,
+   "default": "eval:!doc.is_fixed_asset",
    "fieldname": "sales_details",
    "fieldtype": "Section Break",
    "label": "Sales Details",
@@ -761,6 +769,7 @@
   },
   {
    "collapsible": 1,
+   "depends_on": "eval:!doc.is_fixed_asset",
    "fieldname": "customer_details",
    "fieldtype": "Section Break",
    "label": "Customer Details"
@@ -791,6 +800,7 @@
   },
   {
    "collapsible": 1,
+   "depends_on": "eval:!doc.is_fixed_asset",
    "fieldname": "inspection_criteria",
    "fieldtype": "Section Break",
    "label": "Inspection Criteria",
@@ -861,6 +871,7 @@
   },
   {
    "collapsible": 1,
+   "depends_on": "eval:!doc.is_fixed_asset",
    "fieldname": "website_section",
    "fieldtype": "Section Break",
    "label": "Website",
@@ -987,7 +998,7 @@
   },
   {
    "collapsible": 1,
-   "depends_on": "eval:(!doc.is_item_from_hub)",
+   "depends_on": "eval:(!doc.is_item_from_hub && !doc.is_fixed_asset)",
    "fieldname": "hub_publishing_sb",
    "fieldtype": "Section Break",
    "label": "Hub Publishing Details"
@@ -1021,7 +1032,7 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval:!doc.__islocal",
+   "depends_on": "eval:!doc.__islocal && !doc.is_fixed_asset",
    "fieldname": "over_delivery_receipt_allowance",
    "fieldtype": "Float",
    "label": "Over Delivery/Receipt Allowance (%)",
@@ -1029,7 +1040,7 @@
    "oldfieldtype": "Currency"
   },
   {
-   "depends_on": "eval:!doc.__islocal",
+   "depends_on": "eval:!doc.__islocal && !doc.is_fixed_asset",
    "fieldname": "over_billing_allowance",
    "fieldtype": "Float",
    "label": "Over Billing Allowance (%)"
@@ -1067,7 +1078,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "max_attachments": 1,
- "modified": "2021-07-13 01:29:06.071827",
+ "modified": "2021-08-26 12:23:07.277077",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item",
diff --git a/erpnext/stock/doctype/item/test_item.js b/erpnext/stock/doctype/item/test_item.js
deleted file mode 100644
index af44278..0000000
--- a/erpnext/stock/doctype/item/test_item.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Item", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Item
-		() => frappe.tests.make('Item', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.js b/erpnext/stock/doctype/item_alternative/test_item_alternative.js
deleted file mode 100644
index 8731849..0000000
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Item Alternative", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Item Alternative
-		() => frappe.tests.make('Item Alternative', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.js
deleted file mode 100644
index 3b3bf94..0000000
--- a/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Item Variant Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Item Variant Settings
-		() => frappe.tests.make('Item Variant Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/manufacturer/test_manufacturer.js b/erpnext/stock/doctype/manufacturer/test_manufacturer.js
deleted file mode 100644
index 0254a36..0000000
--- a/erpnext/stock/doctype/manufacturer/test_manufacturer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Manufacturer", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially('Manufacturer', [
-		// insert a new Manufacturer
-		() => frappe.tests.make([
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/material_request/test_material_request.js b/erpnext/stock/doctype/material_request/test_material_request.js
deleted file mode 100644
index 793cad0..0000000
--- a/erpnext/stock/doctype/material_request/test_material_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Material Request", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially('Material Request', [
-		// insert a new Material Request
-		() => frappe.tests.make([
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/price_list/test_price_list.js b/erpnext/stock/doctype/price_list/test_price_list.js
deleted file mode 100644
index fe4e07b..0000000
--- a/erpnext/stock/doctype/price_list/test_price_list.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Price List", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Price List
-		() => frappe.tests.make('Price List', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.js b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.js
deleted file mode 100644
index 327484e..0000000
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Inspection", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Quality Inspection
-		() => frappe.tests.make('Quality Inspection', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.js b/erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.js
deleted file mode 100644
index 879c262..0000000
--- a/erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Inspection Template", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Quality Inspection Template
-		() => frappe.tests.make('Quality Inspection Template', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.js b/erpnext/stock/doctype/serial_no/test_serial_no.js
deleted file mode 100644
index bf82932..0000000
--- a/erpnext/stock/doctype/serial_no/test_serial_no.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Serial No", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Serial No
-		() => frappe.tests.make('Serial No', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 0b4592c..760cb7d 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -272,7 +272,7 @@
 		item_wise_qty = {}
 		if self.purpose == "Manufacture" and self.work_order:
 			for d in self.items:
-				if d.is_finished_item:
+				if d.is_finished_item or d.is_process_loss:
 					item_wise_qty.setdefault(d.item_code, []).append(d.qty)
 
 		for item_code, qty_list in iteritems(item_wise_qty):
@@ -333,7 +333,7 @@
 
 			if self.purpose == "Manufacture":
 				if validate_for_manufacture:
-					if d.is_finished_item or d.is_scrap_item:
+					if d.is_finished_item or d.is_scrap_item or d.is_process_loss:
 						d.s_warehouse = None
 						if not d.t_warehouse:
 							frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
@@ -465,7 +465,7 @@
 		"""
 		# Set rate for outgoing items
 		outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate, raise_error_if_no_rate)
-		finished_item_qty = sum(d.transfer_qty for d in self.items if d.is_finished_item)
+		finished_item_qty = sum(d.transfer_qty for d in self.items if d.is_finished_item or d.is_process_loss)
 
 		# Set basic rate for incoming items
 		for d in self.get('items'):
@@ -486,6 +486,8 @@
 					raise_error_if_no_rate=raise_error_if_no_rate)
 
 			d.basic_rate = flt(d.basic_rate, d.precision("basic_rate"))
+			if d.is_process_loss:
+				d.basic_rate = flt(0.)
 			d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
 
 	def set_rate_for_outgoing_items(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
@@ -1043,6 +1045,7 @@
 
 		self.set_scrap_items()
 		self.set_actual_qty()
+		self.update_items_for_process_loss()
 		self.validate_customer_provided_item()
 		self.calculate_rate_and_amount()
 
@@ -1400,6 +1403,7 @@
 				get_default_cost_center(item_dict[d], company = self.company))
 			se_child.is_finished_item = item_dict[d].get("is_finished_item", 0)
 			se_child.is_scrap_item = item_dict[d].get("is_scrap_item", 0)
+			se_child.is_process_loss = item_dict[d].get("is_process_loss", 0)
 
 			for field in ["idx", "po_detail", "original_item",
 				"expense_account", "description", "item_name", "serial_no", "batch_no"]:
@@ -1578,6 +1582,29 @@
 			if material_request and material_request not in material_requests:
 				material_requests.append(material_request)
 				frappe.db.set_value('Material Request', material_request, 'transfer_status', status)
+				
+	def update_items_for_process_loss(self):
+		process_loss_dict = {}
+		for d in self.get("items"):
+			if not d.is_process_loss:
+				continue
+
+			scrap_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_scrap_warehouse")
+			if scrap_warehouse is not None:
+				d.t_warehouse = scrap_warehouse
+			d.is_scrap_item = 0
+
+			if d.item_code not in process_loss_dict:
+				process_loss_dict[d.item_code] = [flt(0), flt(0)]
+			process_loss_dict[d.item_code][0] += flt(d.transfer_qty)
+			process_loss_dict[d.item_code][1] += flt(d.qty)
+
+		for d in self.get("items"):
+			if not d.is_finished_item or d.item_code not in process_loss_dict:
+				continue
+			# Assumption: 1 finished item has 1 row.
+			d.transfer_qty -= process_loss_dict[d.item_code][0]
+			d.qty -= process_loss_dict[d.item_code][1]
 
 	def set_serial_no_batch_for_finished_good(self):
 		args = {}
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js
new file mode 100644
index 0000000..285ae4f
--- /dev/null
+++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js
@@ -0,0 +1,27 @@
+QUnit.module('Stock');
+
+QUnit.test("test manufacture from bom", function(assert) {
+	assert.expect(2);
+	let done = assert.async();
+	frappe.run_serially([
+		() => {
+			return frappe.tests.make("Stock Entry", [
+				{ purpose: "Manufacture" },
+				{ from_bom: 1 },
+				{ bom_no: "BOM-_Test Item - Non Whole UOM-001" },
+				{ fg_completed_qty: 2 }
+			]);
+		},
+		() => cur_frm.save(),
+		() => frappe.click_button("Update Rate and Availability"),
+		() => {
+			assert.ok(cur_frm.doc.items[1] === 0.75, " Finished Item Qty correct");
+			assert.ok(cur_frm.doc.items[2] === 0.25, " Process Loss Item Qty correct");
+		},
+		() => frappe.tests.click_button('Submit'),
+		() => frappe.tests.click_button('Yes'),
+		() => frappe.timeout(0.3),
+		() => done()
+	]);
+});
+
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index 22f412a..df65706 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -19,6 +19,7 @@
   "is_finished_item",
   "is_scrap_item",
   "quality_inspection",
+  "is_process_loss",
   "subcontracted_item",
   "section_break_8",
   "description",
@@ -543,13 +544,19 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "is_process_loss",
+   "fieldtype": "Check",
+   "label": "Is Process Loss"
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-06-21 16:03:18.834880",
+ "modified": "2021-06-22 16:47:11.268975",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index cda7c1d..24b7b9a 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -390,7 +390,7 @@
 				sl_entries = self.merge_similar_item_serial_nos(sl_entries)
 
 			sl_entries.reverse()
-			allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
+			allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
 			self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock)
 
 
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 94b006c..e438127 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -15,6 +15,7 @@
 from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.tests.utils import change_settings
 
 
 class TestStockReconciliation(unittest.TestCase):
@@ -310,6 +311,7 @@
 		pr2.cancel()
 		pr1.cancel()
 
+	@change_settings("Stock Settings", {"allow_negative_stock": 0})
 	def test_backdated_stock_reco_future_negative_stock(self):
 		"""
 			Test if a backdated stock reco causes future negative stock and is blocked.
@@ -327,8 +329,6 @@
 		warehouse = "_Test Warehouse - _TC"
 		create_item(item_code)
 
-		negative_stock_setting = frappe.db.get_single_value("Stock Settings", "allow_negative_stock")
-		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 0)
 
 		pr1 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=10, rate=100,
 			posting_date=add_days(nowdate(), -2))
@@ -348,11 +348,50 @@
 		self.assertRaises(NegativeStockError, sr3.submit)
 
 		# teardown
-		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", negative_stock_setting)
 		sr3.cancel()
 		dn2.cancel()
 		pr1.cancel()
 
+
+	@change_settings("Stock Settings", {"allow_negative_stock": 0})
+	def test_backdated_stock_reco_cancellation_future_negative_stock(self):
+		"""
+			Test if a backdated stock reco cancellation that causes future negative stock is blocked.
+			-------------------------------------------
+			Var | Doc  | Qty | Balance
+			-------------------------------------------
+			SR  | Reco | 100 | 100     (posting date: today-1) (shouldn't be cancelled after DN)
+			DN  | DN   | 100 |   0     (posting date: today)
+		"""
+		from erpnext.stock.stock_ledger import NegativeStockError
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+		frappe.db.commit()
+
+		item_code = "Backdated-Reco-Cancellation-Item"
+		warehouse = "_Test Warehouse - _TC"
+		create_item(item_code)
+
+
+		sr = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=100, rate=100,
+			posting_date=add_days(nowdate(), -1))
+
+		dn = create_delivery_note(item_code=item_code, warehouse=warehouse, qty=100, rate=120,
+			posting_date=nowdate())
+
+		dn_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": dn.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(dn_balance, 0)
+
+		# check if cancellation of stock reco is blocked
+		self.assertRaises(NegativeStockError, sr.cancel)
+
+		repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name}))
+		self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation")
+
+		# teardown
+		frappe.db.rollback()
+
+
 	def test_valid_batch(self):
 		create_batch_item_with_batch("Testing Batch Item 1", "001")
 		create_batch_item_with_batch("Testing Batch Item 2", "002")
diff --git a/erpnext/stock/doctype/stock_settings/test_stock_settings.js b/erpnext/stock/doctype/stock_settings/test_stock_settings.js
deleted file mode 100644
index 57d9fc6..0000000
--- a/erpnext/stock/doctype/stock_settings/test_stock_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Stock Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Stock Settings
-		() => frappe.tests.make('Stock Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/uom_category/test_uom_category.js b/erpnext/stock/doctype/uom_category/test_uom_category.js
deleted file mode 100644
index 4b5972e..0000000
--- a/erpnext/stock/doctype/uom_category/test_uom_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: UOM Category", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new UOM Category
-		() => frappe.tests.make('UOM Category', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/doctype/variant_field/test_variant_field.js b/erpnext/stock/doctype/variant_field/test_variant_field.js
deleted file mode 100644
index 2600a10..0000000
--- a/erpnext/stock/doctype/variant_field/test_variant_field.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Variant Field", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Variant Field
-		() => frappe.tests.make('Variant Field', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/stock/form_tour/item/item.json b/erpnext/stock/form_tour/item/item.json
new file mode 100644
index 0000000..821e91b
--- /dev/null
+++ b/erpnext/stock/form_tour/item/item.json
@@ -0,0 +1,89 @@
+{
+ "creation": "2021-08-24 17:56:40.754909",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 18:04:50.928431",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Item",
+ "owner": "Administrator",
+ "reference_doctype": "Item",
+ "save_on_complete": 0,
+ "steps": [
+  {
+   "description": "Enter code for Asset Item",
+   "field": "",
+   "fieldname": "item_code",
+   "fieldtype": "Data",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Item Code",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Asset Item Code"
+  },
+  {
+   "description": "Enter name for Asset Item",
+   "field": "",
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Item Name",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Asset Item Name"
+  },
+  {
+   "description": "Check this field to make this an Asset Item",
+   "field": "",
+   "fieldname": "is_fixed_asset",
+   "fieldtype": "Check",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Is Fixed Asset",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Is this a Fixed Asset?"
+  },
+  {
+   "description": "On checking it, the system will create an Asset automatically on purchase",
+   "field": "",
+   "fieldname": "auto_create_assets",
+   "fieldtype": "Check",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Auto Create Assets on Purchase",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Auto Create Asset on Purchase"
+  },
+  {
+   "description": "Select an Asset Category for this Asset Item",
+   "field": "",
+   "fieldname": "asset_category",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Asset Category",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Asset Category"
+  },
+  {
+   "description": "Select a naming series which will be used to create an Asset automatically",
+   "field": "",
+   "fieldname": "asset_naming_series",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Asset Naming Series",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Asset Naming Series"
+  }
+ ],
+ "title": "Item"
+}
diff --git a/erpnext/stock/form_tour/purchase_receipt/purchase_receipt.json b/erpnext/stock/form_tour/purchase_receipt/purchase_receipt.json
new file mode 100644
index 0000000..6fba3f4
--- /dev/null
+++ b/erpnext/stock/form_tour/purchase_receipt/purchase_receipt.json
@@ -0,0 +1,41 @@
+{
+ "creation": "2021-08-24 13:03:21.333088",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 13:03:21.333088",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Purchase Receipt",
+ "owner": "Administrator",
+ "reference_doctype": "Purchase Receipt",
+ "save_on_complete": 0,
+ "steps": [
+  {
+   "description": "Select Asset Supplier",
+   "field": "",
+   "fieldname": "supplier",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Supplier",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Supplier"
+  },
+  {
+   "description": "Select an Asset Item, Enter rate and quantity",
+   "field": "",
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Items",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Items"
+  }
+ ],
+ "title": "Purchase Receipt"
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/stock/report/process_loss_report/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/stock/report/process_loss_report/__init__.py
diff --git a/erpnext/stock/report/process_loss_report/process_loss_report.js b/erpnext/stock/report/process_loss_report/process_loss_report.js
new file mode 100644
index 0000000..b0c2b94
--- /dev/null
+++ b/erpnext/stock/report/process_loss_report/process_loss_report.js
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Process Loss Report"] = {
+	filters: [
+    {
+      label: __("Company"),
+      fieldname: "company",
+      fieldtype: "Link",
+      options: "Company",
+      mandatory: true,
+      default: frappe.defaults.get_user_default("Company"),
+    },
+		{
+      label: __("Item"),
+      fieldname: "item",
+      fieldtype: "Link",
+      options: "Item",
+      mandatory: false,
+		},
+		{
+      label: __("Work Order"),
+      fieldname: "work_order",
+      fieldtype: "Link",
+      options: "Work Order",
+      mandatory: false,
+		},
+    {
+      label: __("From Date"),
+      fieldname: "from_date",
+      fieldtype: "Date",
+      mandatory: true,
+      default: frappe.datetime.year_start(),
+    },
+    {
+      label: __("To Date"),
+      fieldname: "to_date",
+      fieldtype: "Date",
+      mandatory: true,
+      default: frappe.datetime.get_today(),
+    },
+	]
+};
diff --git a/erpnext/stock/report/process_loss_report/process_loss_report.json b/erpnext/stock/report/process_loss_report/process_loss_report.json
new file mode 100644
index 0000000..afe4aff
--- /dev/null
+++ b/erpnext/stock/report/process_loss_report/process_loss_report.json
@@ -0,0 +1,29 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-08-24 16:38:15.233395",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-08-24 16:38:15.233395",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Process Loss Report",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Work Order",
+ "report_name": "Process Loss Report",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Manufacturing User"
+  },
+  {
+   "role": "Stock User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/process_loss_report/process_loss_report.py b/erpnext/stock/report/process_loss_report/process_loss_report.py
new file mode 100644
index 0000000..7494328
--- /dev/null
+++ b/erpnext/stock/report/process_loss_report/process_loss_report.py
@@ -0,0 +1,132 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from typing import Dict, List, Tuple
+
+Filters = frappe._dict
+Row = frappe._dict
+Data = List[Row]
+Columns = List[Dict[str, str]]
+QueryArgs = Dict[str, str]
+
+def execute(filters: Filters) -> Tuple[Columns, Data]:
+	columns = get_columns()
+	data = get_data(filters)
+	return columns, data
+
+def get_data(filters: Filters) -> Data:
+	query_args = get_query_args(filters)
+	data = run_query(query_args)
+	update_data_with_total_pl_value(data)
+	return data
+
+def get_columns() -> Columns:
+	return [
+		{
+			'label': 'Work Order',
+			'fieldname': 'name',
+			'fieldtype': 'Link',
+			'options': 'Work Order',
+			'width': '200'
+		},
+		{
+			'label': 'Item',
+			'fieldname': 'production_item',
+			'fieldtype': 'Link',
+			'options': 'Item',
+			'width': '100'
+		},
+		{
+			'label': 'Status',
+			'fieldname': 'status',
+			'fieldtype': 'Data',
+			'width': '100'
+		},
+		{
+			'label': 'Manufactured Qty',
+			'fieldname': 'produced_qty',
+			'fieldtype': 'Float',
+			'width': '150'
+		},
+		{
+			'label': 'Loss Qty',
+			'fieldname': 'process_loss_qty',
+			'fieldtype': 'Float',
+			'width': '150'
+		},
+		{
+			'label': 'Actual Manufactured Qty',
+			'fieldname': 'actual_produced_qty',
+			'fieldtype': 'Float',
+			'width': '150'
+		},
+		{
+			'label': 'Loss Value',
+			'fieldname': 'total_pl_value',
+			'fieldtype': 'Float',
+			'width': '150'
+		},
+		{
+			'label': 'FG Value',
+			'fieldname': 'total_fg_value',
+			'fieldtype': 'Float',
+			'width': '150'
+		},
+		{
+			'label': 'Raw Material Value',
+			'fieldname': 'total_rm_value',
+			'fieldtype': 'Float',
+			'width': '150'
+		}
+	]
+
+def get_query_args(filters: Filters) -> QueryArgs:
+	query_args = {}
+	query_args.update(filters)
+	query_args.update(
+		get_filter_conditions(filters)
+	)
+	return query_args
+
+def run_query(query_args: QueryArgs) -> Data:
+	return frappe.db.sql("""
+		SELECT 
+			wo.name, wo.status, wo.production_item, wo.qty,
+			wo.produced_qty, wo.process_loss_qty,
+			(wo.produced_qty - wo.process_loss_qty) as actual_produced_qty,
+			sum(se.total_incoming_value) as total_fg_value,
+			sum(se.total_outgoing_value) as total_rm_value
+		FROM
+			`tabWork Order` wo INNER JOIN `tabStock Entry` se
+			ON wo.name=se.work_order
+		WHERE
+			process_loss_qty > 0
+			AND wo.company = %(company)s
+			AND se.docstatus = 1
+			AND se.posting_date BETWEEN %(from_date)s AND %(to_date)s
+			{item_filter}
+			{work_order_filter}
+		GROUP BY
+			se.work_order
+	""".format(**query_args), query_args, as_dict=1, debug=1)
+
+def update_data_with_total_pl_value(data: Data) -> None:
+	for row in data:
+		value_per_unit_fg = row['total_fg_value'] / row['actual_produced_qty']
+		row['total_pl_value'] = row['process_loss_qty'] * value_per_unit_fg
+
+def get_filter_conditions(filters: Filters) -> QueryArgs:
+	filter_conditions = dict(item_filter="", work_order_filter="")
+	if "item" in filters:
+		production_item = filters.get("item")
+		filter_conditions.update(
+			{"item_filter": f"AND wo.production_item='{production_item}'"}
+		)
+	if "work_order" in filters:
+		work_order_name = filters.get("work_order")
+		filter_conditions.update(
+			{"work_order_filter": f"AND wo.name='{work_order_name}'"}
+		)
+	return filter_conditions
+
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index eddd048..48fd7d3 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -332,6 +332,7 @@
 			where
 				item_code = %(item_code)s
 				and warehouse = %(warehouse)s
+				and is_cancelled = 0
 				and timestamp(posting_date, time_format(posting_time, %(time_format)s)) = timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
 
 			order by
@@ -954,7 +955,7 @@
 
 	return valuation_rate
 
-def update_qty_in_future_sle(args, allow_negative_stock=None):
+def update_qty_in_future_sle(args, allow_negative_stock=False):
 	"""Recalculate Qty after Transaction in future SLEs based on current SLE."""
 	datetime_limit_condition = ""
 	qty_shift = args.actual_qty
@@ -1043,8 +1044,8 @@
 			)
 		)"""
 
-def validate_negative_qty_in_future_sle(args, allow_negative_stock=None):
-	allow_negative_stock = allow_negative_stock \
+def validate_negative_qty_in_future_sle(args, allow_negative_stock=False):
+	allow_negative_stock = cint(allow_negative_stock) \
 		or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
 
 	if (args.actual_qty < 0 or args.voucher_type == "Stock Reconciliation") and not allow_negative_stock:
diff --git a/erpnext/support/doctype/support_settings/test_support_settings.js b/erpnext/support/doctype/support_settings/test_support_settings.js
deleted file mode 100644
index 0787306..0000000
--- a/erpnext/support/doctype/support_settings/test_support_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Support Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Support Settings
-		() => frappe.tests.make('Support Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});