Merge branch 'develop' into ordered-qty-for-packed-items
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 9e2cdff..ab1061b 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -120,6 +120,7 @@
 	prev_gl_entry = frappe.db.sql('''
 		select name, posting_date from `tabGL Entry` where company=%s and account=%s and
 		voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
+		and is_cancelled = 0
 		order by posting_date desc limit 1
 	''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
 
@@ -227,6 +228,7 @@
 	gl_entries_details = frappe.db.sql('''
 		select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
 		from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
+		and is_cancelled = 0
 		group by voucher_detail_no
 	'''.format(total_credit_debit, total_credit_debit_currency),
 		(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
@@ -282,7 +284,7 @@
 			return
 
 		# check if books nor frozen till endate:
-		if getdate(end_date) >= getdate(accounts_frozen_upto):
+		if accounts_frozen_upto and (end_date) <= getdate(accounts_frozen_upto):
 			end_date = get_last_day(add_days(accounts_frozen_upto, 1))
 
 		if via_journal_entry:
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
index 3eaf6a2..77d54a6 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
@@ -2,6 +2,7 @@
 # See license.txt
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import (
 	create_dimension,
@@ -10,11 +11,10 @@
 from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import (
 	get_temporary_opening_account,
 )
-from erpnext.tests.utils import ERPNextTestCase
 
 test_dependencies = ["Customer", "Supplier", "Accounting Dimension"]
 
-class TestOpeningInvoiceCreationTool(ERPNextTestCase):
+class TestOpeningInvoiceCreationTool(FrappeTestCase):
 	@classmethod
 	def setUpClass(self):
 		if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index a9bc028..cc32a6c 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -194,8 +194,14 @@
 			frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
 
 		frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
-		frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
-			(frm.doc.paid_from_account_currency != company_currency));
+
+		if (frm.doc.payment_type == "Pay") {
+			frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
+				(frm.doc.paid_to_account_currency != company_currency));
+		} else {
+			frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
+				(frm.doc.paid_from_account_currency != company_currency));
+		}
 
 		frm.toggle_display("base_received_amount", (
 			frm.doc.paid_to_account_currency != company_currency
@@ -230,7 +236,8 @@
 		var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: "";
 
 		frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount",
-			"difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax"], company_currency);
+			"difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax",
+			"base_total_taxes_and_charges"], company_currency);
 
 		frm.set_currency_labels(["paid_amount"], frm.doc.paid_from_account_currency);
 		frm.set_currency_labels(["received_amount"], frm.doc.paid_to_account_currency);
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index c8d1db9..3fc1adf 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -66,7 +66,9 @@
   "tax_withholding_category",
   "section_break_56",
   "taxes",
+  "section_break_60",
   "base_total_taxes_and_charges",
+  "column_break_61",
   "total_taxes_and_charges",
   "deductions_or_loss_section",
   "deductions",
@@ -715,12 +717,21 @@
    "fieldtype": "Data",
    "hidden": 1,
    "label": "Paid To Account Type"
+  },
+  {
+   "fieldname": "column_break_61",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_60",
+   "fieldtype": "Section Break",
+   "hide_border": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-11-24 18:58:24.919764",
+ "modified": "2022-02-23 20:08:39.559814",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Entry",
@@ -763,6 +774,7 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "title",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index c36c843..f9f3350 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -934,8 +934,12 @@
 
 			tax.base_total = tax.total * self.source_exchange_rate
 
-			self.total_taxes_and_charges += current_tax_amount
-			self.base_total_taxes_and_charges += current_tax_amount * self.source_exchange_rate
+			if self.payment_type == 'Pay':
+				self.base_total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
+				self.total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
+			else:
+				self.base_total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
+				self.total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
 
 		if self.get('taxes'):
 			self.paid_amount_after_tax = self.get('taxes')[-1].base_total
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index cc3528e..349b8bb 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -633,6 +633,45 @@
 		self.assertEqual(flt(expected_party_balance), party_balance)
 		self.assertEqual(flt(expected_party_account_balance), party_account_balance)
 
+	def test_multi_currency_payment_entry_with_taxes(self):
+		payment_entry = create_payment_entry(party='_Test Supplier USD', paid_to = '_Test Payable USD - _TC',
+			save=True)
+		payment_entry.append('taxes', {
+			'account_head': '_Test Account Service Tax - _TC',
+			'charge_type': 'Actual',
+			'tax_amount': 10,
+			'add_deduct_tax': 'Add',
+			'description': 'Test'
+		})
+
+		payment_entry.save()
+		self.assertEqual(payment_entry.base_total_taxes_and_charges, 10)
+		self.assertEqual(flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2))
+
+def create_payment_entry(**args):
+	payment_entry = frappe.new_doc('Payment Entry')
+	payment_entry.company = args.get('company') or '_Test Company'
+	payment_entry.payment_type = args.get('payment_type') or 'Pay'
+	payment_entry.party_type = args.get('party_type') or 'Supplier'
+	payment_entry.party = args.get('party') or '_Test Supplier'
+	payment_entry.paid_from = args.get('paid_from') or '_Test Bank - _TC'
+	payment_entry.paid_to = args.get('paid_to') or 'Creditors - _TC'
+	payment_entry.paid_amount = args.get('paid_amount') or 1000
+
+	payment_entry.setup_party_account_field()
+	payment_entry.set_missing_values()
+	payment_entry.set_exchange_rate()
+	payment_entry.received_amount = payment_entry.paid_amount / payment_entry.target_exchange_rate
+	payment_entry.reference_no = 'Test001'
+	payment_entry.reference_date = nowdate()
+
+	if args.get('save'):
+		payment_entry.save()
+		if args.get('submit'):
+			payment_entry.submit()
+
+	return payment_entry
+
 def create_payment_terms_template():
 
 	create_payment_term('Basic Amount Receivable')
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index 09aa723..1b34d6d 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -73,7 +73,7 @@
 			'to_date': doc.to_date,
 			'company': doc.company,
 			'finance_book': doc.finance_book if doc.finance_book else None,
-			'account': doc.account if doc.account else None,
+			'account': [doc.account] if doc.account else None,
 			'party_type': 'Customer',
 			'party': [entry.customer],
 			'presentation_currency': presentation_currency,
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 941061f..6d929e4 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1595,6 +1595,56 @@
 			self.assertEqual(expected_values[gle.account][1], gle.debit)
 			self.assertEqual(expected_values[gle.account][2], gle.credit)
 
+	def test_rounding_adjustment_3(self):
+		si = create_sales_invoice(do_not_save=True)
+		si.items = []
+		for d in [(1122, 2), (1122.01, 1), (1122.01, 1)]:
+			si.append("items", {
+				"item_code": "_Test Item",
+				"gst_hsn_code": "999800",
+				"warehouse": "_Test Warehouse - _TC",
+				"qty": d[1],
+				"rate": d[0],
+				"income_account": "Sales - _TC",
+				"cost_center": "_Test Cost Center - _TC"
+			})
+		for tax_account in ["_Test Account VAT - _TC", "_Test Account Service Tax - _TC"]:
+			si.append("taxes", {
+				"charge_type": "On Net Total",
+				"account_head": tax_account,
+				"description": tax_account,
+				"rate": 6,
+				"cost_center": "_Test Cost Center - _TC",
+				"included_in_print_rate": 1
+			})
+		si.save()
+		si.submit()
+		self.assertEqual(si.net_total, 4007.16)
+		self.assertEqual(si.grand_total, 4488.02)
+		self.assertEqual(si.total_taxes_and_charges, 480.86)
+		self.assertEqual(si.rounding_adjustment, -0.02)
+
+		expected_values = dict((d[0], d) for d in [
+			[si.debit_to, 4488.0, 0.0],
+			["_Test Account Service Tax - _TC", 0.0, 240.43],
+			["_Test Account VAT - _TC", 0.0, 240.43],
+			["Sales - _TC", 0.0, 4007.15],
+			["Round Off - _TC", 0.01, 0]
+		])
+
+		gl_entries = frappe.db.sql("""select account, debit, credit
+			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
+			order by account asc""", si.name, as_dict=1)
+
+		debit_credit_diff = 0
+		for gle in gl_entries:
+			self.assertEqual(expected_values[gle.account][0], gle.account)
+			self.assertEqual(expected_values[gle.account][1], gle.debit)
+			self.assertEqual(expected_values[gle.account][2], gle.credit)
+			debit_credit_diff += (gle.debit - gle.credit)
+
+		self.assertEqual(debit_credit_diff, 0)
+
 	def test_sales_invoice_with_shipping_rule(self):
 		from erpnext.accounts.doctype.shipping_rule.test_shipping_rule import create_shipping_rule
 
@@ -2429,14 +2479,22 @@
 
 
 	def test_sales_commission(self):
-		si = frappe.copy_doc(test_records[0])
+		si = frappe.copy_doc(test_records[2])
+
+		frappe.db.set_value('Item', si.get('items')[0].item_code, 'grant_commission', 1)
+		frappe.db.set_value('Item', si.get('items')[1].item_code, 'grant_commission', 0)
+
 		item = copy.deepcopy(si.get('items')[0])
 		item.update({
 			"qty": 1,
 			"rate": 500,
-			"grant_commission": 1
 		})
-		si.append("items", item)
+
+		item = copy.deepcopy(si.get('items')[1])
+		item.update({
+			"qty": 1,
+			"rate": 500,
+		})
 
 		# Test valid values
 		for commission_rate, total_commission in ((0, 0), (10, 50), (100, 500)):
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 ae9ac35..2901cf0 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -832,6 +832,7 @@
   },
   {
    "default": "0",
+   "fetch_from": "item_code.grant_commission",
    "fieldname": "grant_commission",
    "fieldtype": "Check",
    "label": "Grant Commission",
@@ -841,7 +842,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-10-05 12:24:54.968907",
+ "modified": "2022-02-24 14:41:36.392560",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Item",
@@ -849,5 +850,6 @@
  "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
index 1d30934..8043a1b 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
@@ -55,5 +55,8 @@
 		frappe.throw(_("Disabled template must not be default template"))
 
 def validate_for_tax_category(doc):
+	if not doc.tax_category:
+		return
+
 	if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0, "name": ["!=", doc.name]}):
 		frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index d24d56b..0cd5e86 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -274,7 +274,7 @@
 				debit_credit_diff += flt(d.credit)
 			round_off_account_exists = True
 
-	if round_off_account_exists and abs(debit_credit_diff) <= (1.0 / (10**precision)):
+	if round_off_account_exists and abs(debit_credit_diff) < (1.0 / (10**precision)):
 		gl_map.remove(round_off_gle)
 		return
 
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 645e97e..efa2ab1 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -3,9 +3,9 @@
 
 
 import json
-import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, flt, getdate, nowdate
 
 from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
@@ -27,7 +27,7 @@
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
 
-class TestPurchaseOrder(unittest.TestCase):
+class TestPurchaseOrder(FrappeTestCase):
 	def test_make_purchase_receipt(self):
 		po = create_purchase_order(do_not_submit=True)
 		self.assertRaises(frappe.ValidationError, make_purchase_receipt, po.name)
diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
index 5190199..5b21124 100644
--- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
@@ -1,9 +1,9 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import nowdate
 
 from erpnext.buying.doctype.request_for_quotation.request_for_quotation import (
@@ -16,7 +16,7 @@
 from erpnext.templates.pages.rfq import check_supplier_has_docname_access
 
 
-class TestRequestforQuotation(unittest.TestCase):
+class TestRequestforQuotation(FrappeTestCase):
 	def test_quote_status(self):
 		rfq = make_request_for_quotation()
 
diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py
index 0fb81b2..7358e2a 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.py
+++ b/erpnext/buying/doctype/supplier/test_supplier.py
@@ -1,7 +1,6 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
-import unittest
 
 import frappe
 from frappe.test_runner import make_test_records
@@ -12,8 +11,10 @@
 test_dependencies = ['Payment Term', 'Payment Terms Template']
 test_records = frappe.get_test_records('Supplier')
 
+from frappe.tests.utils import FrappeTestCase
 
-class TestSupplier(unittest.TestCase):
+
+class TestSupplier(FrappeTestCase):
 	def test_get_supplier_group_details(self):
 		doc = frappe.new_doc("Supplier Group")
 		doc.supplier_group_name = "_Testing Supplier Group"
diff --git a/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
index d48ac7e..a4d4597 100644
--- a/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
@@ -3,12 +3,12 @@
 
 
 
-import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 
-class TestPurchaseOrder(unittest.TestCase):
+class TestPurchaseOrder(FrappeTestCase):
 	def test_make_purchase_order(self):
 		from erpnext.buying.doctype.supplier_quotation.supplier_quotation import make_purchase_order
 
diff --git a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
index 7908c35..8ecc2cd 100644
--- a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
@@ -1,12 +1,12 @@
 # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 
-class TestSupplierScorecard(unittest.TestCase):
+class TestSupplierScorecard(FrappeTestCase):
 
 	def test_create_scorecard(self):
 		doc = make_supplier_scorecard().insert()
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py b/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py
index dacc982..7ff84c1 100644
--- a/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py
@@ -1,12 +1,12 @@
 # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 
-class TestSupplierScorecardCriteria(unittest.TestCase):
+class TestSupplierScorecardCriteria(FrappeTestCase):
 	def test_variables_exist(self):
 		delete_test_scorecards()
 		for d in test_good_criteria:
diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py
index 4d75981..32005a3 100644
--- a/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py
@@ -1,16 +1,16 @@
 # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable import (
 	VariablePathNotFound,
 )
 
 
-class TestSupplierScorecardVariable(unittest.TestCase):
+class TestSupplierScorecardVariable(FrappeTestCase):
 	def test_variable_exist(self):
 		for d in test_existing_variables:
 			my_doc = frappe.get_doc("Supplier Scorecard Variable", d.get("name"))
diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
index 84de8c6..4452452 100644
--- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
@@ -2,10 +2,10 @@
 # For license information, please see license.txt
 
 
-import unittest
 from datetime import datetime
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
 from erpnext.buying.report.procurement_tracker.procurement_tracker import execute
@@ -14,7 +14,7 @@
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 
 
-class TestProcurementTracker(unittest.TestCase):
+class TestProcurementTracker(FrappeTestCase):
 	def test_result_for_procurement_tracker(self):
 		filters = {
 			'company': '_Test Procurement Company',
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
index 144523a..c2b38d3 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
@@ -3,9 +3,9 @@
 # Compiled at: 2019-05-06 09:51:46
 # Decompiled by https://python-decompiler.com
 
-import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
 from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
@@ -15,7 +15,7 @@
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
 
-class TestSubcontractedItemToBeReceived(unittest.TestCase):
+class TestSubcontractedItemToBeReceived(FrappeTestCase):
 
 	def test_pending_and_received_qty(self):
 		po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes')
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
index 3c203ac..fc9acab 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
@@ -4,9 +4,9 @@
 # Decompiled by https://python-decompiler.com
 
 import json
-import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry
 from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
@@ -16,7 +16,7 @@
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
 
-class TestSubcontractedItemToBeTransferred(unittest.TestCase):
+class TestSubcontractedItemToBeTransferred(FrappeTestCase):
 
 	def test_pending_and_transferred_qty(self):
 		po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes', supplier_warehouse="_Test Warehouse 1 - _TC")
diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py
index 3addb91..c52c688 100644
--- a/erpnext/controllers/subcontracting.py
+++ b/erpnext/controllers/subcontracting.py
@@ -363,8 +363,6 @@
 			return
 
 		for row in self.get(self.raw_material_table):
-			self.__validate_consumed_qty(row)
-
 			key = (row.rm_item_code, row.main_item_code, row.purchase_order)
 			if not self.__transferred_items or not self.__transferred_items.get(key):
 				return
@@ -372,12 +370,6 @@
 			self.__validate_batch_no(row, key)
 			self.__validate_serial_no(row, key)
 
-	def __validate_consumed_qty(self, row):
-		if self.backflush_based_on != 'BOM' and flt(row.consumed_qty) == 0.0:
-			msg = f'Row {row.idx}: the consumed qty cannot be zero for the item {frappe.bold(row.rm_item_code)}'
-
-			frappe.throw(_(msg),title=_('Consumed Items Qty Check'))
-
 	def __validate_batch_no(self, row, key):
 		if row.get('batch_no') and row.get('batch_no') not in self.__transferred_items.get(key).get('batch_no'):
 			link = get_link_to_form('Purchase Order', row.purchase_order)
diff --git a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
index 37b3672..9c389d0 100644
--- a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
+++ b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
@@ -5,6 +5,7 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import change_settings
 from frappe.utils import add_months, nowdate
 
 from erpnext.accounts.doctype.tax_rule.tax_rule import ConflictingTaxRule
@@ -15,7 +16,7 @@
 	get_party,
 	update_cart,
 )
-from erpnext.tests.utils import change_settings, create_test_contact_and_address
+from erpnext.tests.utils import create_test_contact_and_address
 
 # test_dependencies = ['Payment Terms Template']
 
diff --git a/erpnext/e_commerce/variant_selector/test_variant_selector.py b/erpnext/e_commerce/variant_selector/test_variant_selector.py
index 4d907c6..ee098e1 100644
--- a/erpnext/e_commerce/variant_selector/test_variant_selector.py
+++ b/erpnext/e_commerce/variant_selector/test_variant_selector.py
@@ -1,4 +1,5 @@
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.controllers.item_variant import create_variant
 from erpnext.e_commerce.doctype.e_commerce_settings.test_e_commerce_settings import (
@@ -7,11 +8,10 @@
 from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
 from erpnext.e_commerce.variant_selector.utils import get_next_attribute_and_values
 from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.tests.utils import ERPNextTestCase
 
 test_dependencies = ["Item"]
 
-class TestVariantSelector(ERPNextTestCase):
+class TestVariantSelector(FrappeTestCase):
 
 	@classmethod
 	def setUpClass(cls):
@@ -116,4 +116,4 @@
 		self.assertEqual(next_values["exact_match"][0],"Test-Tshirt-Temp-S-R")
 		self.assertEqual(next_values["exact_match"][0],"Test-Tshirt-Temp-S-R")
 		self.assertEqual(price_info["price_list_rate"], 100.0)
-		self.assertEqual(price_info["formatted_price_sales_uom"], "₹ 100.00")
\ No newline at end of file
+		self.assertEqual(price_info["formatted_price_sales_uom"], "₹ 100.00")
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json
index 0475453..b050183 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.json
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.json
@@ -2,7 +2,7 @@
  "actions": [],
  "allow_import": 1,
  "autoname": "naming_series:",
- "creation": "2017-10-09 14:26:29.612365",
+ "creation": "2022-01-17 18:36:51.450395",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
@@ -121,7 +121,7 @@
    "fieldtype": "Select",
    "label": "Status",
    "no_copy": 1,
-   "options": "Draft\nPaid\nUnpaid\nClaimed\nCancelled",
+   "options": "Draft\nPaid\nUnpaid\nClaimed\nReturned\nPartly Claimed and Returned\nCancelled",
    "read_only": 1
   },
   {
@@ -200,7 +200,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-09-11 18:38:38.617478",
+ "modified": "2022-01-17 19:33:52.345823",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee Advance",
@@ -237,5 +237,41 @@
  "search_fields": "employee,employee_name",
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [
+  {
+   "color": "Red",
+   "custom": 1,
+   "title": "Draft"
+  },
+  {
+   "color": "Green",
+   "custom": 1,
+   "title": "Paid"
+  },
+  {
+   "color": "Orange",
+   "custom": 1,
+   "title": "Unpaid"
+  },
+  {
+   "color": "Blue",
+   "custom": 1,
+   "title": "Claimed"
+  },
+  {
+   "color": "Gray",
+   "title": "Returned"
+  },
+  {
+   "color": "Yellow",
+   "title": "Partly Claimed and Returned"
+  },
+  {
+   "color": "Red",
+   "custom": 1,
+   "title": "Cancelled"
+  }
+ ],
+ "title_field": "employee_name",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py
index 7aac2b6..79d389d 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.py
@@ -27,19 +27,33 @@
 
 	def on_cancel(self):
 		self.ignore_linked_doctypes = ('GL Entry')
+		self.set_status(update=True)
 
-	def set_status(self):
+	def set_status(self, update=False):
+		precision = self.precision("paid_amount")
+		total_amount = flt(flt(self.claimed_amount) + flt(self.return_amount), precision)
+		status = None
+
 		if self.docstatus == 0:
-			self.status = "Draft"
-		if self.docstatus == 1:
-			if self.claimed_amount and flt(self.claimed_amount) == flt(self.paid_amount):
-				self.status = "Claimed"
-			elif self.paid_amount and self.advance_amount == flt(self.paid_amount):
-				self.status = "Paid"
+			status = "Draft"
+		elif self.docstatus == 1:
+			if flt(self.claimed_amount) > 0 and flt(self.claimed_amount, precision) == flt(self.paid_amount, precision):
+				status = "Claimed"
+			elif flt(self.return_amount) > 0 and flt(self.return_amount, precision) == flt(self.paid_amount, precision):
+				status = "Returned"
+			elif flt(self.claimed_amount) > 0 and (flt(self.return_amount) > 0) and total_amount == flt(self.paid_amount, precision):
+				status = "Partly Claimed and Returned"
+			elif flt(self.paid_amount) > 0 and flt(self.advance_amount, precision) == flt(self.paid_amount, precision):
+				status = "Paid"
 			else:
-				self.status = "Unpaid"
+				status = "Unpaid"
 		elif self.docstatus == 2:
-			self.status = "Cancelled"
+			status = "Cancelled"
+
+		if update:
+			self.db_set("status", status)
+		else:
+			self.status = status
 
 	def set_total_advance_paid(self):
 		gle = frappe.qb.DocType("GL Entry")
@@ -85,9 +99,7 @@
 
 		self.db_set("paid_amount", paid_amount)
 		self.db_set("return_amount", return_amount)
-		self.set_status()
-		frappe.db.set_value("Employee Advance", self.name , "status", self.status)
-
+		self.set_status(update=True)
 
 	def update_claimed_amount(self):
 		claimed_amount = frappe.db.sql("""
@@ -103,8 +115,8 @@
 
 		frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
 		self.reload()
-		self.set_status()
-		frappe.db.set_value("Employee Advance", self.name, "status", self.status)
+		self.set_status(update=True)
+
 
 @frappe.whitelist()
 def get_pending_amount(employee, posting_date):
@@ -222,7 +234,8 @@
 		'reference_name': employee_advance_name,
 		'party_type': 'Employee',
 		'party': employee,
-		'is_advance': 'Yes'
+		'is_advance': 'Yes',
+		'cost_center': erpnext.get_default_cost_center(company)
 	})
 
 	bank_amount = flt(return_amount) if bank_cash_account.account_currency==currency \
@@ -233,7 +246,8 @@
 		"debit_in_account_currency": bank_amount,
 		"account_currency": bank_cash_account.account_currency,
 		"account_type": bank_cash_account.account_type,
-		"exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1
+		"exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1,
+		"cost_center": erpnext.get_default_cost_center(company)
 	})
 
 	return je.as_dict()
diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.py b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
index 5f2e720..e3c1487 100644
--- a/erpnext/hr/doctype/employee_advance/test_employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
@@ -4,7 +4,7 @@
 import unittest
 
 import frappe
-from frappe.utils import nowdate
+from frappe.utils import flt, nowdate
 
 import erpnext
 from erpnext.hr.doctype.employee.test_employee import make_employee
@@ -12,12 +12,21 @@
 	EmployeeAdvanceOverPayment,
 	create_return_through_additional_salary,
 	make_bank_entry,
+	make_return_entry,
+)
+from erpnext.hr.doctype.expense_claim.expense_claim import get_advances
+from erpnext.hr.doctype.expense_claim.test_expense_claim import (
+	get_payable_account,
+	make_expense_claim,
 )
 from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component
 from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
 
 
 class TestEmployeeAdvance(unittest.TestCase):
+	def setUp(self):
+		frappe.db.delete("Employee Advance")
+
 	def test_paid_amount_and_status(self):
 		employee_name = make_employee("_T@employe.advance")
 		advance = make_employee_advance(employee_name)
@@ -52,9 +61,102 @@
 		self.assertEqual(advance.paid_amount, 0)
 		self.assertEqual(advance.status, "Unpaid")
 
+		advance.cancel()
+		advance.reload()
+		self.assertEqual(advance.status, "Cancelled")
+
+	def test_claimed_status(self):
+		# CLAIMED Status check, full amount claimed
+		payable_account = get_payable_account("_Test Company")
+		claim = make_expense_claim(payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
+
+		advance = make_employee_advance(claim.employee)
+		pe = make_payment_entry(advance)
+		pe.submit()
+
+		claim = get_advances_for_claim(claim, advance.name)
+		claim.save()
+		claim.submit()
+
+		advance.reload()
+		self.assertEqual(advance.claimed_amount, 1000)
+		self.assertEqual(advance.status, "Claimed")
+
+		# advance should not be shown in claims
+		advances = get_advances(claim.employee)
+		advances = [entry.name for entry in advances]
+		self.assertTrue(advance.name not in advances)
+
+		# cancel claim; status should be Paid
+		claim.cancel()
+		advance.reload()
+		self.assertEqual(advance.claimed_amount, 0)
+		self.assertEqual(advance.status, "Paid")
+
+	def test_partly_claimed_and_returned_status(self):
+		payable_account = get_payable_account("_Test Company")
+		claim = make_expense_claim(payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
+
+		advance = make_employee_advance(claim.employee)
+		pe = make_payment_entry(advance)
+		pe.submit()
+
+		# PARTLY CLAIMED AND RETURNED status check
+		# 500 Claimed, 500 Returned
+		claim = make_expense_claim(payable_account, 500, 500, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
+
+		advance = make_employee_advance(claim.employee)
+		pe = make_payment_entry(advance)
+		pe.submit()
+
+		claim = get_advances_for_claim(claim, advance.name, amount=500)
+		claim.save()
+		claim.submit()
+
+		advance.reload()
+		self.assertEqual(advance.claimed_amount, 500)
+		self.assertEqual(advance.status, "Paid")
+
+		entry = make_return_entry(
+			employee=advance.employee,
+			company=advance.company,
+			employee_advance_name=advance.name,
+			return_amount=flt(advance.paid_amount - advance.claimed_amount),
+			advance_account=advance.advance_account,
+			mode_of_payment=advance.mode_of_payment,
+			currency=advance.currency,
+			exchange_rate=advance.exchange_rate
+		)
+
+		entry = frappe.get_doc(entry)
+		entry.insert()
+		entry.submit()
+
+		advance.reload()
+		self.assertEqual(advance.return_amount, 500)
+		self.assertEqual(advance.status, "Partly Claimed and Returned")
+
+		# advance should not be shown in claims
+		advances = get_advances(claim.employee)
+		advances = [entry.name for entry in advances]
+		self.assertTrue(advance.name not in advances)
+
+		# Cancel return entry; status should change to PAID
+		entry.cancel()
+		advance.reload()
+		self.assertEqual(advance.return_amount, 0)
+		self.assertEqual(advance.status, "Paid")
+
+		# advance should be shown in claims
+		advances = get_advances(claim.employee)
+		advances = [entry.name for entry in advances]
+		self.assertTrue(advance.name in advances)
+
 	def test_repay_unclaimed_amount_from_salary(self):
 		employee_name = make_employee("_T@employe.advance")
 		advance = make_employee_advance(employee_name, {"repay_unclaimed_amount_from_salary": 1})
+		pe = make_payment_entry(advance)
+		pe.submit()
 
 		args = {"type": "Deduction"}
 		create_salary_component("Advance Salary - Deduction", **args)
@@ -82,11 +184,13 @@
 
 		advance.reload()
 		self.assertEqual(advance.return_amount, 1000)
+		self.assertEqual(advance.status, "Returned")
 
 		# update advance return amount on additional salary cancellation
 		additional_salary.cancel()
 		advance.reload()
 		self.assertEqual(advance.return_amount, 700)
+		self.assertEqual(advance.status, "Paid")
 
 	def tearDown(self):
 		frappe.db.rollback()
@@ -118,3 +222,24 @@
 	doc.submit()
 
 	return doc
+
+
+def get_advances_for_claim(claim, advance_name, amount=None):
+	advances = get_advances(claim.employee, advance_name)
+
+	for entry in advances:
+		if amount:
+			allocated_amount = amount
+		else:
+			allocated_amount = flt(entry.paid_amount) - flt(entry.claimed_amount)
+
+		claim.append("advances", {
+			"employee_advance": entry.name,
+			"posting_date": entry.posting_date,
+			"advance_account": entry.advance_account,
+			"advance_paid": entry.paid_amount,
+			"unclaimed_amount": allocated_amount,
+			"allocated_amount": allocated_amount
+		})
+
+	return claim
\ No newline at end of file
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index 0479457..af80b63 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -171,7 +171,7 @@
 					['docstatus', '=', 1],
 					['employee', '=', frm.doc.employee],
 					['paid_amount', '>', 0],
-					['status', '!=', 'Claimed']
+					['status', 'not in', ['Claimed', 'Returned', 'Partly Claimed and Returned']]
 				]
 			};
 		});
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index 7e3898b..fe04efb 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -23,10 +23,10 @@
 
 	def validate(self):
 		validate_active_employee(self.employee)
-		self.validate_advances()
+		set_employee_name(self)
 		self.validate_sanctioned_amount()
 		self.calculate_total_amount()
-		set_employee_name(self)
+		self.validate_advances()
 		self.set_expense_account(validate=True)
 		self.set_payable_account()
 		self.set_cost_center()
@@ -341,18 +341,27 @@
 
 @frappe.whitelist()
 def get_advances(employee, advance_id=None):
-	if not advance_id:
-		condition = 'docstatus=1 and employee={0} and paid_amount > 0 and paid_amount > claimed_amount + return_amount'.format(frappe.db.escape(employee))
-	else:
-		condition = 'name={0}'.format(frappe.db.escape(advance_id))
+	advance = frappe.qb.DocType("Employee Advance")
 
-	return frappe.db.sql("""
-		select
-			name, posting_date, paid_amount, claimed_amount, advance_account
-		from
-			`tabEmployee Advance`
-		where {0}
-	""".format(condition), as_dict=1)
+	query = (
+		frappe.qb.from_(advance)
+		.select(
+			advance.name, advance.posting_date, advance.paid_amount,
+			advance.claimed_amount, advance.advance_account
+		)
+	)
+
+	if not advance_id:
+		query = query.where(
+			(advance.docstatus == 1)
+			& (advance.employee == employee)
+			& (advance.paid_amount > 0)
+			& (advance.status.notin(["Claimed", "Returned", "Partly Claimed and Returned"]))
+		)
+	else:
+		query = query.where(advance.name == advance_id)
+
+	return query.run(as_dict=True)
 
 
 @frappe.whitelist()
diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
index eff2344..d4d337d 100644
--- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
@@ -1,15 +1,15 @@
 # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_months, today
 
 from erpnext import get_company_currency
-from erpnext.tests.utils import ERPNextTestCase
 
 from .blanket_order import make_order
 
 
-class TestBlanketOrder(ERPNextTestCase):
+class TestBlanketOrder(FrappeTestCase):
 	def setUp(self):
 		frappe.flags.args = frappe._dict()
 
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index d640f3f..37d2b9f 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -918,7 +918,7 @@
 			frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item))
 
 @frappe.whitelist()
-def get_children(doctype, parent=None, is_root=False, **filters):
+def get_children(parent=None, is_root=False, **filters):
 	if not parent or parent=="BOM":
 		frappe.msgprint(_('Please select a BOM'))
 		return
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 53437c8..3cc91b3 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -7,6 +7,7 @@
 
 import frappe
 from frappe.test_runner import make_test_records
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import cstr, flt
 
 from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
@@ -17,11 +18,10 @@
 	create_stock_reconciliation,
 )
 from erpnext.tests.test_subcontracting import set_backflush_based_on
-from erpnext.tests.utils import ERPNextTestCase
 
 test_records = frappe.get_test_records('BOM')
 
-class TestBOM(ERPNextTestCase):
+class TestBOM(FrappeTestCase):
 	def setUp(self):
 		if not frappe.get_value('Item', '_Test Item'):
 			make_test_records('Item')
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
index 12576cb..b4c625d 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
@@ -2,15 +2,15 @@
 # License: GNU General Public License v3. See license.txt
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
 from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
 from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.tests.utils import ERPNextTestCase
 
 test_records = frappe.get_test_records('BOM')
 
-class TestBOMUpdateTool(ERPNextTestCase):
+class TestBOMUpdateTool(FrappeTestCase):
 	def test_replace_bom(self):
 		current_bom = "BOM-_Test Item Home Desktop Manufactured-001"
 
diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py
index bb5004b..33425d2 100644
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py
@@ -2,6 +2,7 @@
 # See license.txt
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import random_string
 
 from erpnext.manufacturing.doctype.job_card.job_card import OperationMismatchError, OverlapError
@@ -11,10 +12,9 @@
 from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
 from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestJobCard(ERPNextTestCase):
+class TestJobCard(FrappeTestCase):
 	def setUp(self):
 		make_bom_for_jc_tests()
 
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 0babf87..e8759f5 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -49,7 +49,7 @@
 			if (d.item_code) {
 				return {
 					query: "erpnext.controllers.queries.bom",
-					filters:{'item': cstr(d.item_code)}
+					filters:{'item': cstr(d.item_code), 'docstatus': 1}
 				}
 			} else frappe.msgprint(__("Please enter Item first"));
 		}
@@ -232,7 +232,7 @@
 		});
 	},
 	combine_items: function (frm) {
-		frm.clear_table('prod_plan_references');
+		frm.clear_table("prod_plan_references");
 
 		frappe.call({
 			method: "get_items",
@@ -247,6 +247,13 @@
 		});
 	},
 
+	combine_sub_items: (frm) => {
+		if (frm.doc.sub_assembly_items.length > 0) {
+			frm.clear_table("sub_assembly_items");
+			frm.trigger("get_sub_assembly_items");
+		}
+	},
+
 	get_sub_assembly_items: function(frm) {
 		frm.dirty();
 
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 56cf2b4..3bfb764 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -36,6 +36,7 @@
   "prod_plan_references",
   "section_break_24",
   "get_sub_assembly_items",
+  "combine_sub_items",
   "sub_assembly_items",
   "material_request_planning",
   "include_non_stock_items",
@@ -340,7 +341,6 @@
   {
    "fieldname": "prod_plan_references",
    "fieldtype": "Table",
-   "hidden": 1,
    "label": "Production Plan Item Reference",
    "options": "Production Plan Item Reference"
   },
@@ -370,16 +370,23 @@
    "fieldname": "to_delivery_date",
    "fieldtype": "Date",
    "label": "To Delivery Date"
+  },
+  {
+   "default": "0",
+   "fieldname": "combine_sub_items",
+   "fieldtype": "Check",
+   "label": "Consolidate Sub Assembly Items"
   }
  ],
  "icon": "fa fa-calendar",
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-09-06 18:35:59.642232",
+ "modified": "2022-02-23 17:16:10.629378",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 80003da..48cd753 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -21,7 +21,8 @@
 )
 from frappe.utils.csvutils import build_csv_response
 
-from erpnext.manufacturing.doctype.bom.bom import get_children, validate_bom_no
+from erpnext.manufacturing.doctype.bom.bom import get_children as get_bom_children
+from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
 from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
 from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
 
@@ -570,17 +571,28 @@
 
 	@frappe.whitelist()
 	def get_sub_assembly_items(self, manufacturing_type=None):
+		"Fetch sub assembly items and optionally combine them."
 		self.sub_assembly_items = []
+		sub_assembly_items_store = [] # temporary store to process all subassembly items
+
 		for row in self.po_items:
 			bom_data = []
 			get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
 			self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
+			sub_assembly_items_store.extend(bom_data)
 
-		self.sub_assembly_items.sort(key= lambda d: d.bom_level, reverse=True)
-		for idx, row in enumerate(self.sub_assembly_items, start=1):
-			row.idx = idx
+		if self.combine_sub_items:
+			# Combine subassembly items
+			sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store)
+
+		sub_assembly_items_store.sort(key= lambda d: d.bom_level, reverse=True) # sort by bom level
+
+		for idx, row in enumerate(sub_assembly_items_store):
+			row.idx = idx + 1
+			self.append("sub_assembly_items", row)
 
 	def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None):
+		"Modify bom_data, set additional details."
 		for data in bom_data:
 			data.qty = data.stock_qty
 			data.production_plan_item = row.name
@@ -589,7 +601,32 @@
 			data.type_of_manufacturing = manufacturing_type or ("Subcontract" if data.is_sub_contracted_item
 				else "In House")
 
-			self.append("sub_assembly_items", data)
+	def combine_subassembly_items(self, sub_assembly_items_store):
+		"Aggregate if same: Item, Warehouse, Inhouse/Outhouse Manu.g, BOM No."
+		key_wise_data = {}
+		for row in sub_assembly_items_store:
+			key = (
+				row.get("production_item"), row.get("fg_warehouse"),
+				row.get("bom_no"), row.get("type_of_manufacturing")
+			)
+			if key not in key_wise_data:
+				# intialise (item, wh, bom no, man.g type) wise dict
+				key_wise_data[key] = row
+				continue
+
+			existing_row = key_wise_data[key]
+			if existing_row:
+				# if row with same (item, wh, bom no, man.g type) key, merge
+				existing_row.qty += flt(row.qty)
+				existing_row.stock_qty += flt(row.stock_qty)
+				existing_row.bom_level = max(existing_row.bom_level, row.bom_level)
+				continue
+			else:
+				# add row with key
+				key_wise_data[key] = row
+
+		sub_assembly_items_store = [key_wise_data[key] for key in key_wise_data] # unpack into single level list
+		return sub_assembly_items_store
 
 	def all_items_completed(self):
 		all_items_produced = all(flt(d.planned_qty) - flt(d.produced_qty) < 0.000001
@@ -1031,7 +1068,7 @@
 	}
 
 def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
-	data = get_children('BOM', parent = bom_no)
+	data = get_bom_children(parent=bom_no)
 	for d in data:
 		if d.expandable:
 			parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 2359815..eeab788 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -1,6 +1,7 @@
 # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_to_date, flt, now_datetime, nowdate
 
 from erpnext.controllers.item_variant import create_variant
@@ -16,10 +17,9 @@
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 	create_stock_reconciliation,
 )
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestProductionPlan(ERPNextTestCase):
+class TestProductionPlan(FrappeTestCase):
 	def setUp(self):
 		for item in ['Test Production Item 1', 'Subassembly Item 1',
 			'Raw Material Item 1', 'Raw Material Item 2']:
@@ -38,6 +38,9 @@
 			if not frappe.db.get_value('BOM', {'item': item}):
 				make_bom(item = item, raw_materials = raw_materials)
 
+	def tearDown(self) -> None:
+		frappe.db.rollback()
+
 	def test_production_plan_mr_creation(self):
 		"Test if MRs are created for unavailable raw materials."
 		pln = create_production_plan(item_code='Test Production Item 1')
@@ -110,7 +113,7 @@
 			item_code='Test Production Item 1',
 			ignore_existing_ordered_qty=1
 		)
-		self.assertTrue(len(pln.mr_items), 1)
+		self.assertTrue(len(pln.mr_items))
 		self.assertTrue(flt(pln.mr_items[0].quantity), 1.0)
 
 		sr1.cancel()
@@ -151,7 +154,7 @@
 			use_multi_level_bom=0,
 			ignore_existing_ordered_qty=0
 		)
-		self.assertTrue(len(pln.mr_items), 0)
+		self.assertFalse(len(pln.mr_items))
 
 		sr1.cancel()
 		sr2.cancel()
@@ -258,6 +261,51 @@
 		pln.reload()
 		pln.cancel()
 
+	def test_production_plan_combine_subassembly(self):
+		"""
+		Test combining Sub assembly items belonging to the same BOM in Prod Plan.
+		1) Red-Car -> Wheel (sub assembly) > BOM-WHEEL-001
+		2) Green-Car -> Wheel (sub assembly) > BOM-WHEEL-001
+		"""
+		from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
+
+		bom_tree_1 = {
+			"Red-Car": {"Wheel": {"Rubber": {}}}
+		}
+		bom_tree_2 = {
+			"Green-Car": {"Wheel": {"Rubber": {}}}
+		}
+
+		parent_bom_1 = create_nested_bom(bom_tree_1, prefix="")
+		parent_bom_2 = create_nested_bom(bom_tree_2, prefix="")
+
+		# make sure both boms use same subassembly bom
+		subassembly_bom = parent_bom_1.items[0].bom_no
+		frappe.db.set_value("BOM Item", parent_bom_2.items[0].name, "bom_no", subassembly_bom)
+
+		plan = create_production_plan(item_code="Red-Car", use_multi_level_bom=1, do_not_save=True)
+		plan.append("po_items", { # Add Green-Car to Prod Plan
+			'use_multi_level_bom': 1,
+			'item_code': "Green-Car",
+			'bom_no': frappe.db.get_value('Item', "Green-Car", 'default_bom'),
+			'planned_qty': 1,
+			'planned_start_date': now_datetime()
+		})
+		plan.get_sub_assembly_items()
+		self.assertTrue(len(plan.sub_assembly_items), 2)
+
+		plan.combine_sub_items = 1
+		plan.get_sub_assembly_items()
+
+		self.assertTrue(len(plan.sub_assembly_items), 1) # check if sub-assembly items merged
+		self.assertEqual(plan.sub_assembly_items[0].qty, 2.0)
+		self.assertEqual(plan.sub_assembly_items[0].stock_qty, 2.0)
+
+		# change warehouse in one row, sub-assemblies should not merge
+		plan.po_items[0].warehouse = "Finished Goods - _TC"
+		plan.get_sub_assembly_items()
+		self.assertTrue(len(plan.sub_assembly_items), 2)
+
 	def test_pp_to_mr_customer_provided(self):
 		" Test Material Request from Production Plan for Customer Provided Item."
 		create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
@@ -532,6 +580,7 @@
 			wip_warehouse='Work In Progress - _TC',
 			fg_warehouse='Finished Goods - _TC',
 			skip_transfer=1,
+			use_multi_level_bom=1,
 			do_not_submit=True
 		)
 		wo.production_plan = pln.name
@@ -576,6 +625,7 @@
 			wip_warehouse='Work In Progress - _TC',
 			fg_warehouse='Finished Goods - _TC',
 			skip_transfer=1,
+			use_multi_level_bom=1,
 			do_not_submit=True
 		)
 		wo.production_plan = pln.name
diff --git a/erpnext/manufacturing/doctype/routing/test_routing.py b/erpnext/manufacturing/doctype/routing/test_routing.py
index 8bd60ea..696d9bc 100644
--- a/erpnext/manufacturing/doctype/routing/test_routing.py
+++ b/erpnext/manufacturing/doctype/routing/test_routing.py
@@ -2,14 +2,14 @@
 # See license.txt
 import frappe
 from frappe.test_runner import make_test_records
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.manufacturing.doctype.job_card.job_card import OperationSequenceError
 from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
 from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestRouting(ERPNextTestCase):
+class TestRouting(FrappeTestCase):
 	@classmethod
 	def setUpClass(cls):
 		cls.item_code = "Test Routing Item - A"
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 67c47ef..bc07d22 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -2,6 +2,7 @@
 # License: GNU General Public License v3. See license.txt
 
 import frappe
+from frappe.tests.utils import FrappeTestCase, timeout
 from frappe.utils import add_days, add_months, cint, flt, now, today
 
 from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError
@@ -21,10 +22,9 @@
 from erpnext.stock.doctype.stock_entry import test_stock_entry
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.utils import get_bin
-from erpnext.tests.utils import ERPNextTestCase, timeout
 
 
-class TestWorkOrder(ERPNextTestCase):
+class TestWorkOrder(FrappeTestCase):
 	def setUp(self):
 		self.warehouse = '_Test Warehouse 2 - _TC'
 		self.item = '_Test Item'
@@ -1040,7 +1040,7 @@
 	wo_order.scrap_warehouse = args.fg_warehouse or "_Test Scrap Warehouse - _TC"
 	wo_order.company = args.company or "_Test Company"
 	wo_order.stock_uom = args.stock_uom or "_Test UOM"
-	wo_order.use_multi_level_bom=0
+	wo_order.use_multi_level_bom= args.use_multi_level_bom or 0
 	wo_order.skip_transfer=args.skip_transfer or 0
 	wo_order.get_items_and_operations_from_bom()
 	wo_order.sales_order = args.sales_order or None
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index e1120c7..374ab86 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -854,7 +854,7 @@
 	res = res[0]
 	if skip_bom_info: return res
 
-	filters = {"item": item, "is_default": 1}
+	filters = {"item": item, "is_default": 1, "docstatus": 1}
 
 	if project:
 		filters = {"item": item, "project": project}
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py
index c298c0a..dd51017 100644
--- a/erpnext/manufacturing/doctype/workstation/test_workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py
@@ -2,6 +2,7 @@
 # See license.txt
 import frappe
 from frappe.test_runner import make_test_records
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.manufacturing.doctype.operation.test_operation import make_operation
 from erpnext.manufacturing.doctype.routing.test_routing import create_routing, setup_bom
@@ -10,13 +11,12 @@
 	WorkstationHolidayError,
 	check_if_within_operating_hours,
 )
-from erpnext.tests.utils import ERPNextTestCase
 
 test_dependencies = ["Warehouse"]
 test_records = frappe.get_test_records('Workstation')
 make_test_records('Workstation')
 
-class TestWorkstation(ERPNextTestCase):
+class TestWorkstation(FrappeTestCase):
 	def test_validate_timings(self):
 		check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00")
 		check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 10:00:00", "2013-02-02 20:00:00")
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 12af8f8..13f0e7b 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -356,4 +356,5 @@
 erpnext.patches.v13_0.set_work_order_qty_in_so_from_mr
 erpnext.patches.v13_0.update_accounts_in_loan_docs
 erpnext.patches.v14_0.update_batch_valuation_flag
-erpnext.patches.v14_0.delete_non_profit_doctypes
\ No newline at end of file
+erpnext.patches.v14_0.delete_non_profit_doctypes
+erpnext.patches.v14_0.update_employee_advance_status
\ No newline at end of file
diff --git a/erpnext/patches/v14_0/update_employee_advance_status.py b/erpnext/patches/v14_0/update_employee_advance_status.py
new file mode 100644
index 0000000..a20e35a
--- /dev/null
+++ b/erpnext/patches/v14_0/update_employee_advance_status.py
@@ -0,0 +1,26 @@
+import frappe
+
+
+def execute():
+	frappe.reload_doc('hr', 'doctype', 'employee_advance')
+
+	advance = frappe.qb.DocType('Employee Advance')
+	(frappe.qb
+		.update(advance)
+		.set(advance.status, 'Returned')
+		.where(
+			(advance.docstatus == 1)
+			& ((advance.return_amount) & (advance.paid_amount == advance.return_amount))
+			& (advance.status == 'Paid')
+		)
+	).run()
+
+	(frappe.qb
+		.update(advance)
+		.set(advance.status, 'Partly Claimed and Returned')
+		.where(
+			(advance.docstatus == 1)
+			& ((advance.claimed_amount & advance.return_amount) & (advance.paid_amount == (advance.return_amount + advance.claimed_amount)))
+			& (advance.status == 'Paid')
+		)
+	).run()
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
index bf8bd05..d618568 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -105,6 +105,8 @@
 				return_amount += self.amount
 
 			frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", return_amount)
+			advance = frappe.get_doc("Employee Advance", self.ref_docname)
+			advance.set_status(update=True)
 
 	def update_employee_referral(self, cancel=False):
 		if self.ref_doctype == "Employee Referral":
diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py
index 5301fd0..165ee81 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -4,12 +4,13 @@
 
 import frappe
 from frappe.test_runner import make_test_records
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import flt
 
 from erpnext.accounts.party import get_due_date
 from erpnext.exceptions import PartyDisabled, PartyFrozen
 from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding
-from erpnext.tests.utils import ERPNextTestCase, create_test_contact_and_address
+from erpnext.tests.utils import create_test_contact_and_address
 
 test_ignore = ["Price List"]
 test_dependencies = ['Payment Term', 'Payment Terms Template']
@@ -17,7 +18,7 @@
 
 
 
-class TestCustomer(ERPNextTestCase):
+class TestCustomer(FrappeTestCase):
 	def setUp(self):
 		if not frappe.get_value('Item', '_Test Item'):
 			make_test_records('Item')
diff --git a/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py b/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py
index b951044..9b672b4 100644
--- a/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py
+++ b/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py
@@ -1,12 +1,10 @@
 # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-import unittest
-
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.controllers.queries import item_query
-from erpnext.tests.utils import ERPNextTestCase
 
 test_dependencies = ['Item', 'Customer', 'Supplier']
 
@@ -18,7 +16,7 @@
 	psi.based_on_value = args.get('based_on_value')
 	psi.insert()
 
-class TestPartySpecificItem(ERPNextTestCase):
+class TestPartySpecificItem(FrappeTestCase):
 	def setUp(self):
 		self.customer = frappe.get_last_doc("Customer")
 		self.supplier = frappe.get_last_doc("Supplier")
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index 4357201..a749d9e 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -2,14 +2,13 @@
 # License: GNU General Public License v3. See license.txt
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, add_months, flt, getdate, nowdate
 
-from erpnext.tests.utils import ERPNextTestCase
-
 test_dependencies = ["Product Bundle"]
 
 
-class TestQuotation(ERPNextTestCase):
+class TestQuotation(FrappeTestCase):
 	def test_make_quotation_without_terms(self):
 		quotation = make_quotation(do_not_save=1)
 		self.assertFalse(quotation.get('payment_schedule'))
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index e56e56c..b528479 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -6,6 +6,7 @@
 import frappe
 import frappe.permissions
 from frappe.core.doctype.user_permission.test_user_permission import create_user
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, flt, getdate, nowdate, today
 
 from erpnext.controllers.accounts_controller import update_child_qty_rate
@@ -27,10 +28,9 @@
 )
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestSalesOrder(ERPNextTestCase):
+class TestSalesOrder(FrappeTestCase):
 
 	@classmethod
 	def setUpClass(cls):
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 080d517..7e55499 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -83,8 +83,8 @@
   "planned_qty",
   "column_break_69",
   "work_order_qty",
-  "produced_qty",
   "delivered_qty",
+  "produced_qty",
   "returned_qty",
   "shopping_cart_section",
   "additional_notes",
@@ -701,8 +701,10 @@
    "width": "50px"
   },
   {
+   "description": "For Production",
    "fieldname": "produced_qty",
    "fieldtype": "Float",
+   "hidden": 1,
    "label": "Produced Quantity",
    "oldfieldname": "produced_qty",
    "oldfieldtype": "Currency",
@@ -791,6 +793,7 @@
   },
   {
    "default": "0",
+   "fetch_from": "item_code.grant_commission",
    "fieldname": "grant_commission",
    "fieldtype": "Check",
    "label": "Grant Commission",
@@ -800,7 +803,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-02-21 13:55:08.883104",
+ "modified": "2022-02-24 14:41:57.325799",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",
diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py
index cad41e1..f7f8a5d 100644
--- a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py
+++ b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py
@@ -1,6 +1,7 @@
 import datetime
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days
 
 from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
@@ -9,12 +10,11 @@
 	execute,
 )
 from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.tests.utils import ERPNextTestCase
 
 test_dependencies = ["Sales Order", "Item", "Sales Invoice", "Payment Terms Template"]
 
 
-class TestPaymentTermsStatusForSalesOrder(ERPNextTestCase):
+class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
 	def create_payment_terms_template(self):
 		# create template for 50-50 payments
 		template = None
diff --git a/erpnext/selling/report/pending_so_items_for_purchase_request/test_pending_so_items_for_purchase_request.py b/erpnext/selling/report/pending_so_items_for_purchase_request/test_pending_so_items_for_purchase_request.py
index d62915f..16162ac 100644
--- a/erpnext/selling/report/pending_so_items_for_purchase_request/test_pending_so_items_for_purchase_request.py
+++ b/erpnext/selling/report/pending_so_items_for_purchase_request/test_pending_so_items_for_purchase_request.py
@@ -2,6 +2,7 @@
 # For license information, please see license.txt
 
 
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_months, nowdate
 
 from erpnext.selling.doctype.sales_order.sales_order import make_material_request
@@ -9,10 +10,9 @@
 from erpnext.selling.report.pending_so_items_for_purchase_request.pending_so_items_for_purchase_request import (
 	execute,
 )
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestPendingSOItemsForPurchaseRequest(ERPNextTestCase):
+class TestPendingSOItemsForPurchaseRequest(FrappeTestCase):
     def test_result_for_partial_material_request(self):
         so = make_sales_order()
         mr=make_material_request(so.name)
diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py
index f56cce2..564f48f 100644
--- a/erpnext/selling/report/sales_analytics/test_analytics.py
+++ b/erpnext/selling/report/sales_analytics/test_analytics.py
@@ -3,13 +3,13 @@
 
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
 from erpnext.selling.report.sales_analytics.sales_analytics import execute
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestAnalytics(ERPNextTestCase):
+class TestAnalytics(FrappeTestCase):
 	def test_sales_analytics(self):
 		frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'")
 
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index 37e9e89..c9d5f61 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -213,7 +213,14 @@
 			label: __('Target Warehouse'),
 			fieldtype: 'Link',
 			options: 'Warehouse',
-			reqd: 1
+			reqd: 1,
+			get_query() {
+				return {
+					filters: {
+						is_group: 0
+					}
+				}
+			}
 		},
 		{
 			fieldname: 'qty',
@@ -252,52 +259,21 @@
 		dialog.get_field('target').refresh();
 	}
 
-	dialog.set_primary_action(__('Submit'), function () {
-		var values = dialog.get_values();
-		if (!values) {
-			return;
-		}
-		if (source && values.qty > actual_qty) {
-			frappe.msgprint(__('Quantity must be less than or equal to {0}', [actual_qty]));
-			return;
-		}
-		if (values.source === values.target) {
-			frappe.msgprint(__('Source and target warehouse must be different'));
-		}
-
-		frappe.call({
-			method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry',
-			args: values,
-			btn: dialog.get_primary_btn(),
-			freeze: true,
-			freeze_message: __('Creating Stock Entry'),
-			callback: function (r) {
-				frappe.show_alert(__('Stock Entry {0} created',
-					['<a href="/app/stock-entry/' + r.message.name + '">' + r.message.name + '</a>']));
-				dialog.hide();
-				callback(r);
-			},
+	dialog.set_primary_action(__('Create Stock Entry'), function () {
+		frappe.model.with_doctype('Stock Entry', function () {
+			let doc = frappe.model.get_new_doc('Stock Entry');
+			doc.from_warehouse = dialog.get_value('source');
+			doc.to_warehouse = dialog.get_value('target');
+			doc.stock_entry_type = doc.from_warehouse ? "Material Transfer" : "Material Receipt";
+			let row = frappe.model.add_child(doc, 'items');
+			row.item_code = dialog.get_value('item_code');
+			row.s_warehouse = dialog.get_value('source');
+			row.t_warehouse = dialog.get_value('target');
+			row.qty = dialog.get_value('qty');
+			row.conversion_factor = 1;
+			row.transfer_qty = dialog.get_value('qty');
+			row.basic_rate = dialog.get_value('rate');
+			frappe.set_route('Form', doc.doctype, doc.name);
 		});
 	});
-
-	$('<p style="margin-left: 10px;"><a class="link-open text-muted small">' +
-			__("Add more items or open full form") + '</a></p>')
-		.appendTo(dialog.body)
-		.find('.link-open')
-		.on('click', function () {
-			frappe.model.with_doctype('Stock Entry', function () {
-				var doc = frappe.model.get_new_doc('Stock Entry');
-				doc.from_warehouse = dialog.get_value('source');
-				doc.to_warehouse = dialog.get_value('target');
-				var row = frappe.model.add_child(doc, 'items');
-				row.item_code = dialog.get_value('item_code');
-				row.f_warehouse = dialog.get_value('target');
-				row.t_warehouse = dialog.get_value('target');
-				row.qty = dialog.get_value('qty');
-				row.conversion_factor = 1;
-				row.transfer_qty = dialog.get_value('qty');
-				row.basic_rate = dialog.get_value('rate');
-				frappe.set_route('Form', doc.doctype, doc.name);
-			});
-		});
 };
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 613dd3f..5763753 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -5,6 +5,7 @@
 
 import frappe
 from frappe.exceptions import ValidationError
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import cint, flt
 from frappe.utils.data import add_to_date, getdate
 
@@ -16,10 +17,9 @@
 )
 from erpnext.stock.get_item_details import get_item_details
 from erpnext.stock.stock_ledger import get_valuation_rate
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestBatch(ERPNextTestCase):
+class TestBatch(FrappeTestCase):
 	def test_item_has_batch_enabled(self):
 		self.assertRaises(ValidationError, frappe.get_doc({
 			"doctype": "Batch",
diff --git a/erpnext/stock/doctype/bin/test_bin.py b/erpnext/stock/doctype/bin/test_bin.py
index 250126c..ec0d8a8 100644
--- a/erpnext/stock/doctype/bin/test_bin.py
+++ b/erpnext/stock/doctype/bin/test_bin.py
@@ -2,13 +2,13 @@
 # See license.txt
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.utils import _create_bin
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestBin(ERPNextTestCase):
+class TestBin(FrappeTestCase):
 
 
 	def test_concurrent_inserts(self):
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index bd18e78..16c8921 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -6,6 +6,7 @@
 import json
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import cstr, flt, nowdate, nowtime
 
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
@@ -35,10 +36,9 @@
 )
 from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
 from erpnext.stock.stock_ledger import get_previous_sle
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestDeliveryNote(ERPNextTestCase):
+class TestDeliveryNote(FrappeTestCase):
 	def test_over_billing_against_dn(self):
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index 51c88be..f1f5d96 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -757,6 +757,7 @@
   },
   {
    "default": "0",
+   "fetch_from": "item_code.grant_commission",
    "fieldname": "grant_commission",
    "fieldtype": "Check",
    "label": "Grant Commission",
@@ -767,12 +768,14 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-10-06 12:12:44.018872",
+ "modified": "2022-02-24 14:42:20.211085",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
+ "naming_rule": "Random",
  "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
- "sort_order": "DESC"
-}
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
index 321f48b..dcdff4a 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -4,6 +4,7 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, flt, now_datetime, nowdate
 
 import erpnext
@@ -12,10 +13,10 @@
 	make_expense_claim,
 	notify_customers,
 )
-from erpnext.tests.utils import ERPNextTestCase, create_test_contact_and_address
+from erpnext.tests.utils import create_test_contact_and_address
 
 
-class TestDeliveryTrip(ERPNextTestCase):
+class TestDeliveryTrip(FrappeTestCase):
 	def setUp(self):
 		super().setUp()
 		driver = create_driver()
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 9491e17..d7671b1 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -6,6 +6,7 @@
 
 import frappe
 from frappe.test_runner import make_test_objects
+from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_days, today
 
 from erpnext.controllers.item_variant import (
@@ -25,7 +26,6 @@
 )
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.get_item_details import get_item_details
-from erpnext.tests.utils import ERPNextTestCase, change_settings
 
 test_ignore = ["BOM"]
 test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
@@ -53,7 +53,7 @@
 
 	return item
 
-class TestItem(ERPNextTestCase):
+class TestItem(FrappeTestCase):
 	def setUp(self):
 		super().setUp()
 		frappe.flags.attribute_values = None
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
index 3976af4..501c1c1 100644
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
@@ -4,6 +4,7 @@
 import json
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import flt
 
 from erpnext.buying.doctype.purchase_order.purchase_order import (
@@ -18,10 +19,9 @@
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 	create_stock_reconciliation,
 )
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestItemAlternative(ERPNextTestCase):
+class TestItemAlternative(FrappeTestCase):
 	def setUp(self):
 		super().setUp()
 		make_items()
diff --git a/erpnext/stock/doctype/item_attribute/test_item_attribute.py b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
index 0b7ca25..055c22e 100644
--- a/erpnext/stock/doctype/item_attribute/test_item_attribute.py
+++ b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
@@ -6,11 +6,12 @@
 
 test_records = frappe.get_test_records('Item Attribute')
 
+from frappe.tests.utils import FrappeTestCase
+
 from erpnext.stock.doctype.item_attribute.item_attribute import ItemAttributeIncrementError
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestItemAttribute(ERPNextTestCase):
+class TestItemAttribute(FrappeTestCase):
 	def setUp(self):
 		super().setUp()
 		if frappe.db.exists("Item Attribute", "_Test_Length"):
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index f81770e..6ceba3f 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -4,13 +4,13 @@
 
 import frappe
 from frappe.test_runner import make_test_records_for_doctype
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
 from erpnext.stock.get_item_details import get_price_list_rate_for, process_args
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestItemPrice(ERPNextTestCase):
+class TestItemPrice(FrappeTestCase):
 	def setUp(self):
 		super().setUp()
 		frappe.db.sql("delete from `tabItem Price`")
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index df8cadd..dbaefc1 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -4,6 +4,7 @@
 
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_to_date, flt, now
 
 from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
@@ -14,10 +15,9 @@
 	get_gl_entries,
 	make_purchase_receipt,
 )
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestLandedCostVoucher(ERPNextTestCase):
+class TestLandedCostVoucher(FrappeTestCase):
 	def test_landed_cost_voucher(self):
 		frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
 
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 383b0ae..1cda781 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -6,6 +6,7 @@
 
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import flt, today
 
 from erpnext.stock.doctype.item.test_item import create_item
@@ -15,10 +16,9 @@
 	make_supplier_quotation,
 	raise_work_orders,
 )
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestMaterialRequest(ERPNextTestCase):
+class TestMaterialRequest(FrappeTestCase):
 	def test_make_purchase_order(self):
 		mr = frappe.copy_doc(test_records[0]).insert()
 
diff --git a/erpnext/stock/doctype/packed_item/test_packed_item.py b/erpnext/stock/doctype/packed_item/test_packed_item.py
index 2521ac9..94268a8 100644
--- a/erpnext/stock/doctype/packed_item/test_packed_item.py
+++ b/erpnext/stock/doctype/packed_item/test_packed_item.py
@@ -1,6 +1,7 @@
 # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
+from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_to_date, nowdate
 
 from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
@@ -9,10 +10,9 @@
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext.tests.utils import ERPNextTestCase, change_settings
 
 
-class TestPackedItem(ERPNextTestCase):
+class TestPackedItem(FrappeTestCase):
 	"Test impact on Packed Items table in various scenarios."
 	@classmethod
 	def setUpClass(cls) -> None:
diff --git a/erpnext/stock/doctype/packing_slip/test_packing_slip.py b/erpnext/stock/doctype/packing_slip/test_packing_slip.py
index 5eb6b73..bc405b2 100644
--- a/erpnext/stock/doctype/packing_slip/test_packing_slip.py
+++ b/erpnext/stock/doctype/packing_slip/test_packing_slip.py
@@ -4,7 +4,7 @@
 import unittest
 
 # test_records = frappe.get_test_records('Packing Slip')
-from erpnext.tests.utils import ERPNextTestCase
+from frappe.tests.utils import FrappeTestCase
 
 
 class TestPackingSlip(unittest.TestCase):
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index 41e3150..f3b6b89 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -6,16 +6,17 @@
 
 test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch']
 
+from frappe.tests.utils import FrappeTestCase
+
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.pick_list.pick_list import create_delivery_note
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
 	EmptyStockReconciliationItemsError,
 )
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestPickList(ERPNextTestCase):
+class TestPickList(FrappeTestCase):
 
 	def test_pick_list_picks_warehouse_for_each_item(self):
 		try:
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index d481689..a24acb1 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -7,6 +7,7 @@
 from collections import defaultdict
 
 import frappe
+from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_days, cint, cstr, flt, today
 
 import erpnext
@@ -17,10 +18,9 @@
 from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
-from erpnext.tests.utils import ERPNextTestCase, change_settings
 
 
-class TestPurchaseReceipt(ERPNextTestCase):
+class TestPurchaseReceipt(FrappeTestCase):
 	def setUp(self):
 		frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
 
diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
index ff1c19a..4e8d71f 100644
--- a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
@@ -2,6 +2,7 @@
 # See license.txt
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.stock.doctype.batch.test_batch import make_new_batch
 from erpnext.stock.doctype.item.test_item import make_item
@@ -9,10 +10,9 @@
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.get_item_details import get_conversion_factor
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestPutawayRule(ERPNextTestCase):
+class TestPutawayRule(FrappeTestCase):
 	def setUp(self):
 		if not frappe.db.exists("Item", "_Rice"):
 			make_item("_Rice", {
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index 308c628..601ca05 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -2,6 +2,7 @@
 # See license.txt
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import nowdate
 
 from erpnext.controllers.stock_controller import (
@@ -13,12 +14,11 @@
 from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext.tests.utils import ERPNextTestCase
 
 # test_records = frappe.get_test_records('Quality Inspection')
 
 
-class TestQualityInspection(ERPNextTestCase):
+class TestQualityInspection(FrappeTestCase):
 	def setUp(self):
 		super().setUp()
 		create_item("_Test Item with QA")
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py
index f8cea71..057a7d4 100644
--- a/erpnext/stock/doctype/serial_no/test_serial_no.py
+++ b/erpnext/stock/doctype/serial_no/test_serial_no.py
@@ -18,11 +18,12 @@
 test_dependencies = ["Item"]
 test_records = frappe.get_test_records('Serial No')
 
+from frappe.tests.utils import FrappeTestCase
+
 from erpnext.stock.doctype.serial_no.serial_no import *
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestSerialNo(ERPNextTestCase):
+class TestSerialNo(FrappeTestCase):
 
 	def tearDown(self):
 		frappe.db.rollback()
diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
index afe8218..317abb6 100644
--- a/erpnext/stock/doctype/shipment/test_shipment.py
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -4,12 +4,12 @@
 from datetime import date, timedelta
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestShipment(ERPNextTestCase):
+class TestShipment(FrappeTestCase):
 	def test_shipment_from_delivery_note(self):
 		delivery_note = create_test_delivery_note()
 		delivery_note.submit()
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index c5afa49..54c0e43 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -6,6 +6,7 @@
 
 import frappe
 from frappe.permissions import add_user_permission, remove_user_permission
+from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import flt, nowdate, nowtime
 
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
@@ -28,7 +29,6 @@
 	create_stock_reconciliation,
 )
 from erpnext.stock.stock_ledger import NegativeStockError, get_previous_sle
-from erpnext.tests.utils import ERPNextTestCase, change_settings
 
 
 def get_sle(**args):
@@ -42,7 +42,7 @@
 		order by timestamp(posting_date, posting_time) desc, creation desc limit 1"""% condition,
 		values, as_dict=1)
 
-class TestStockEntry(ERPNextTestCase):
+class TestStockEntry(FrappeTestCase):
 	def tearDown(self):
 		frappe.db.rollback()
 		frappe.set_user("Administrator")
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index 0864ece..01d25b2 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -7,6 +7,7 @@
 
 import frappe
 from frappe.core.page.permission_manager.permission_manager import reset
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, today
 
 from erpnext.stock.doctype.delivery_note.test_delivery_note import (
@@ -24,10 +25,9 @@
 	create_stock_reconciliation,
 )
 from erpnext.stock.stock_ledger import get_previous_sle
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestStockLedgerEntry(ERPNextTestCase):
+class TestStockLedgerEntry(FrappeTestCase):
 	def setUp(self):
 		items = create_items()
 		reset('Stock Entry')
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 2ffe127..e6b252e 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -6,6 +6,7 @@
 
 
 import frappe
+from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_days, cstr, flt, nowdate, nowtime, random_string
 
 from erpnext.accounts.utils import get_stock_and_account_balance
@@ -19,10 +20,9 @@
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
 from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
-from erpnext.tests.utils import ERPNextTestCase, change_settings
 
 
-class TestStockReconciliation(ERPNextTestCase):
+class TestStockReconciliation(FrappeTestCase):
 	@classmethod
 	def setUpClass(cls):
 		create_batch_or_serial_no_items()
diff --git a/erpnext/stock/doctype/stock_settings/test_stock_settings.py b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
index 072b54b..1349671 100644
--- a/erpnext/stock/doctype/stock_settings/test_stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
@@ -4,11 +4,10 @@
 import unittest
 
 import frappe
-
-from erpnext.tests.utils import ERPNextTestCase
+from frappe.tests.utils import FrappeTestCase
 
 
-class TestStockSettings(ERPNextTestCase):
+class TestStockSettings(FrappeTestCase):
 	def setUp(self):
 		super().setUp()
 		frappe.db.set_value("Stock Settings", None, "clean_description_html", 0)
diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py
index 26db264..cdb7719 100644
--- a/erpnext/stock/doctype/warehouse/test_warehouse.py
+++ b/erpnext/stock/doctype/warehouse/test_warehouse.py
@@ -3,17 +3,17 @@
 
 import frappe
 from frappe.test_runner import make_test_records
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import cint
 
 import erpnext
 from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext.tests.utils import ERPNextTestCase
 
 test_records = frappe.get_test_records('Warehouse')
 
-class TestWarehouse(ERPNextTestCase):
+class TestWarehouse(FrappeTestCase):
 	def setUp(self):
 		super().setUp()
 		if not frappe.get_value('Item', '_Test Item'):
diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json
index 05076b5..c695d54 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.json
+++ b/erpnext/stock/doctype/warehouse/warehouse.json
@@ -244,7 +244,7 @@
  "idx": 1,
  "is_tree": 1,
  "links": [],
- "modified": "2021-12-03 04:40:06.414630",
+ "modified": "2022-03-01 02:37:48.034944",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Warehouse",
@@ -301,5 +301,7 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
- "title_field": "warehouse_name"
+ "states": [],
+ "title_field": "warehouse_name",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py
index 3fc357e..2630805 100644
--- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py
@@ -2,12 +2,12 @@
 # See license.txt
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, format_report_data
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestStockAgeing(ERPNextTestCase):
+class TestStockAgeing(FrappeTestCase):
 	def setUp(self) -> None:
 		self.filters = frappe._dict(
 			company="_Test Company",
@@ -610,4 +610,4 @@
 	item_wh_wise_slots = FIFOSlots(filters, sle).generate()
 	filters.show_warehouse_wise_stock = False
 
-	return item_wise_slots, item_wh_wise_slots
\ No newline at end of file
+	return item_wise_slots, item_wh_wise_slots
diff --git a/erpnext/stock/report/stock_analytics/test_stock_analytics.py b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
index 32df585..f6c98f9 100644
--- a/erpnext/stock/report/stock_analytics/test_stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
@@ -1,14 +1,13 @@
 import datetime
-import unittest
 
 from frappe import _dict
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges
-from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestStockAnalyticsReport(ERPNextTestCase):
+class TestStockAnalyticsReport(FrappeTestCase):
 	def test_get_period_date_ranges(self):
 
 		filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")
diff --git a/erpnext/stock/tests/test_valuation.py b/erpnext/stock/tests/test_valuation.py
index bdb768f..b64ff8e 100644
--- a/erpnext/stock/tests/test_valuation.py
+++ b/erpnext/stock/tests/test_valuation.py
@@ -2,13 +2,13 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from hypothesis import given
 from hypothesis import strategies as st
 
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.valuation import FIFOValuation, LIFOValuation, round_off_if_near_zero
-from erpnext.tests.utils import ERPNextTestCase
 
 qty_gen = st.floats(min_value=-1e6, max_value=1e6)
 value_gen = st.floats(min_value=1, max_value=1e6)
@@ -290,7 +290,7 @@
 			self.assertTotalQty(total_qty)
 			self.assertTotalValue(total_value)
 
-class TestLIFOValuationSLE(ERPNextTestCase):
+class TestLIFOValuationSLE(FrappeTestCase):
 	ITEM_CODE = "_Test LIFO item"
 	WAREHOUSE = "_Test Warehouse - _TC"
 
diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html
index 82b2716..faf5e92 100644
--- a/erpnext/templates/includes/footer/footer_powered.html
+++ b/erpnext/templates/includes/footer/footer_powered.html
@@ -1,27 +1 @@
-{% set domains = frappe.get_doc("Domain Settings").active_domains %}
-{% set links = {
-	'Manufacturing': '/manufacturing',
-	'Services': '/services',
-	'Retail': '/retail',
-	'Distribution': '/distribution',
-	'Non Profit': '/non-profit',
-	'Education': '/education',
-	'Healthcare': '/healthcare',
-	'Agriculture': '/agriculture',
-	'Hospitality': ''
-} %}
-
-{% set link = '' %}
-{% set label = '' %}
-{% if domains %}
-	{% set label = domains[0].domain %}
-	{% set link = links[label] %}
-{% endif %}
-
-{% if label == "Services" %}
-	{% set label = "Service" %}
-{% endif %}
-
-
-
-<a href="https://erpnext.com{{ link }}?source=website_footer" target="_blank" class="text-muted">Powered by ERPNext - {{ '' if domains else 'Open Source' }} ERP Software {{ ('for ' + label + ' Companies') if domains else '' }}</a>
+<a href="https://erpnext.com?source=website_footer" target="_blank" class="text-muted">Powered by ERPNext</a>
diff --git a/erpnext/tests/test_point_of_sale.py b/erpnext/tests/test_point_of_sale.py
index 3299c88..38f2c16 100644
--- a/erpnext/tests/test_point_of_sale.py
+++ b/erpnext/tests/test_point_of_sale.py
@@ -25,7 +25,7 @@
 		Test Stock and Service Item Search.
 		"""
 
-		pos_profile = make_pos_profile()
+		pos_profile = make_pos_profile(name="Test POS Profile for Search")
 		item1 = make_item("Test Search Stock Item", {"is_stock_item": 1})
 		make_stock_entry(
 			item_code="Test Search Stock Item",
diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py
index 40c95eb..d795253 100644
--- a/erpnext/tests/utils.py
+++ b/erpnext/tests/utils.py
@@ -1,10 +1,6 @@
 # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
-import copy
-import signal
-import unittest
-from contextlib import contextmanager
 from typing import Any, Dict, NewType, Optional
 
 import frappe
@@ -13,22 +9,6 @@
 ReportFilters = Dict[str, Any]
 ReportName = NewType("ReportName", str)
 
-
-class ERPNextTestCase(unittest.TestCase):
-	"""A sane default test class for ERPNext tests."""
-
-
-	@classmethod
-	def setUpClass(cls) -> None:
-		frappe.db.commit()
-		return super().setUpClass()
-
-	@classmethod
-	def tearDownClass(cls) -> None:
-		frappe.db.rollback()
-		return super().tearDownClass()
-
-
 def create_test_contact_and_address():
 	frappe.db.sql('delete from tabContact')
 	frappe.db.sql('delete from `tabContact Email`')
@@ -81,43 +61,6 @@
 	contact_two.insert()
 
 
-@contextmanager
-def change_settings(doctype, settings_dict):
-	""" A context manager to ensure that settings are changed before running
-	function and restored after running it regardless of exceptions occured.
-	This is useful in tests where you want to make changes in a function but
-	don't retain those changes.
-	import and use as decorator to cover full function or using `with` statement.
-
-	example:
-	@change_settings("Stock Settings", {"item_naming_by": "Naming Series"})
-	def test_case(self):
-		...
-	"""
-
-	try:
-		settings = frappe.get_doc(doctype)
-		# remember setting
-		previous_settings = copy.deepcopy(settings_dict)
-		for key in previous_settings:
-			previous_settings[key] = getattr(settings, key)
-
-		# change setting
-		for key, value in settings_dict.items():
-			setattr(settings, key, value)
-		settings.save()
-		# singles are cached by default, clear to avoid flake
-		frappe.db.value_cache[settings] = {}
-		yield # yield control to calling function
-
-	finally:
-		# restore settings
-		settings = frappe.get_doc(doctype)
-		for key, value in previous_settings.items():
-			setattr(settings, key, value)
-		settings.save()
-
-
 def execute_script_report(
 		report_name: ReportName,
 		module: str,
@@ -157,24 +100,3 @@
 		except Exception:
 			print(f"Report failed to execute with filters: {test_filter}")
 			raise
-
-
-
-def timeout(seconds=30, error_message="Test timed out."):
-	""" Timeout decorator to ensure a test doesn't run for too long.
-
-		adapted from https://stackoverflow.com/a/2282656"""
-	def decorator(func):
-		def _handle_timeout(signum, frame):
-			raise Exception(error_message)
-
-		def wrapper(*args, **kwargs):
-			signal.signal(signal.SIGALRM, _handle_timeout)
-			signal.alarm(seconds)
-			try:
-				result = func(*args, **kwargs)
-			finally:
-				signal.alarm(0)
-			return result
-		return wrapper
-	return decorator