Merge branch 'develop' into item-dashboard-fixes
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..399b176
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,32 @@
+[flake8]
+ignore =
+    E121,
+    E126,
+    E127,
+    E128,
+    E203,
+    E225,
+    E226,
+    E231,
+    E241,
+    E251,
+    E261,
+    E265,
+    E302,
+    E303,
+    E305,
+    E402,
+    E501,
+    E741,
+    W291,
+    W292,
+    W293,
+    W391,
+    W503,
+    W504,
+    F403,
+    B007,
+    B950,
+    W191,
+
+max-line-length = 200
\ No newline at end of file
diff --git a/.github/helper/install.sh b/.github/helper/install.sh
index 253ad70..7b0f944 100644
--- a/.github/helper/install.sh
+++ b/.github/helper/install.sh
@@ -12,7 +12,7 @@
 
 pip install frappe-bench
 
-git clone https://github.com/frappe/frappe --branch "${GITHUB_BASE_REF}" --depth 1
+git clone https://github.com/frappe/frappe --branch "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}" --depth 1
 bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench
 
 mkdir ~/frappe-bench/sites/test_site
@@ -43,4 +43,4 @@
 
 bench get-app erpnext "${GITHUB_WORKSPACE}"
 bench start &
-bench --site test_site reinstall --yes
\ No newline at end of file
+bench --site test_site reinstall --yes
diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml
index 2a1db14..78c2f5a 100644
--- a/.github/workflows/ci-tests.yml
+++ b/.github/workflows/ci-tests.yml
@@ -1,12 +1,10 @@
 name: CI
 
-on:
-  pull_request:
-  workflow_dispatch:
+on: [pull_request, workflow_dispatch, push]
 
 jobs:
   test:
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-18.04
 
     strategy:
       fail-fast: false
diff --git a/README.md b/README.md
index 15782a2..bb592ae 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
         <p>ERP made simple</p>
     </p>
 
-[![Build Status](https://api.travis-ci.com/frappe/erpnext.svg?branch=develop)](https://travis-ci.com/frappe/erpnext)
+[![CI](https://github.com/frappe/erpnext/actions/workflows/ci-tests.yml/badge.svg?branch=develop)](https://github.com/frappe/erpnext/actions/workflows/ci-tests.yml)
 [![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
 [![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop)
 
diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
index 022d7a7..10cd939 100644
--- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
@@ -11,36 +11,36 @@
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 
 class TestAccountingPeriod(unittest.TestCase):
-    def test_overlap(self):
-        ap1 = create_accounting_period(start_date = "2018-04-01",
-            end_date = "2018-06-30", company = "Wind Power LLC")
-        ap1.save()
+	def test_overlap(self):
+		ap1 = create_accounting_period(start_date = "2018-04-01",
+			end_date = "2018-06-30", company = "Wind Power LLC")
+		ap1.save()
 
-        ap2 = create_accounting_period(start_date = "2018-06-30",
-            end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
-        self.assertRaises(OverlapError, ap2.save)
+		ap2 = create_accounting_period(start_date = "2018-06-30",
+			end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
+		self.assertRaises(OverlapError, ap2.save)
 
-    def test_accounting_period(self):
-        ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
-        ap1.save()
+	def test_accounting_period(self):
+		ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
+		ap1.save()
 
-        doc = create_sales_invoice(do_not_submit=1, cost_center = "_Test Company - _TC", warehouse = "Stores - _TC")
-        self.assertRaises(ClosedAccountingPeriod, doc.submit)
+		doc = create_sales_invoice(do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
+		self.assertRaises(ClosedAccountingPeriod, doc.submit)
 
-    def tearDown(self):
-        for d in frappe.get_all("Accounting Period"):
-            frappe.delete_doc("Accounting Period", d.name)
+	def tearDown(self):
+		for d in frappe.get_all("Accounting Period"):
+			frappe.delete_doc("Accounting Period", d.name)
 
 def create_accounting_period(**args):
-    args = frappe._dict(args)
+	args = frappe._dict(args)
 
-    accounting_period = frappe.new_doc("Accounting Period")
-    accounting_period.start_date = args.start_date or nowdate()
-    accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
-    accounting_period.company = args.company or "_Test Company"
-    accounting_period.period_name =args.period_name or  "_Test_Period_Name_1"
-    accounting_period.append("closed_documents", {
-        "document_type": 'Sales Invoice', "closed": 1
-    })
+	accounting_period = frappe.new_doc("Accounting Period")
+	accounting_period.start_date = args.start_date or nowdate()
+	accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
+	accounting_period.company = args.company or "_Test Company"
+	accounting_period.period_name =args.period_name or  "_Test_Period_Name_1"
+	accounting_period.append("closed_documents", {
+		"document_type": 'Sales Invoice', "closed": 1
+	})
 
-    return accounting_period
\ No newline at end of file
+	return accounting_period
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 bdfe532..8d6de2d 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
@@ -6,10 +6,12 @@
 import frappe
 import unittest
 
-test_dependencies = ["Customer", "Supplier"]
+from frappe.cache_manager import clear_doctype_cache
 from frappe.custom.doctype.property_setter.property_setter import make_property_setter
 from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
 
+test_dependencies = ["Customer", "Supplier"]
+
 class TestOpeningInvoiceCreationTool(unittest.TestCase):
 	def setUp(self):
 		if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
@@ -24,22 +26,25 @@
 
 	def test_opening_sales_invoice_creation(self):
 		property_setter = make_property_setter("Sales Invoice", "update_stock", "default", 1, "Check")
-		invoices = self.make_invoices(company="_Test Opening Invoice Company")
+		try:
+			invoices = self.make_invoices(company="_Test Opening Invoice Company")
 
-		self.assertEqual(len(invoices), 2)
-		expected_value = {
-			"keys": ["customer", "outstanding_amount", "status"],
-			0: ["_Test Customer", 300, "Overdue"],
-			1: ["_Test Customer 1", 250, "Overdue"],
-		}
-		self.check_expected_values(invoices, expected_value)
+			self.assertEqual(len(invoices), 2)
+			expected_value = {
+				"keys": ["customer", "outstanding_amount", "status"],
+				0: ["_Test Customer", 300, "Overdue"],
+				1: ["_Test Customer 1", 250, "Overdue"],
+			}
+			self.check_expected_values(invoices, expected_value)
 
-		si = frappe.get_doc("Sales Invoice", invoices[0])
+			si = frappe.get_doc("Sales Invoice", invoices[0])
 
-		# Check if update stock is not enabled
-		self.assertEqual(si.update_stock, 0)
+			# Check if update stock is not enabled
+			self.assertEqual(si.update_stock, 0)
 
-		property_setter.delete()
+		finally:
+			property_setter.delete()
+			clear_doctype_cache("Sales Invoice")
 
 	def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
 		doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
@@ -143,4 +148,4 @@
 		customer.insert(ignore_permissions=True)
 		return customer.name
 	else:
-		return frappe.db.exists("Customer", customer_name)
\ No newline at end of file
+		return frappe.db.exists("Customer", customer_name)
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 6412772..b5f6a40 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -605,12 +605,22 @@
 			{fieldtype:"Column Break"},
 			{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
 			{fieldtype:"Section Break"},
+			{fieldtype:"Link", label:__("Cost Center"), fieldname:"cost_center", options:"Cost Center",
+                "get_query": function() {
+                    return {
+                        "filters": {"company": frm.doc.company}
+					}
+				}
+			},
+			{fieldtype:"Column Break"},
+			{fieldtype:"Section Break"},
 			{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
 		];
 
 		frappe.prompt(fields, function(filters){
 			frappe.flags.allocate_payment_amount = true;
 			frm.events.validate_filters_data(frm, filters);
+			frm.doc.cost_center = filters.cost_center;
 			frm.events.get_outstanding_documents(frm, filters);
 		}, __("Filters"), __("Get Outstanding Documents"));
 	},
@@ -1066,11 +1076,6 @@
 								frm.set_value("paid_from_account_balance", r.message.paid_from_account_balance);
 								frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance);
 								frm.set_value("party_balance", r.message.party_balance);
-							},
-							() => {
-								if(frm.doc.payment_type != "Internal") {
-									frm.clear_table("references");
-								}
 							}
 						]);
 
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index eb52fd6..054afe5 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -9,8 +9,16 @@
 from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.stock.doctype.item.test_item import make_item
 
 class TestPOSInvoice(unittest.TestCase):
+	def tearDown(self):
+		if frappe.session.user != "Administrator":
+			frappe.set_user("Administrator")
+
+		if frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
+			frappe.db.set_value("Selling Settings", None, "validate_selling_price", 0)
+
 	def test_timestamp_change(self):
 		w = create_pos_invoice(do_not_save=1)
 		w.docstatus = 0
@@ -370,7 +378,6 @@
 		pos_inv.load_from_db()
 		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
 		self.assertEqual(rounded_total, 3470)
-		frappe.set_user("Administrator")
 
 	def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
 		from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
@@ -412,7 +419,6 @@
 		pos_inv.load_from_db()
 		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
 		self.assertEqual(rounded_total, 840)
-		frappe.set_user("Administrator")
 
 	def test_merging_with_validate_selling_price(self):
 		from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
@@ -421,10 +427,12 @@
 		if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
 			frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 1)
 
-		make_purchase_receipt(item_code="_Test Item", warehouse="_Test Warehouse - _TC", qty=1, rate=300)
+		item = "Test Selling Price Validation"
+		make_item(item, {"is_stock_item": 1})
+		make_purchase_receipt(item_code=item, warehouse="_Test Warehouse - _TC", qty=1, rate=300)
 		frappe.db.sql("delete from `tabPOS Invoice`")
 		test_user, pos_profile = init_user_and_profile()
-		pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
+		pos_inv = create_pos_invoice(item=item, rate=300, do_not_submit=1)
 		pos_inv.append('payments', {
 			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
 		})
@@ -438,7 +446,7 @@
 		})
 		self.assertRaises(frappe.ValidationError, pos_inv.submit)
 
-		pos_inv2 = create_pos_invoice(rate=400, do_not_submit=1)
+		pos_inv2 = create_pos_invoice(item=item, rate=400, do_not_submit=1)
 		pos_inv2.append('payments', {
 			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 400
 		})
@@ -457,8 +465,6 @@
 		pos_inv2.load_from_db()
 		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
 		self.assertEqual(rounded_total, 400)
-		frappe.set_user("Administrator")
-		frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 0)
 
 def create_pos_invoice(**args):
 	args = frappe._dict(args)
@@ -508,4 +514,4 @@
 	else:
 		pos_inv.payment_schedule = []
 
-	return pos_inv
\ No newline at end of file
+	return pos_inv
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 06aa20b..66a8e20 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -524,7 +524,7 @@
 	},
 
 	onload: function(frm) {
-		if(frm.doc.__onload) {
+		if(frm.doc.__onload && frm.is_new()) {
 			if(frm.doc.supplier) {
 				frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0;
 			}
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 4076be7..a1bf66b 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -76,7 +76,7 @@
 
 		if not self.is_pos:
 			self.so_dn_required()
-		
+
 		self.set_tax_withholding()
 
 		self.validate_proj_cust()
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 1b95578..90e2144 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1800,6 +1800,15 @@
 		si.selling_price_list = "_Test Price List Rest of the World"
 		si.update_stock = 1
 		si.items[0].target_warehouse = 'Work In Progress - TCP1'
+
+		# Add stock to stores for succesful stock transfer
+		make_stock_entry(
+			target="Stores - TCP1",
+			company = "_Test Company with perpetual inventory",
+			qty=1,
+			basic_rate=100
+		)
+
 		add_taxes(si)
 		si.save()
 
@@ -2269,4 +2278,4 @@
 		"cost_center": "Main - TCP1",
 		"description": "Excise Duty",
 		"rate": 12
-	})
\ 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 429a9f3..52d19d5 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
@@ -46,5 +46,5 @@
 		frappe.throw(_("Disabled template must not be default template"))
 
 def validate_for_tax_category(doc):
-	if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0}):
+	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/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index 9ce8e3f..dd3b49a 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -177,7 +177,7 @@
 
 	for d in purchase_invoices:
 		frappe.get_doc('Purchase Invoice', d).cancel()
-	
+
 	for d in sales_invoices:
 		frappe.get_doc('Sales Invoice', d).cancel()
 
@@ -229,7 +229,8 @@
 			'qty': args.qty or 1,
 			'rate': args.rate or 10000,
 			'cost_center': 'Main - _TC',
-			'expense_account': 'Cost of Goods Sold - _TC'
+			'expense_account': 'Cost of Goods Sold - _TC',
+			'warehouse': args.warehouse or '_Test Warehouse - _TC'
 		}]
 	})
 
@@ -353,4 +354,4 @@
 				'company': '_Test Company',
 				'account': 'TDS - _TC'
 			}]
-		}).insert()
\ No newline at end of file
+		}).insert()
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 7dfce85..14efa1f 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -51,7 +51,11 @@
 			"from_date": start_date
 		})
 
-		to_date = add_months(start_date, months_to_add)
+		if i==0 and filter_based_on == 'Date Range':
+			to_date = add_months(get_first_day(start_date), months_to_add)
+		else:
+			to_date = add_months(start_date, months_to_add)
+
 		start_date = to_date
 
 		# Subtract one day from to_date, as it may be first day in next fiscal year or month
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 02d4865..604c886 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -723,7 +723,7 @@
 			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
 
 		make_stock_entry(target="_Test Warehouse - _TC",
-			item_code="_Test Item Home Desktop 100", qty=10, basic_rate=100)
+			item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100)
 		make_stock_entry(target="_Test Warehouse - _TC",
 			item_code = "Test Extra Item 1", qty=100, basic_rate=100)
 		make_stock_entry(target="_Test Warehouse - _TC",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 12a81c7..e8f27c3 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -829,10 +829,10 @@
 				party_account_currency = get_party_account_currency(party_type, party, self.company)
 
 				if (party_account_currency
-						and party_account_currency != self.company_currency
-						and self.currency != party_account_currency):
+						and (self.currency != party_account_currency
+						and self.currency != self.company_currency)):
 					frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
-								 .format(party_type, party, party_account_currency), InvalidCurrency)
+								 .format(party_type, party, frappe.bold(party_account_currency), InvalidCurrency))
 
 				# Note: not validating with gle account because we don't have the account
 				# at quotation / sales order level and we shouldn't stop someone
@@ -898,7 +898,7 @@
 		date = self.get("due_date")
 		due_date = date or posting_date
 
-		if party_account_currency == self.company_currency:
+		if self.company_currency == self.currency:
 			grand_total = self.get("base_rounded_total") or self.base_grand_total
 		else:
 			grand_total = self.get("rounded_total") or self.grand_total
@@ -959,7 +959,7 @@
 			for d in self.get("payment_schedule"):
 				total += flt(d.payment_amount)
 
-			if party_account_currency == self.company_currency:
+			if self.company_currency == self.currency:
 				total = flt(total, self.precision("base_grand_total"))
 				grand_total = flt(self.get("base_rounded_total") or self.base_grand_total, self.precision('base_grand_total'))
 			else:
@@ -1394,7 +1394,7 @@
 			)
 
 	def get_new_child_item(item_row):
-		child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item" 
+		child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item"
 		return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row)
 
 	def validate_quantity(child_item, d):
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index fb52c1f..edc40c4 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -144,7 +144,7 @@
 
 			if sales_person.commission_rate:
 				sales_person.incentives = flt(
-					sales_person.allocated_amount * flt(sales_person.commission_rate) / 100.0, 
+					sales_person.allocated_amount * flt(sales_person.commission_rate) / 100.0,
 					self.precision("incentives", sales_person))
 
 			total += sales_person.allocated_percentage
@@ -502,4 +502,4 @@
 	for d in obj.get("items"):
 		if d.item_code:
 			if getattr(d, "income_account", None):
-				set_item_default(d.item_code, obj.company, 'income_account', d.income_account)
\ No newline at end of file
+				set_item_default(d.item_code, obj.company, 'income_account', d.income_account)
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index aab5770..c33e556 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -795,7 +795,7 @@
 		for d in self.doc.get(self.tax_field):
 			if d.account_currency == company_currency:
 				d.exchange_rate = 1
-			elif not d.exchange_rate or d.exchange_rate == 1 or self.doc.posting_date:
+			elif not d.exchange_rate:
 				d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
 					account_currency=d.account_currency, company=self.doc.company)
 
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
index 74ad456..5f471ab 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
@@ -5,7 +5,7 @@
 import frappe
 
 import unittest, os, json
-from frappe.utils import cstr
+from frappe.utils import cstr, cint
 from erpnext.erpnext_integrations.connectors.shopify_connection import create_order
 from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import make_item
 from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer
@@ -13,9 +13,14 @@
 
 
 class ShopifySettings(unittest.TestCase):
-	def setUp(self):
+	@classmethod
+	def setUpClass(cls):
 		frappe.set_user("Administrator")
 
+		cls.allow_negative_stock = cint(frappe.db.get_value('Stock Settings', None, 'allow_negative_stock'))
+		if not cls.allow_negative_stock:
+			frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1)
+
 		# use the fixture data
 		import_doc(frappe.get_app_path("erpnext", "erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json"))
 
@@ -24,9 +29,15 @@
 		frappe.reload_doctype("Delivery Note")
 		frappe.reload_doctype("Sales Invoice")
 
-		self.setup_shopify()
+		cls.setup_shopify()
 
-	def setup_shopify(self):
+	@classmethod
+	def tearDownClass(cls):
+		if not cls.allow_negative_stock:
+			frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
+
+	@classmethod
+	def setup_shopify(cls):
 		shopify_settings = frappe.get_doc("Shopify Settings")
 		shopify_settings.taxes = []
 
@@ -56,21 +67,20 @@
 			"delivery_note_series": "DN-"
 		}).save(ignore_permissions=True)
 
-		self.shopify_settings = shopify_settings
+		cls.shopify_settings = shopify_settings
 
 	def test_order(self):
-		### Create Customer ###
+		# Create Customer
 		with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_customer.json")) as shopify_customer:
 			shopify_customer = json.load(shopify_customer)
 		create_customer(shopify_customer.get("customer"), self.shopify_settings)
 
-		### Create Item ###
+		# Create Item
 		with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_item.json")) as shopify_item:
 			shopify_item = json.load(shopify_item)
 		make_item("_Test Warehouse - _TC", shopify_item.get("product"))
 
-
-		### Create Order ###
+		# Create Order
 		with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_order.json")) as shopify_order:
 			shopify_order = json.load(shopify_order)
 
@@ -80,17 +90,17 @@
 
 		self.assertEqual(cstr(shopify_order.get("order").get("id")), sales_order.shopify_order_id)
 
-		#check for customer
+		# Check for customer
 		shopify_order_customer_id = cstr(shopify_order.get("order").get("customer").get("id"))
 		sales_order_customer_id = frappe.get_value("Customer", sales_order.customer, "shopify_customer_id")
 
 		self.assertEqual(shopify_order_customer_id, sales_order_customer_id)
 
-		#check sales invoice
+		# Check sales invoice
 		sales_invoice = frappe.get_doc("Sales Invoice", {"shopify_order_id": sales_order.shopify_order_id})
 		self.assertEqual(sales_invoice.rounded_total, sales_order.rounded_total)
 
-		#check delivery note
+		# Check delivery note
 		delivery_note_count = frappe.db.sql("""select count(*) from `tabDelivery Note`
 			where shopify_order_id = %s""", sales_order.shopify_order_id)[0][0]
 
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
index 5ced845..aaf0e85 100644
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
+++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
@@ -53,7 +53,7 @@
   "discharge_ordered_date",
   "discharge_practitioner",
   "discharge_encounter",
-  "discharge_date",
+  "discharge_datetime",
   "cb_discharge",
   "discharge_instructions",
   "followup_date",
@@ -404,14 +404,15 @@
    "permlevel": 1
   },
   {
-   "fieldname": "discharge_date",
-   "fieldtype": "Date",
+   "fieldname": "discharge_datetime",
+   "fieldtype": "Datetime",
    "label": "Discharge Date",
    "read_only": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-05-21 02:26:22.144575",
+ "modified": "2021-03-18 14:44:11.689956",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Inpatient Record",
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
index 88d7f0b..2934316 100644
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
+++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
@@ -151,7 +151,7 @@
 
 def discharge_patient(inpatient_record):
 	validate_inpatient_invoicing(inpatient_record)
-	inpatient_record.discharge_date = today()
+	inpatient_record.discharge_datetime = now_datetime()
 	inpatient_record.status = "Discharged"
 
 	inpatient_record.save(ignore_permissions = True)
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 4b3597a..9b9a0da 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -324,6 +324,7 @@
 		"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
 		"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
 		"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
+		"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries"
 	],
 	"daily": [
 		"erpnext.stock.reorder_item.reorder_item",
diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.py b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
index 92b1eae..3c42bd9 100644
--- a/erpnext/hr/doctype/attendance_request/test_attendance_request.py
+++ b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
@@ -8,6 +8,8 @@
 from frappe.utils import nowdate
 from datetime import date
 
+test_dependencies = ["Employee"]
+
 class TestAttendanceRequest(unittest.TestCase):
 	def setUp(self):
 		for doctype in ["Attendance Request", "Attendance"]:
@@ -56,4 +58,4 @@
 		self.assertEqual(attendance.docstatus, 2)
 
 def get_employee():
-	return frappe.get_doc("Employee", "_T-Employee-00001")
\ No newline at end of file
+	return frappe.get_doc("Employee", "_T-Employee-00001")
diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
index 1615ab3..74ce301 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
+++ b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
@@ -10,6 +10,8 @@
 from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
 from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
 
+test_dependencies = ["Employee"]
+
 class TestCompensatoryLeaveRequest(unittest.TestCase):
 	def setUp(self):
 		frappe.db.sql(''' delete from `tabCompensatory Leave Request`''')
@@ -129,4 +131,4 @@
 		],
 		"holiday_list_name": "_Test Compensatory Leave"
 	})
-	holiday_list.save()
\ No newline at end of file
+	holiday_list.save()
diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.py b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
index 6d275c8..8728342 100644
--- a/erpnext/hr/doctype/job_applicant/test_job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
@@ -13,11 +13,21 @@
 
 def create_job_applicant(**args):
 	args = frappe._dict(args)
-	job_applicant = frappe.get_doc({
-		"doctype": "Job Applicant",
+
+	filters = {
 		"applicant_name": args.applicant_name or "_Test Applicant",
 		"email_id": args.email_id or "test_applicant@example.com",
+	}
+
+	if frappe.db.exists("Job Applicant", filters):
+		return frappe.get_doc("Job Applicant", filters)
+
+	job_applicant = frappe.get_doc({
+		"doctype": "Job Applicant",
 		"status": args.status or "Open"
 	})
+
+	job_applicant.update(filters)
 	job_applicant.save()
-	return job_applicant
\ No newline at end of file
+
+	return job_applicant
diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py
index 8886596..690a692 100644
--- a/erpnext/hr/doctype/job_offer/test_job_offer.py
+++ b/erpnext/hr/doctype/job_offer/test_job_offer.py
@@ -13,14 +13,15 @@
 
 class TestJobOffer(unittest.TestCase):
 	def test_job_offer_creation_against_vacancies(self):
-		create_staffing_plan(staffing_details=[{
-			"designation": "Designer",
+		frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
+		job_applicant = create_job_applicant(email_id="test_job_offer@example.com")
+		job_offer = create_job_offer(job_applicant=job_applicant.name, designation="UX Designer")
+
+		create_staffing_plan(name='Test No Vacancies', staffing_details=[{
+			"designation": "UX Designer",
 			"vacancies": 0,
 			"estimated_cost_per_position": 5000
 		}])
-		frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
-		job_applicant = create_job_applicant(email_id="test_job_offer@example.com")
-		job_offer = create_job_offer(job_applicant=job_applicant.name, designation="Researcher")
 		self.assertRaises(frappe.ValidationError, job_offer.submit)
 
 		# test creation of job offer when vacancies are not present
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
index 26f077a..0b71036 100644
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
@@ -6,6 +6,10 @@
 from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation, expire_allocation
 
 class TestLeaveAllocation(unittest.TestCase):
+	@classmethod
+	def setUpClass(cls):
+		frappe.db.sql("delete from `tabLeave Period`")
+
 	def test_overlapping_allocation(self):
 		frappe.db.sql("delete from `tabLeave Allocation`")
 
@@ -177,4 +181,4 @@
 	})
 	return leave_allocation
 
-test_dependencies = ["Employee", "Leave Type"]
\ No newline at end of file
+test_dependencies = ["Employee", "Leave Type"]
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py
index 53b7a39..b335c48 100644
--- a/erpnext/hr/doctype/leave_application/test_leave_application.py
+++ b/erpnext/hr/doctype/leave_application/test_leave_application.py
@@ -12,7 +12,7 @@
 from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
 from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
 
-test_dependencies = ["Leave Allocation", "Leave Block List"]
+test_dependencies = ["Leave Allocation", "Leave Block List", "Employee"]
 
 _test_records = [
  {
@@ -639,4 +639,4 @@
 		"docstatus": 1
 	}).insert()
 
-	allocate_leave.submit()
\ No newline at end of file
+	allocate_leave.submit()
diff --git a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py
index c7bc6fb..838e794 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py
+++ b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py
@@ -9,6 +9,8 @@
 from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
 from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy
 
+test_dependencies = ["Employee"]
+
 class TestLeavePolicyAssignment(unittest.TestCase):
 
 	def setUp(self):
diff --git a/erpnext/hr/doctype/shift_request/test_shift_request.py b/erpnext/hr/doctype/shift_request/test_shift_request.py
index 3dcfcbf..230bb2b 100644
--- a/erpnext/hr/doctype/shift_request/test_shift_request.py
+++ b/erpnext/hr/doctype/shift_request/test_shift_request.py
@@ -7,6 +7,8 @@
 import unittest
 from frappe.utils import nowdate, add_days
 
+test_dependencies = ["Shift Type"]
+
 class TestShiftRequest(unittest.TestCase):
 	def setUp(self):
 		for doctype in ["Shift Request", "Shift Assignment"]:
@@ -46,4 +48,4 @@
 	department_doc = frappe.get_doc("Department", department)
 	department_doc.append('shift_request_approver',{'approver': "test1@example.com"})
 	department_doc.save()
-	department_doc.reload()
\ No newline at end of file
+	department_doc.reload()
diff --git a/erpnext/hr/doctype/shift_type/test_records.json b/erpnext/hr/doctype/shift_type/test_records.json
new file mode 100644
index 0000000..9040b91
--- /dev/null
+++ b/erpnext/hr/doctype/shift_type/test_records.json
@@ -0,0 +1,8 @@
+[
+  {
+    "doctype": "Shift Type",
+    "name": "Day Shift",
+    "start_time": "9:00:00",
+    "end_time": "18:00:00"
+  }
+]
diff --git a/erpnext/hr/doctype/shift_type/test_shift_type.py b/erpnext/hr/doctype/shift_type/test_shift_type.py
index 535072a..bc4f0ea 100644
--- a/erpnext/hr/doctype/shift_type/test_shift_type.py
+++ b/erpnext/hr/doctype/shift_type/test_shift_type.py
@@ -7,14 +7,4 @@
 import unittest
 
 class TestShiftType(unittest.TestCase):
-	def test_make_shift_type(self):
-		if frappe.db.exists("Shift Type", "Day Shift"):
-			return
-		shift_type = frappe.get_doc({
-			"doctype": "Shift Type",
-			"name": "Day Shift",
-			"start_time": "9:00:00",
-			"end_time": "18:00:00"
-		})
-		shift_type.insert()
- 
\ No newline at end of file
+	pass
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index 5b84d00..533149a 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -39,6 +39,7 @@
 			detail.current_count = designation_counts['employee_count']
 			detail.current_openings = designation_counts['job_openings']
 
+			detail.total_estimated_cost = 0
 			if detail.number_of_positions > 0:
 				if detail.vacancies > 0 and detail.estimated_cost_per_position:
 					detail.total_estimated_cost = cint(detail.vacancies) * flt(detail.estimated_cost_per_position)
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 13a2094..4b9a894 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -275,6 +275,11 @@
 		frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
 			where loan_security='Test Security 2'""")
 
+		create_process_loan_security_shortfall()
+		loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
+		self.assertEquals(loan_security_shortfall.status, "Completed")
+		self.assertEquals(loan_security_shortfall.shortfall_amount, 0)
+
 	def test_loan_security_unpledge(self):
 		pledge = [{
 			"loan_security": "Test Security 1",
diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
index 6469806..b5e7898 100644
--- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
@@ -55,6 +55,9 @@
 		'total_interest_payable', 'disbursed_amount', 'status'],
 		filters={'status': ('in',['Disbursed','Partially Disbursed']), 'is_secured_loan': 1})
 
+	loan_shortfall_map = frappe._dict(frappe.get_all("Loan Security Shortfall",
+		fields=["loan", "name"], filters={"status": "Pending"}, as_list=1))
+
 	loan_security_map = {}
 
 	for loan in loans:
@@ -71,14 +74,19 @@
 		for security, qty in pledged_securities.items():
 			if not ltv_ratio:
 				ltv_ratio = get_ltv_ratio(security)
-			security_value += loan_security_price_map.get(security) * qty
+			security_value += flt(loan_security_price_map.get(security)) * flt(qty)
 
-		current_ratio = (outstanding_amount/security_value) * 100
+		current_ratio = (outstanding_amount/security_value) * 100 if security_value else 0
 
 		if current_ratio > ltv_ratio:
 			shortfall_amount = outstanding_amount - ((security_value * ltv_ratio) / 100)
 			create_loan_security_shortfall(loan.name, outstanding_amount, security_value, shortfall_amount,
 				process_loan_security_shortfall)
+		elif loan_shortfall_map.get(loan.name):
+			shortfall_amount = outstanding_amount - ((security_value * ltv_ratio) / 100)
+			if shortfall_amount <= 0:
+				shortfall = loan_shortfall_map.get(loan.name)
+				update_pending_shortfall(shortfall)
 
 def create_loan_security_shortfall(loan, loan_amount, security_value, shortfall_amount, process_loan_security_shortfall):
 	existing_shortfall = frappe.db.get_value("Loan Security Shortfall", {"loan": loan, "status": "Pending"}, "name")
@@ -101,3 +109,11 @@
 	ltv_ratio = frappe.db.get_value('Loan Security Type', loan_security_type, 'loan_to_value_ratio')
 	return ltv_ratio
 
+def update_pending_shortfall(shortfall):
+	# Get all pending loan security shortfall
+	frappe.db.set_value("Loan Security Shortfall", shortfall,
+		{
+			"status": "Completed",
+			"shortfall_amount": 0
+		})
+
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 662a06b..7aaf2a0 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -255,6 +255,9 @@
 				data.actual_operation_time = time_in_mins
 				data.actual_start_time = time_data[0].start_time if time_data else None
 				data.actual_end_time = time_data[0].end_time if time_data else None
+				if data.get("workstation") != self.workstation:
+					# workstations can change in a job card
+					data.workstation = self.workstation
 
 		wo.flags.ignore_validate_update_after_submit = True
 		wo.update_operation_status()
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index 585a09d..cd9edee 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -333,8 +333,7 @@
    "fieldname": "operations",
    "fieldtype": "Table",
    "label": "Operations",
-   "options": "Work Order Operation",
-   "read_only": 1
+   "options": "Work Order Operation"
   },
   {
    "depends_on": "operations",
@@ -496,7 +495,7 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2020-05-05 19:32:43.323054",
+ "modified": "2021-03-16 13:27:51.116484",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order",
diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
index 2ca9f16..fc27d35 100644
--- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
+++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
@@ -61,7 +61,7 @@
 
 		from_date = add_years(self.filters.from_date, cint(self.filters.no_of_years) * -1)
 		self.period_list = get_period_list(from_date, self.filters.to_date,
-			from_date, self.filters.to_date, None, self.filters.periodicity, ignore_fiscal_year=True)
+			from_date, self.filters.to_date, "Date Range", self.filters.periodicity, ignore_fiscal_year=True)
 
 		order_data = self.get_data_for_forecast() or []
 
diff --git a/erpnext/non_profit/doctype/donation/donation.py b/erpnext/non_profit/doctype/donation/donation.py
index e947588..6a2a06d 100644
--- a/erpnext/non_profit/doctype/donation/donation.py
+++ b/erpnext/non_profit/doctype/donation/donation.py
@@ -91,6 +91,10 @@
 		if not data.event == 'payment.captured':
 			return
 
+		# to avoid capturing subscription payments as donations
+		if payment.description and 'subscription' in str(payment.description).lower():
+			return
+
 		donor = get_donor(payment.email)
 		if not donor:
 			donor = create_donor(payment)
@@ -119,7 +123,7 @@
 		'donor_name': donor.donor_name,
 		'email': donor.email,
 		'date': getdate(),
-		'amount': flt(payment.amount),
+		'amount': flt(payment.amount) / 100, # Convert to rupees from paise
 		'mode_of_payment': payment.method,
 		'razorpay_payment_id': payment.id
 	}).insert(ignore_mandatory=True)
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index 191281f..52447e4 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -48,7 +48,7 @@
 		last_membership = erpnext.get_last_membership(self.member)
 
 		# if person applied for offline membership
-		if last_membership and not frappe.session.user == "Administrator":
+		if last_membership and last_membership.name != self.name and not frappe.session.user == "Administrator":
 			# if last membership does not expire in 30 days, then do not allow to renew
 			if getdate(add_days(last_membership.to_date, -30)) > getdate(nowdate()) :
 				frappe.throw(_("You can only renew if your membership expires within 30 days"))
@@ -90,6 +90,7 @@
 		self.validate_membership_type_and_settings(plan, settings)
 
 		invoice = make_invoice(self, member, plan, settings)
+		self.reload()
 		self.invoice = invoice.name
 
 		if with_payment_entry:
@@ -284,10 +285,11 @@
 
 		settings = frappe.get_doc("Non Profit Settings")
 		if settings.allow_invoicing and settings.automate_membership_invoicing:
+			membership.reload()
 			membership.generate_invoice(with_payment_entry=settings.automate_membership_payment_entries, save=True)
 
 	except Exception as e:
-		message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), __("Payment ID"), payment.id)
+		message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), _("Payment ID"), payment.id)
 		log = frappe.log_error(message, _("Error creating membership entry for {0}").format(member.name))
 		notify_failure(log)
 		return { "status": "Failed", "reason": e}
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 7016ecd..3675184 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -760,3 +760,4 @@
 erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
 erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
 execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
+erpnext.patches.v13_0.rename_discharge_date_in_ip_record
diff --git a/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py b/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py
new file mode 100644
index 0000000..491dc82
--- /dev/null
+++ b/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+	frappe.reload_doc("Healthcare", "doctype", "Inpatient Record")
+	if frappe.db.has_column("Inpatient Record", "discharge_date"):
+		rename_field("Inpatient Record", "discharge_date", "discharge_datetime")
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
index f5af677..13b6c05 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -9,17 +9,10 @@
 from frappe.utils import getdate, date_diff, comma_and, formatdate
 
 class AdditionalSalary(Document):
-
 	def on_submit(self):
 		if self.ref_doctype == "Employee Advance" and self.ref_docname:
 			frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", self.amount)
 
-	def before_insert(self):
-		if frappe.db.exists("Additional Salary", {"employee": self.employee, "salary_component": self.salary_component,
-			"amount": self.amount, "payroll_date": self.payroll_date, "company": self.company, "docstatus": 1}):
-
-			frappe.throw(_("Additional Salary Component Exists."))
-
 	def validate(self):
 		self.validate_dates()
 		self.validate_salary_structure()
@@ -89,10 +82,11 @@
 		no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
 		return amount_per_day * no_of_days
 
-@frappe.whitelist()
-def get_additional_salary_component(employee, start_date, end_date, component_type):
-	additional_salaries = frappe.db.sql("""
-		select name, salary_component, type, amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
+def get_additional_salaries(employee, start_date, end_date, component_type):
+	additional_salary_list = frappe.db.sql("""
+		select name, salary_component as component, type, amount,
+		overwrite_salary_structure_amount as overwrite,
+		deduct_full_tax_on_selected_payroll_date
 		from `tabAdditional Salary`
 		where employee=%(employee)s
 			and docstatus = 1
@@ -102,7 +96,7 @@
 					from_date <= %(to_date)s and to_date >= %(to_date)s
 				)
 		and type = %(component_type)s
-		order by salary_component, overwrite_salary_structure_amount DESC
+		order by salary_component, overwrite ASC
 	""", {
 		'employee': employee,
 		'from_date': start_date,
@@ -110,38 +104,18 @@
 		'component_type': "Earning" if component_type == "earnings" else "Deduction"
 	}, as_dict=1)
 
-	existing_salary_components= []
-	salary_components_details = {}
-	additional_salary_details = []
+	additional_salaries = []
+	components_to_overwrite = []
 
-	overwrites_components = [ele.salary_component for ele in additional_salaries if ele.overwrite_salary_structure_amount == 1]
+	for d in additional_salary_list:
+		if d.overwrite:
+			if d.component in components_to_overwrite:
+				frappe.throw(_("Multiple Additional Salaries with overwrite "
+					"property exist for Salary Component {0} between {1} and {2}.").format(
+					frappe.bold(d.component), start_date, end_date), title=_("Error"))
 
-	component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
-	for d in additional_salaries:
+			components_to_overwrite.append(d.component)
 
-		if d.salary_component not in existing_salary_components:
-			component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
-			struct_row = frappe._dict({'salary_component': d.salary_component})
-			if component:
-				struct_row.update(component[0])
+		additional_salaries.append(d)
 
-			struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
-			struct_row['is_additional_component'] = 1
-
-			salary_components_details[d.salary_component] = struct_row
-
-
-		if overwrites_components.count(d.salary_component) > 1:
-			frappe.throw(_("Multiple Additional Salaries with overwrite property exist for Salary Component: {0} between {1} and {2}.".format(d.salary_component, start_date, end_date)), title=_("Error"))
-		else:
-			additional_salary_details.append({
-				'name': d.name,
-				'component': d.salary_component,
-				'amount': d.amount,
-				'type': d.type,
-				'overwrite': d.overwrite_salary_structure_amount,
-			})
-
-		existing_salary_components.append(d.salary_component)
-
-	return salary_components_details, additional_salary_details
+	return additional_salaries
diff --git a/erpnext/payroll/doctype/gratuity/test_gratuity.py b/erpnext/payroll/doctype/gratuity/test_gratuity.py
index e89e3dd..7daea2d 100644
--- a/erpnext/payroll/doctype/gratuity/test_gratuity.py
+++ b/erpnext/payroll/doctype/gratuity/test_gratuity.py
@@ -15,9 +15,12 @@
 
 test_dependencies = ["Salary Component", "Salary Slip", "Account"]
 class TestGratuity(unittest.TestCase):
-	def setUp(self):
+	@classmethod
+	def setUpClass(cls):
 		make_earning_salary_component(setup=True, test_tax=True, company_list=['_Test Company'])
 		make_deduction_salary_component(setup=True, test_tax=True, company_list=['_Test Company'])
+
+	def setUp(self):
 		frappe.db.sql("DELETE FROM `tabGratuity`")
 		frappe.db.sql("DELETE FROM `tabAdditional Salary` WHERE ref_doctype = 'Gratuity'")
 
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
index e098ec7..84c3814 100644
--- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
@@ -12,7 +12,7 @@
 from erpnext.payroll.doctype.salary_slip.test_salary_slip import get_salary_component_account, \
 		make_earning_salary_component, make_deduction_salary_component, create_account, make_employee_salary_slip
 from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure, create_salary_structure_assignment
-from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry
+from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry, create_loan_type, create_loan_accounts
 from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
 
 class TestPayrollEntry(unittest.TestCase):
@@ -168,15 +168,23 @@
 		salary_structure = "Test Salary Structure for Loan"
 		make_salary_structure(salary_structure, "Monthly", employee=employee_doc.name, company="_Test Company", currency=company_doc.default_currency)
 
+		if not frappe.db.exists("Loan Type", "Car Loan"):
+			create_loan_accounts()
+			create_loan_type("Car Loan", 500000, 8.4,
+				is_term_loan=1,
+				mode_of_payment='Cash',
+				payment_account='Payment Account - _TC',
+				loan_account='Loan Account - _TC',
+				interest_income_account='Interest Income Account - _TC',
+				penalty_income_account='Penalty Income Account - _TC')
+
 		loan = create_loan(applicant, "Car Loan", 280000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
 		loan.repay_from_salary = 1
 		loan.submit()
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
-
 		process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
 
-
 		dates = get_start_end_dates('Monthly', nowdate())
 		make_payroll_entry(company="_Test Company", start_date=dates.start_date, payable_account=company_doc.default_payroll_payable_account,
 			currency=company_doc.default_currency, end_date=dates.end_date, branch=branch, cost_center="Main - _TC", payment_account="Cash - _TC")
@@ -267,4 +275,4 @@
 	salary_slip.calculate_net_pay()
 	salary_slip.db_update()
 
-	return salary_slip
\ No newline at end of file
+	return salary_slip
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index 595d697..a04a635 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -13,7 +13,7 @@
 from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
 from erpnext.utilities.transaction_base import TransactionBase
 from frappe.utils.background_jobs import enqueue
-from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salary_component
+from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries
 from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
 from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
 from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
@@ -524,7 +524,7 @@
 
 		except NameError as err:
 			frappe.throw(_("{0} <br> This error can be due to missing or deleted field.").format(err),
-			    title=_("Name error"))
+				title=_("Name error"))
 		except SyntaxError as err:
 			frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
 		except Exception as e:
@@ -558,15 +558,16 @@
 						self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
 
 	def add_additional_salary_components(self, component_type):
-		salary_components_details, additional_salary_details = get_additional_salary_component(self.employee,
+		additional_salaries = get_additional_salaries(self.employee,
 			self.start_date, self.end_date, component_type)
-		if salary_components_details and additional_salary_details:
-			for additional_salary in additional_salary_details:
-				additional_salary =frappe._dict(additional_salary)
-				amount = additional_salary.amount
-				overwrite = additional_salary.overwrite
-				self.update_component_row(frappe._dict(salary_components_details[additional_salary.component]), amount,
-					component_type, overwrite=overwrite, additional_salary=additional_salary.name)
+
+		for additional_salary in additional_salaries:
+			self.update_component_row(
+				get_salary_component_data(additional_salary.component),
+				additional_salary.amount,
+				component_type,
+				additional_salary
+			)
 
 	def add_tax_components(self, payroll_period):
 		# Calculate variable_based_on_taxable_salary after all components updated in salary slip
@@ -583,47 +584,59 @@
 
 		for d in tax_components:
 			tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period)
-			tax_row = self.get_salary_slip_row(d)
+			tax_row = get_salary_component_data(d)
 			self.update_component_row(tax_row, tax_amount, "deductions")
 
-	def update_component_row(self, struct_row, amount, key, overwrite=1, additional_salary = ''):
+	def update_component_row(self, component_data, amount, component_type, additional_salary=None):
 		component_row = None
-		for d in self.get(key):
-			if d.salary_component == struct_row.salary_component:
+		for d in self.get(component_type):
+			if d.salary_component != component_data.salary_component:
+				continue
+
+			if (
+				not d.additional_salary
+				and (not additional_salary or additional_salary.overwrite)
+				or additional_salary
+				and additional_salary.name == d.additional_salary
+			):
 				component_row = d
+				break
 
-		if not component_row or (struct_row.get("is_additional_component") and not overwrite):
-			if amount:
-				self.append(key, {
-					'amount': amount,
-					'default_amount': amount if not struct_row.get("is_additional_component") else 0,
-					'depends_on_payment_days' : struct_row.depends_on_payment_days,
-					'salary_component' : struct_row.salary_component,
-					'abbr' : struct_row.abbr or struct_row.get("salary_component_abbr"),
-					'additional_salary': additional_salary,
-					'do_not_include_in_total' : struct_row.do_not_include_in_total,
-					'is_tax_applicable': struct_row.is_tax_applicable,
-					'is_flexible_benefit': struct_row.is_flexible_benefit,
-					'variable_based_on_taxable_salary': struct_row.variable_based_on_taxable_salary,
-					'deduct_full_tax_on_selected_payroll_date': struct_row.deduct_full_tax_on_selected_payroll_date,
-					'additional_amount': amount if struct_row.get("is_additional_component") else 0,
-					'exempted_from_income_tax': struct_row.exempted_from_income_tax
-				})
+		if additional_salary and additional_salary.overwrite:
+			# Additional Salary with overwrite checked, remove default rows of same component
+			self.set(component_type, [
+				d for d in self.get(component_type)
+				if d.salary_component != component_data.salary_component
+				or d.additional_salary and additional_salary.name != d.additional_salary
+				or d == component_row
+			])
+
+		if not component_row:
+			if not amount:
+				return
+
+			component_row = self.append(component_type)
+			for attr in (
+				'depends_on_payment_days', 'salary_component', 'abbr'
+				'do_not_include_in_total', 'is_tax_applicable',
+				'is_flexible_benefit', 'variable_based_on_taxable_salary',
+				'exempted_from_income_tax'
+			):
+				component_row.set(attr, component_data.get(attr))
+
+		if additional_salary:
+			component_row.default_amount = 0
+			component_row.additional_amount = amount
+			component_row.additional_salary = additional_salary.name
+			component_row.deduct_full_tax_on_selected_payroll_date = \
+				additional_salary.deduct_full_tax_on_selected_payroll_date
 		else:
-			if struct_row.get("is_additional_component"):
-				if overwrite:
-					component_row.additional_amount = amount - component_row.get("default_amount", 0)
-					component_row.additional_salary = additional_salary
-				else:
-					component_row.additional_amount = amount
+			component_row.default_amount = amount
+			component_row.additional_amount = 0
+			component_row.deduct_full_tax_on_selected_payroll_date = \
+				component_data.deduct_full_tax_on_selected_payroll_date
 
-				if not overwrite and component_row.default_amount:
-					amount += component_row.default_amount
-			else:
-				component_row.default_amount = amount
-
-			component_row.amount = amount
-			component_row.deduct_full_tax_on_selected_payroll_date = struct_row.deduct_full_tax_on_selected_payroll_date
+		component_row.amount = amount
 
 	def calculate_variable_based_on_taxable_salary(self, tax_component, payroll_period):
 		if not payroll_period:
@@ -950,26 +963,13 @@
 				return frappe.safe_eval(condition, self.whitelisted_globals, data)
 		except NameError as err:
 			frappe.throw(_("{0} <br> This error can be due to missing or deleted field.").format(err),
-			    title=_("Name error"))
+				title=_("Name error"))
 		except SyntaxError as err:
 			frappe.throw(_("Syntax error in condition: {0}").format(err))
 		except Exception as e:
 			frappe.throw(_("Error in formula or condition: {0}").format(e))
 			raise
 
-	def get_salary_slip_row(self, salary_component):
-		component = frappe.get_doc("Salary Component", salary_component)
-		# Data for update_component_row
-		struct_row = frappe._dict()
-		struct_row['depends_on_payment_days'] = component.depends_on_payment_days
-		struct_row['salary_component'] = component.name
-		struct_row['abbr'] = component.salary_component_abbr
-		struct_row['do_not_include_in_total'] = component.do_not_include_in_total
-		struct_row['is_tax_applicable'] = component.is_tax_applicable
-		struct_row['is_flexible_benefit'] = component.is_flexible_benefit
-		struct_row['variable_based_on_taxable_salary'] = component.variable_based_on_taxable_salary
-		return struct_row
-
 	def get_component_totals(self, component_type, depends_on_payment_days=0):
 		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
 			["date_of_joining", "relieving_date"])
@@ -1032,7 +1032,6 @@
 			self.total_loan_repayment += payment.total_payment
 
 	def get_loan_details(self):
-
 		return frappe.get_all("Loan",
 			fields=["name", "interest_income_account", "loan_account", "loan_type"],
 			filters = {
@@ -1263,3 +1262,19 @@
 def generate_password_for_pdf(policy_template, employee):
 	employee = frappe.get_doc("Employee", employee)
 	return policy_template.format(**employee.as_dict())
+
+def get_salary_component_data(component):
+	return frappe.get_value(
+		"Salary Component",
+		component,
+		[
+			"name as salary_component",
+			"depends_on_payment_days",
+			"salary_component_abbr as abbr",
+			"do_not_include_in_total",
+			"is_tax_applicable",
+			"is_flexible_benefit",
+			"variable_based_on_taxable_salary",
+		],
+		as_dict=1,
+	)
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index 143a306..7672695 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -361,7 +361,6 @@
 		# as per assigned salary structure 40500 in monthly salary so 236000*5/100/12
 		frappe.db.sql("""delete from `tabPayroll Period`""")
 		frappe.db.sql("""delete from `tabSalary Component`""")
-		frappe.db.sql("""delete from `tabAdditional Salary`""")
 
 		payroll_period = create_payroll_period()
 
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 8ba0b6c..f9e1359 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -81,12 +81,18 @@
 
 	def calculate_start_date(self, task_details):
 		self.start_date = add_days(self.expected_start_date, task_details.start)
-		self.start_date = update_if_holiday(self.holiday_list, self.start_date)
+		self.start_date = self.update_if_holiday(self.start_date)
 		return self.start_date
 
 	def calculate_end_date(self, task_details):
 		self.end_date = add_days(self.start_date, task_details.duration)
-		return update_if_holiday(self.holiday_list, self.end_date)
+		return self.update_if_holiday(self.end_date)
+
+	def update_if_holiday(self, date):
+		holiday_list = self.holiday_list or get_holiday_list(self.company)
+		while is_holiday(holiday_list, date):
+			date = add_days(date, 1)
+		return date
 
 	def dependency_mapping(self, template_tasks, project_tasks):
 		for template_task in template_tasks:
@@ -541,9 +547,3 @@
 
 	project.status = status
 	project.save()
-
-def update_if_holiday(holiday_list, date):
-	holiday_list = holiday_list or get_holiday_list()
-	while is_holiday(holiday_list, date):
-		date = add_days(date, 1)
-	return date
diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py
index 6290538..15a2873 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.py
@@ -8,7 +8,6 @@
 test_ignore = ["Sales Order"]
 
 from erpnext.projects.doctype.project_template.test_project_template import make_project_template
-from erpnext.projects.doctype.project.project import update_if_holiday
 from erpnext.projects.doctype.task.test_task import create_task
 from frappe.utils import getdate, nowdate, add_days
 
@@ -97,7 +96,8 @@
 		project_name = name,
 		status = 'Open',
 		project_template = template.name,
-		expected_start_date = nowdate()
+		expected_start_date = nowdate(),
+		company="_Test Company"
 	)).insert()
 
 	return project
@@ -131,7 +131,7 @@
 
 def calculate_end_date(project, start, duration):
 	start = add_days(project.expected_start_date, start)
-	start = update_if_holiday(project.holiday_list, start)
+	start = project.update_if_holiday(start)
 	end = add_days(start, duration)
-	end = update_if_holiday(project.holiday_list, end)
-	return getdate(end)
\ No newline at end of file
+	end = project.update_if_holiday(end)
+	return getdate(end)
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index 4cb3804..f7c764e 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -13,9 +13,18 @@
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.payroll.doctype.salary_structure.test_salary_structure \
 	import make_salary_structure, create_salary_structure_assignment
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+	make_earning_salary_component,
+	make_deduction_salary_component
+)
 from erpnext.hr.doctype.employee.test_employee import make_employee
 
 class TestTimesheet(unittest.TestCase):
+	@classmethod
+	def setUpClass(cls):
+		make_earning_salary_component(setup=True, company_list=['_Test Company'])
+		make_deduction_salary_component(setup=True, company_list=['_Test Company'])
+
 	def setUp(self):
 		for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment", "Timesheet"]:
 			frappe.db.sql("delete from `tab%s`" % dt)
@@ -49,7 +58,7 @@
 		self.assertEqual(timesheet.total_billable_amount, 0)
 
 	def test_salary_slip_from_timesheet(self):
-		emp = make_employee("test_employee_6@salary.com")
+		emp = make_employee("test_employee_6@salary.com", company="_Test Company")
 
 		salary_structure = make_salary_structure_for_timesheet(emp)
 		timesheet = make_timesheet(emp, simulate = True, billable=1)
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 1c0abdf..365851a 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -738,21 +738,15 @@
 			}
 			else {
 				var valid_serial_nos = [];
-
+				var serialnos = [];
 				// Replacing all occurences of comma with carriage return
-				var serial_nos = item.serial_no.trim().replace(/,/g, '\n');
-
-				serial_nos = serial_nos.trim().split('\n');
-
-				// Trim each string and push unique string to new list
-				for (var x=0; x<=serial_nos.length - 1; x++) {
-					if (serial_nos[x].trim() != "" && valid_serial_nos.indexOf(serial_nos[x].trim()) == -1) {
-						valid_serial_nos.push(serial_nos[x].trim());
+				item.serial_no = item.serial_no.replace(/,/g, '\n');
+				serialnos = item.serial_no.split("\n");
+				for (var i = 0; i < serialnos.length; i++) {
+					if (serialnos[i] != "") {
+						valid_serial_nos.push(serialnos[i]);
 					}
 				}
-
-				// Add the new list to the serial no. field in grid with each in new line
-				item.serial_no = valid_serial_nos.join('\n');
 				item.conversion_factor = item.conversion_factor || 1;
 
 				refresh_field("serial_no", item.name, item.parentfield);
diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
index 68c8a0d..a49996d 100644
--- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
@@ -349,13 +349,12 @@
 		return inter_state_supply_details
 
 	def get_inward_nil_exempt(self, state):
-
 		inward_nil_exempt = frappe.db.sql(""" select p.place_of_supply, sum(i.base_amount) as base_amount,
 			i.is_nil_exempt, i.is_non_gst from `tabPurchase Invoice` p , `tabPurchase Invoice Item` i
 			where p.docstatus = 1 and p.name = i.parent
-			and i.is_nil_exempt = 1 or i.is_non_gst = 1 and
+			and (i.is_nil_exempt = 1 or i.is_non_gst = 1) and
 			month(p.posting_date) = %s and year(p.posting_date) = %s and p.company = %s and p.company_gstin = %s
-			group by p.place_of_supply """, (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
+			group by p.place_of_supply, i.is_nil_exempt, i.is_non_gst""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
 
 		inward_nil_exempt_details = {
 			"gst": {
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
index d734a18..5bbd575 100644
--- a/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
@@ -16,6 +16,7 @@
 		self.validate_duplicates()
 		self.validate_company_details()
 		self.set_company_address()
+		self.calculate_total()
 		self.set_title()
 
 	def validate_date(self):
@@ -29,7 +30,10 @@
 
 	def validate_duplicates(self):
 		if self.recipient == 'Donor':
-			certificate = frappe.db.exists(self.doctype, {'donation': self.donation})
+			certificate = frappe.db.exists(self.doctype, {
+				'donation': self.donation,
+				'name': ('!=', self.name)
+			})
 			if certificate:
 				frappe.throw(_('An 80G Certificate {0} already exists for the donation {1}').format(
 					get_link_to_form(self.doctype, certificate), frappe.bold(self.donation)
@@ -51,8 +55,17 @@
 		self.company_address = address.company_address
 		self.company_address_display = address.company_address_display
 
+	def calculate_total(self):
+		if self.recipient == 'Donor':
+			return
+
+		total = 0
+		for entry in self.payments:
+			total += flt(entry.amount)
+		self.total = total
+
 	def set_title(self):
-		if self.recipient == "Member":
+		if self.recipient == 'Member':
 			self.title = self.member_name
 		else:
 			self.title = self.donor_name
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index ee16f44..0fdfb1b 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1,11 +1,12 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 from __future__ import unicode_literals
-import frappe
 import json
-from frappe.utils import flt, add_days, nowdate
-import frappe.permissions
 import unittest
+import frappe
+import frappe.permissions
+from frappe.utils import flt, add_days, nowdate
+from frappe.core.doctype.user_permission.test_user_permission import create_user
 from erpnext.selling.doctype.sales_order.sales_order \
 	import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
@@ -444,10 +445,8 @@
 	def test_update_child_perm(self):
 		so = make_sales_order(item_code= "_Test Item", qty=4)
 
-		user = 'test@example.com'
-		test_user = frappe.get_doc('User', user)
-		test_user.add_roles("Accounts User")
-		frappe.set_user(user)
+		test_user = create_user("test_so_child_perms@example.com", "Accounts User")
+		frappe.set_user(test_user.name)
 
 		# update qty
 		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}])
@@ -456,18 +455,14 @@
 		# add new item
 		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
 		self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
-		test_user.remove_roles("Accounts User")
-		frappe.set_user("Administrator")
 
 	def test_update_child_qty_rate_with_workflow(self):
 		from frappe.model.workflow import apply_workflow
 
-		frappe.set_user("Administrator")
 		workflow = make_sales_order_workflow()
 		so = make_sales_order(item_code= "_Test Item", qty=1, rate=150, do_not_submit=1)
 		apply_workflow(so, 'Approve')
 
-		frappe.set_user("Administrator")
 		user = 'test@example.com'
 		test_user = frappe.get_doc('User', user)
 		test_user.add_roles("Sales User", "Test Junior Approver")
@@ -618,33 +613,31 @@
 		frappe.db.set_value("Stock Settings", None, "default_warehouse", old_stock_settings_value)
 
 	def test_warehouse_user(self):
-		frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
-		frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
-		frappe.permissions.add_user_permission("Company", "_Test Company 1", "test2@example.com")
-
-		test_user = frappe.get_doc("User", "test@example.com")
-		test_user.add_roles("Sales User", "Stock User")
-		test_user.remove_roles("Sales Manager")
+		test_user = create_user("test_so_warehouse_user@example.com", "Sales User", "Stock User")
 
 		test_user_2 = frappe.get_doc("User", "test2@example.com")
 		test_user_2.add_roles("Sales User", "Stock User")
 		test_user_2.remove_roles("Sales Manager")
 
-		frappe.set_user("test@example.com")
+		frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", test_user.name)
+		frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", test_user_2.name)
+		frappe.permissions.add_user_permission("Company", "_Test Company 1", test_user_2.name)
 
-		so = make_sales_order(company="_Test Company 1",
+		frappe.set_user(test_user.name)
+
+		so = make_sales_order(company="_Test Company 1", customer="_Test Customer 1",
 			warehouse="_Test Warehouse 2 - _TC1", do_not_save=True)
 		so.conversion_rate = 0.02
 		so.plc_conversion_rate = 0.02
 		self.assertRaises(frappe.PermissionError, so.insert)
 
-		frappe.set_user("test2@example.com")
+		frappe.set_user(test_user_2.name)
 		so.insert()
 
 		frappe.set_user("Administrator")
-		frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
-		frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
-		frappe.permissions.remove_user_permission("Company", "_Test Company 1", "test2@example.com")
+		frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 1 - _TC", test_user.name)
+		frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", test_user_2.name)
+		frappe.permissions.remove_user_permission("Company", "_Test Company 1", test_user_2.name)
 
 	def test_block_delivery_note_against_cancelled_sales_order(self):
 		so = make_sales_order()
diff --git a/erpnext/setup/doctype/naming_series/naming_series.py b/erpnext/setup/doctype/naming_series/naming_series.py
index abff973..2ea0bc0 100644
--- a/erpnext/setup/doctype/naming_series/naming_series.py
+++ b/erpnext/setup/doctype/naming_series/naming_series.py
@@ -10,6 +10,7 @@
 from frappe.model.document import Document
 from frappe.model.naming import parse_naming_series
 from frappe.permissions import get_doctypes_with_read
+from frappe.core.doctype.doctype.doctype import validate_series
 
 class NamingSeriesNotSetError(frappe.ValidationError): pass
 
@@ -126,7 +127,7 @@
 		dt = frappe.get_doc("DocType", self.select_doc_for_series)
 		options = self.scrub_options_list(self.set_options.split("\n"))
 		for series in options:
-			dt.validate_series(series)
+			validate_series(dt, series)
 			for i in sr:
 				if i[0]:
 					existing_series = [d.split('.')[0] for d in i[0].split("\n")]
diff --git a/erpnext/setup/workspace/home/home.json b/erpnext/setup/workspace/home/home.json
index 69ca7cf..305456b 100644
--- a/erpnext/setup/workspace/home/home.json
+++ b/erpnext/setup/workspace/home/home.json
@@ -10,13 +10,14 @@
  "hide_custom": 0,
  "icon": "getting-started",
  "idx": 0,
+ "is_default": 0,
  "is_standard": 1,
  "label": "Home",
  "links": [
   {
    "hidden": 0,
    "is_query_report": 0,
-   "label": "Healthcare",
+   "label": "Accounting",
    "onboard": 0,
    "type": "Card Break"
   },
@@ -24,8 +25,8 @@
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
-   "label": "Patient",
-   "link_to": "Patient",
+   "label": "Chart of Accounts",
+   "link_to": "Account",
    "link_type": "DocType",
    "onboard": 1,
    "type": "Link"
@@ -34,25 +35,8 @@
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
-   "label": "Diagnosis",
-   "link_to": "Diagnosis",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Agriculture",
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Crop",
-   "link_to": "Crop",
+   "label": "Company",
+   "link_to": "Company",
    "link_type": "DocType",
    "onboard": 1,
    "type": "Link"
@@ -61,8 +45,8 @@
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
-   "label": "Crop Cycle",
-   "link_to": "Crop Cycle",
+   "label": "Customer",
+   "link_to": "Customer",
    "link_type": "DocType",
    "onboard": 1,
    "type": "Link"
@@ -71,112 +55,8 @@
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
-   "label": "Location",
-   "link_to": "Location",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Fertilizer",
-   "link_to": "Fertilizer",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Education",
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student",
-   "link_to": "Student",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Course",
-   "link_to": "Course",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Instructor",
-   "link_to": "Instructor",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Room",
-   "link_to": "Room",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Non Profit",
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Member",
-   "link_to": "Member",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Volunteer",
-   "link_to": "Volunteer",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Chapter",
-   "link_to": "Chapter",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Donor",
-   "link_to": "Donor",
+   "label": "Supplier",
+   "link_to": "Supplier",
    "link_type": "DocType",
    "onboard": 1,
    "type": "Link"
@@ -192,6 +72,16 @@
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
+   "label": "Item",
+   "link_to": "Item",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
    "label": "Warehouse",
    "link_to": "Warehouse",
    "link_type": "DocType",
@@ -305,73 +195,6 @@
   {
    "hidden": 0,
    "is_query_report": 0,
-   "label": "Accounting",
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Item",
-   "link_to": "Item",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Customer",
-   "link_to": "Customer",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Supplier",
-   "link_to": "Supplier",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Company",
-   "link_to": "Company",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Chart of Accounts",
-   "link_to": "Account",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Opening Invoice Creation Tool",
-   "link_to": "Opening Invoice Creation Tool",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
    "label": "Data Import and Settings",
    "onboard": 0,
    "type": "Card Break"
@@ -390,6 +213,16 @@
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
+   "label": "Opening Invoice Creation Tool",
+   "link_to": "Opening Invoice Creation Tool",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
    "label": "Chart of Accounts Importer",
    "link_to": "Chart of Accounts Importer",
    "link_type": "DocType",
@@ -415,9 +248,177 @@
    "link_type": "DocType",
    "onboard": 1,
    "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Healthcare",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Patient",
+   "link_to": "Patient",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Diagnosis",
+   "link_to": "Diagnosis",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Education",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Student",
+   "link_to": "Student",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Instructor",
+   "link_to": "Instructor",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Course",
+   "link_to": "Course",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Room",
+   "link_to": "Room",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Non Profit",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Donor",
+   "link_to": "Donor",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Member",
+   "link_to": "Member",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Volunteer",
+   "link_to": "Volunteer",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Chapter",
+   "link_to": "Chapter",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Agriculture",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Location",
+   "link_to": "Location",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Crop",
+   "link_to": "Crop",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Crop Cycle",
+   "link_to": "Crop Cycle",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Fertilizer",
+   "link_to": "Fertilizer",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
   }
  ],
- "modified": "2021-01-01 12:13:16.055668",
+ "modified": "2021-03-16 15:59:58.416154",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Home",
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 5539123..2079cf8 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -717,6 +717,18 @@
 				.on('focus', function(e) {
 					$(e.target).val('').trigger('input');
 				})
+				.on("awesomplete-open", () => {
+					let modal = field.$input.parents('.modal-dialog')[0];
+					if (modal) {
+						$(modal).removeClass("modal-dialog-scrollable");
+					}
+				})
+				.on("awesomplete-close", () => {
+					let modal = field.$input.parents('.modal-dialog')[0];
+					if (modal) {
+						$(modal).addClass("modal-dialog-scrollable");
+					}
+				});
 		});
 	},
 
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 0da57b7..d723fac 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -25,8 +25,8 @@
 			if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'):
 				continue
 			if not item.serial_no:
-				frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}".format(
-					frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse))),
+				frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}").format(
+					frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)),
 					title=_("Serial Nos Required"))
 			if len(item.serial_no.split('\n')) == item.picked_qty:
 				continue
@@ -380,7 +380,7 @@
 
 	stock_entry.set_incoming_rate()
 	stock_entry.set_actual_qty()
-	stock_entry.calculate_rate_and_amount(update_finished_item_rate=False)
+	stock_entry.calculate_rate_and_amount()
 
 	return stock_entry.as_dict()
 
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 8436acb..559f9a5 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -5,7 +5,7 @@
 from __future__ import unicode_literals
 import frappe, erpnext
 from frappe.model.document import Document
-from frappe.utils import cint, get_link_to_form
+from frappe.utils import cint, get_link_to_form, add_to_date, today
 from erpnext.stock.stock_ledger import repost_future_sle
 from erpnext.accounts.utils import update_gl_entries_after, check_if_stock_and_account_balance_synced
 from frappe.utils.user import get_users_with_role
@@ -29,7 +29,7 @@
 			self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company")
 		elif self.warehouse:
 			self.company = frappe.get_cached_value("Warehouse", self.warehouse, "company")
-	
+
 	def set_status(self, status=None):
 		if not status:
 			status = 'Queued'
@@ -54,7 +54,6 @@
 
 		repost_sl_entries(doc)
 		repost_gl_entries(doc)
-		check_if_stock_and_account_balance_synced(doc.posting_date, doc.company)
 
 		doc.set_status('Completed')
 	except Exception:
@@ -103,7 +102,7 @@
 	recipients = get_users_with_role("Stock Manager")
 	if not recipients:
 		get_users_with_role("System Manager")
-	
+
 	subject = _("Error while reposting item valuation")
 	message = (_("Hi,") + "<br>"
 		+ _("An error has been appeared while reposting item valuation via {0}")
@@ -112,4 +111,24 @@
 	)
 	frappe.sendmail(recipients=recipients, subject=subject, message=message)
 
+def repost_entries():
+	riv_entries = get_repost_item_valuation_entries()
 
+	for row in riv_entries:
+		doc = frappe.get_cached_doc('Repost Item Valuation', row.name)
+		repost(doc)
+
+	riv_entries = get_repost_item_valuation_entries()
+	if riv_entries:
+		return
+
+	for d in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
+		check_if_stock_and_account_balance_synced(today(), d.company)
+
+def get_repost_item_valuation_entries():
+	date = add_to_date(today(), hours=-12)
+
+	return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation`
+		WHERE status != 'Completed' and creation <= %s and docstatus = 1
+		ORDER BY timestamp(posting_date, posting_time) asc, creation asc
+	""", date, as_dict=1)
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index f0a90f9..b452e96 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -29,6 +29,8 @@
 		self.remove_items_with_no_change()
 		self.validate_data()
 		self.validate_expense_account()
+		self.validate_customer_provided_item()
+		self.set_zero_value_for_customer_provided_items()
 		self.set_total_qty_and_amount()
 		self.validate_putaway_capacity()
 
@@ -217,7 +219,7 @@
 					if row.valuation_rate in ("", None):
 						row.valuation_rate = previous_sle.get("valuation_rate", 0)
 
-				if row.qty and not row.valuation_rate:
+				if row.qty and not row.valuation_rate and not row.allow_zero_valuation_rate:
 					frappe.throw(_("Valuation Rate required for Item {0} at row {1}").format(row.item_code, row.idx))
 
 				if ((previous_sle and row.qty == previous_sle.get("qty_after_transaction")
@@ -436,6 +438,20 @@
 			if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
 				frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry"), OpeningEntryAccountError)
 
+	def set_zero_value_for_customer_provided_items(self):
+		changed_any_values = False
+
+		for d in self.get('items'):
+			is_customer_item = frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item')
+			if is_customer_item and d.valuation_rate:
+				d.valuation_rate = 0.0
+				changed_any_values = True
+
+		if changed_any_values:
+			msgprint(_("Valuation rate for customer provided items has been set to zero."),
+				title=_("Note"), indicator="blue")
+
+
 	def set_total_qty_and_amount(self):
 		for d in self.get("items"):
 			d.amount = flt(d.qty, d.precision("qty")) * flt(d.valuation_rate, d.precision("valuation_rate"))
@@ -531,4 +547,4 @@
 		account = frappe.db.get_value('Account', {'is_group': 0,
 			'company': company, 'account_type': 'Temporary'}, 'name')
 
-	return account
\ No newline at end of file
+	return account
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 088456f..6690c6a 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -193,6 +193,16 @@
 			stock_doc = frappe.get_doc("Stock Reconciliation", d)
 			stock_doc.cancel()
 
+	def test_customer_provided_items(self):
+		item_code = 'Stock-Reco-customer-Item-100'
+		create_item(item_code, is_customer_provided_item = 1,
+			  customer = '_Test Customer', is_purchase_item = 0)
+
+		sr = create_stock_reconciliation(item_code = item_code, qty = 10, rate = 420)
+
+		self.assertEqual(sr.get("items")[0].allow_zero_valuation_rate, 1)
+		self.assertEqual(sr.get("items")[0].valuation_rate, 0)
+		self.assertEqual(sr.get("items")[0].amount, 0)
 
 def insert_existing_sle(warehouse):
 	from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
index e53db07..85c7ebe 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -13,6 +13,7 @@
   "qty",
   "valuation_rate",
   "amount",
+  "allow_zero_valuation_rate",
   "serial_no_and_batch_section",
   "serial_no",
   "column_break_11",
@@ -166,10 +167,19 @@
    "fieldtype": "Link",
    "label": "Batch No",
    "options": "Batch"
+  },
+  {
+   "default": "0",
+   "fieldname": "allow_zero_valuation_rate",
+   "fieldtype": "Check",
+   "label": "Allow Zero Valuation Rate",
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "istable": 1,
- "modified": "2019-06-14 17:10:53.188305",
+ "links": [],
+ "modified": "2021-03-23 11:09:44.407157",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Reconciliation Item",
@@ -179,4 +189,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index e5d4d62..6dfede4 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -198,7 +198,7 @@
 		else:
 			qty_diff = flt(d.actual_qty)
 
-		value_diff = flt(d.stock_value) - flt(qty_dict.bal_val)
+		value_diff = flt(d.stock_value_difference)
 
 		if d.posting_date < from_date:
 			qty_dict.opening_qty += qty_diff
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index 483bb15..46d02d8 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -12,7 +12,6 @@
 class TestIssue(unittest.TestCase):
 	def setUp(self):
 		frappe.db.sql("delete from `tabService Level Agreement`")
-		frappe.db.sql("delete from `tabEmployee`")
 		frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
 		create_service_level_agreements_for_issues()
 
diff --git a/erpnext/support/report/issue_summary/issue_summary.py b/erpnext/support/report/issue_summary/issue_summary.py
index 3d73531..7861e30 100644
--- a/erpnext/support/report/issue_summary/issue_summary.py
+++ b/erpnext/support/report/issue_summary/issue_summary.py
@@ -260,8 +260,7 @@
 					self.issue_summary_data[value]['avg_user_resolution_time'] = entry.get('avg_user_resolution_time') or 0.0
 
 	def get_chart_data(self):
-		if not self.data:
-			return None
+		self.chart = []
 
 		labels = []
 		open_issues = []
@@ -310,8 +309,7 @@
 		}
 
 	def get_report_summary(self):
-		if not self.data:
-			return None
+		self.report_summary = []
 
 		open_issues = 0
 		replied = 0
diff --git a/erpnext/templates/includes/issue_row.html b/erpnext/templates/includes/issue_row.html
index d909c5f..a04f558 100644
--- a/erpnext/templates/includes/issue_row.html
+++ b/erpnext/templates/includes/issue_row.html
@@ -1,6 +1,6 @@
 <div class="web-list-item transaction-list-item">
 	<a href="/issues?name={{ doc.name }}" class="no-underline">
-		<div class="row py-4 border-bottom">
+		<div class="row py-4">
 			<div class="col-3 d-flex align-items-center">
 				{% set indicator = 'red' if doc.status == 'Open' else 'gray' %}
 				{% set indicator = 'green' if doc.status == 'Closed' else indicator %}
diff --git a/sider.yml b/sider.yml
new file mode 100644
index 0000000..2ca6e8d
--- /dev/null
+++ b/sider.yml
@@ -0,0 +1,3 @@
+linter:
+  flake8:
+    config: .flake8
\ No newline at end of file