Merge branch 'develop' of https://github.com/frappe/erpnext into project_filter_search_fields
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 1bd42f5..0ebf0eb 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -61,8 +61,10 @@
 	def on_update(self):
 		frappe.flags.accounting_dimensions = None
 
-def make_dimension_in_accounting_doctypes(doc):
-	doclist = get_doctypes_with_dimensions()
+def make_dimension_in_accounting_doctypes(doc, doclist=None):
+	if not doclist:
+		doclist = get_doctypes_with_dimensions()
+
 	doc_count = len(get_accounting_dimensions())
 	count = 0
 
@@ -82,13 +84,13 @@
 			"owner": "Administrator"
 		}
 
-		if doctype == "Budget":
-			add_dimension_to_budget_doctype(df, doc)
-		else:
-			meta = frappe.get_meta(doctype, cached=False)
-			fieldnames = [d.fieldname for d in meta.get("fields")]
+		meta = frappe.get_meta(doctype, cached=False)
+		fieldnames = [d.fieldname for d in meta.get("fields")]
 
-			if df['fieldname'] not in fieldnames:
+		if df['fieldname'] not in fieldnames:
+			if doctype == "Budget":
+				add_dimension_to_budget_doctype(df.copy(), doc)
+			else:
 				create_custom_field(doctype, df)
 
 		count += 1
@@ -178,15 +180,7 @@
 		frappe.clear_cache(doctype=doctype)
 
 def get_doctypes_with_dimensions():
-	doclist = ["GL Entry", "Sales Invoice", "POS Invoice", "Purchase Invoice", "Payment Entry", "Asset",
-		"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
-		"Sales Invoice Item", "POS Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
-		"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
-		"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
-		"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
-		"Subscription Plan"]
-
-	return doclist
+	return frappe.get_hooks("accounting_dimension_doctypes")
 
 def get_accounting_dimensions(as_list=True):
 	if frappe.flags.accounting_dimensions is None:
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
index 342f21b..03c3eb0 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
@@ -22,9 +22,10 @@
 		'allow_account_creation_against_child_company'])
 
 	if parent_company and (not allow_account_creation_against_child_company):
-		frappe.throw(_("""{0} is a child company. Please import accounts against parent company
-			or enable {1} in company master""").format(frappe.bold(company),
-			frappe.bold('Allow Account Creation Against Child Company')), title='Wrong Company')
+		msg = _("{} is a child company. ").format(frappe.bold(company))
+		msg += _("Please import accounts against parent company or enable {} in company master.").format(
+			frappe.bold('Allow Account Creation Against Child Company'))
+		frappe.throw(msg, title=_('Wrong Company'))
 
 	if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1):
 		return False
@@ -74,7 +75,9 @@
 			if as_dict:
 				data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
 			else:
-				if not row[1]: row[1] = row[0]
+				if not row[1]:
+					row[1] = row[0]
+					row[3] = row[2]
 				data.append(row)
 
 	# convert csv data
@@ -96,7 +99,9 @@
 		if as_dict:
 			data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
 		else:
-			if not row[1]: row[1] = row[0]
+			if not row[1]:
+					row[1] = row[0]
+					row[3] = row[2]
 			data.append(row)
 
 	return data
@@ -147,7 +152,13 @@
 		from frappe import _
 
 		for row in data:
-			account_name, parent_account = row[0:2]
+			account_name, parent_account, account_number, parent_account_number = row[0:4]
+			if account_number:
+				account_name = "{} - {}".format(account_number, account_name)
+			if parent_account_number:
+				parent_account_number = cstr(parent_account_number).strip()
+				parent_account = "{} - {}".format(parent_account_number, parent_account)
+
 			if parent_account == account_name == child:
 				return [parent_account]
 			elif account_name == child:
@@ -159,20 +170,23 @@
 
 	charts_map, paths = {}, []
 
-	line_no = 3
+	line_no = 2
 	error_messages = []
 
 	for i in data:
-		account_name, dummy, account_number, is_group, account_type, root_type = i
+		account_name, parent_account, account_number, parent_account_number, is_group, account_type, root_type = i
 
 		if not account_name:
 			error_messages.append("Row {0}: Please enter Account Name".format(line_no))
 
+		if account_number:
+			account_number = cstr(account_number).strip()
+			account_name = "{} - {}".format(account_number, account_name)
+
 		charts_map[account_name] = {}
 		if cint(is_group) == 1: charts_map[account_name]["is_group"] = is_group
 		if account_type: charts_map[account_name]["account_type"] = account_type
 		if root_type: charts_map[account_name]["root_type"] = root_type
-		if account_number: charts_map[account_name]["account_number"] = account_number
 		path = return_parent(data, account_name)[::-1]
 		paths.append(path) # List of path is created
 		line_no += 1
@@ -221,7 +235,7 @@
 
 def get_template(template_type):
 
-	fields = ["Account Name", "Parent Account", "Account Number", "Is Group", "Account Type", "Root Type"]
+	fields = ["Account Name", "Parent Account", "Account Number", "Parent Account Number", "Is Group", "Account Type", "Root Type"]
 	writer = UnicodeWriter()
 	writer.writerow(fields)
 
@@ -241,23 +255,23 @@
 
 def get_sample_template(writer):
 	template = [
-		["Application Of Funds(Assets)", "", "", 1, "", "Asset"],
-		["Sources Of Funds(Liabilities)", "", "", 1, "", "Liability"],
-		["Equity", "", "", 1, "", "Equity"],
-		["Expenses", "", "", 1, "", "Expense"],
-		["Income", "", "", 1, "", "Income"],
-		["Bank Accounts", "Application Of Funds(Assets)", "", 1, "Bank", "Asset"],
-		["Cash In Hand", "Application Of Funds(Assets)", "", 1, "Cash", "Asset"],
-		["Stock Assets", "Application Of Funds(Assets)", "", 1, "Stock", "Asset"],
-		["Cost Of Goods Sold", "Expenses", "", 0, "Cost of Goods Sold", "Expense"],
-		["Asset Depreciation", "Expenses", "", 0, "Depreciation", "Expense"],
-		["Fixed Assets", "Application Of Funds(Assets)", "", 0, "Fixed Asset", "Asset"],
-		["Accounts Payable", "Sources Of Funds(Liabilities)", "", 0, "Payable", "Liability"],
-		["Accounts Receivable", "Application Of Funds(Assets)", "", 1, "Receivable", "Asset"],
-		["Stock Expenses", "Expenses", "", 0, "Stock Adjustment", "Expense"],
-		["Sample Bank", "Bank Accounts", "", 0, "Bank", "Asset"],
-		["Cash", "Cash In Hand", "", 0, "Cash", "Asset"],
-		["Stores", "Stock Assets", "", 0, "Stock", "Asset"],
+		["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"],
+		["Sources Of Funds(Liabilities)", "", "", "", 1, "", "Liability"],
+		["Equity", "", "", "", 1, "", "Equity"],
+		["Expenses", "", "", "", 1, "", "Expense"],
+		["Income", "", "", "", 1, "", "Income"],
+		["Bank Accounts", "Application Of Funds(Assets)", "", "", 1, "Bank", "Asset"],
+		["Cash In Hand", "Application Of Funds(Assets)", "", "", 1, "Cash", "Asset"],
+		["Stock Assets", "Application Of Funds(Assets)", "", "", 1, "Stock", "Asset"],
+		["Cost Of Goods Sold", "Expenses", "", "", 0, "Cost of Goods Sold", "Expense"],
+		["Asset Depreciation", "Expenses", "", "", 0, "Depreciation", "Expense"],
+		["Fixed Assets", "Application Of Funds(Assets)", "", "", 0, "Fixed Asset", "Asset"],
+		["Accounts Payable", "Sources Of Funds(Liabilities)", "", "", 0, "Payable", "Liability"],
+		["Accounts Receivable", "Application Of Funds(Assets)", "", "", 1, "Receivable", "Asset"],
+		["Stock Expenses", "Expenses", "", "", 0, "Stock Adjustment", "Expense"],
+		["Sample Bank", "Bank Accounts", "", "", 0, "Bank", "Asset"],
+		["Cash", "Cash In Hand", "", "", 0, "Cash", "Asset"],
+		["Stores", "Stock Assets", "", "", 0, "Stock", "Asset"],
 	]
 
 	for row in template:
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 0565264..f0b4e29 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -136,7 +136,7 @@
 			for d in self.items:
 				max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
 				if max_discount and flt(self.discount_percentage) > flt(max_discount):
-					throw(_("Max discount allowed for item: {0} is {1}%").format(self.item_code, max_discount))
+					throw(_("Max discount allowed for item: {0} is {1}%").format(d.item_code, max_discount))
 
 	def validate_price_list_with_currency(self):
 		if self.currency and self.for_price_list:
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 9599d4e..4217711 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -21,6 +21,7 @@
 from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
 	get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
 from erpnext.accounts.deferred_revenue import validate_service_stop_date
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
 from frappe.model.utils import get_fetch_values
 from frappe.contacts.doctype.address.address import get_address_display
 
@@ -75,6 +76,8 @@
 
 		if not self.is_pos:
 			self.so_dn_required()
+		
+		self.set_tax_withholding()
 
 		self.validate_proj_cust()
 		self.validate_pos_return()
@@ -153,6 +156,32 @@
 			if cost_center_company != self.company:
 				frappe.throw(_("Row #{0}: Cost Center {1} does not belong to company {2}").format(frappe.bold(item.idx), frappe.bold(item.cost_center), frappe.bold(self.company)))
 
+	def set_tax_withholding(self):
+		tax_withholding_details = get_party_tax_withholding_details(self)
+
+		if not tax_withholding_details:
+			return
+
+		accounts = []
+		tax_withholding_account = tax_withholding_details.get("account_head")
+
+		for d in self.taxes:
+			if d.account_head == tax_withholding_account:
+				d.update(tax_withholding_details)
+			accounts.append(d.account_head)
+
+		if not accounts or tax_withholding_account not in accounts:
+			self.append("taxes", tax_withholding_details)
+
+		to_remove = [d for d in self.taxes
+			if not d.tax_amount and d.charge_type == "Actual" and d.account_head == tax_withholding_account]
+
+		for d in to_remove:
+			self.remove(d)
+
+		# calculate totals again after applying TDS
+		self.calculate_taxes_and_totals()
+
 	def before_save(self):
 		set_account_for_mode_of_payment(self)
 
diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js
index ba98eb9..1a90664 100644
--- a/erpnext/accounts/doctype/subscription/subscription.js
+++ b/erpnext/accounts/doctype/subscription/subscription.js
@@ -10,6 +10,14 @@
 				}
 			}
 		});
+
+		frm.set_query('cost_center', function() {
+			return {
+				filters: {
+					company: frm.doc.company
+				}
+			};
+		});
 	},
 
 	refresh: function(frm) {
diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json
index afb94fe..e80df2a 100644
--- a/erpnext/accounts/doctype/subscription/subscription.json
+++ b/erpnext/accounts/doctype/subscription/subscription.json
@@ -7,9 +7,10 @@
  "engine": "InnoDB",
  "field_order": [
   "party_type",
-  "status",
-  "cb_1",
   "party",
+  "cb_1",
+  "company",
+  "status",
   "subscription_period",
   "start_date",
   "end_date",
@@ -44,80 +45,107 @@
   {
    "allow_on_submit": 1,
    "fieldname": "cb_1",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "status",
    "fieldtype": "Select",
    "label": "Status",
+   "no_copy": 1,
    "options": "\nTrialling\nActive\nPast Due Date\nCancelled\nUnpaid\nCompleted",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "subscription_period",
    "fieldtype": "Section Break",
-   "label": "Subscription Period"
+   "label": "Subscription Period",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "cancelation_date",
    "fieldtype": "Date",
    "label": "Cancelation Date",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
    "fieldname": "trial_period_start",
    "fieldtype": "Date",
    "label": "Trial Period Start Date",
-   "set_only_once": 1
+   "set_only_once": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "eval:doc.trial_period_start",
    "fieldname": "trial_period_end",
    "fieldtype": "Date",
    "label": "Trial Period End Date",
-   "set_only_once": 1
+   "set_only_once": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_11",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "current_invoice_start",
    "fieldtype": "Date",
    "label": "Current Invoice Start Date",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "current_invoice_end",
    "fieldtype": "Date",
    "label": "Current Invoice End Date",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
    "description": "Number of days that the subscriber has to pay invoices generated by this subscription",
    "fieldname": "days_until_due",
    "fieldtype": "Int",
-   "label": "Days Until Due"
+   "label": "Days Until Due",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
    "fieldname": "cancel_at_period_end",
    "fieldtype": "Check",
-   "label": "Cancel At End Of Period"
+   "label": "Cancel At End Of Period",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
    "fieldname": "generate_invoice_at_period_start",
    "fieldtype": "Check",
-   "label": "Generate Invoice At Beginning Of Period"
+   "label": "Generate Invoice At Beginning Of Period",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
    "fieldname": "sb_4",
    "fieldtype": "Section Break",
-   "label": "Plans"
+   "label": "Plans",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
@@ -125,62 +153,84 @@
    "fieldtype": "Table",
    "label": "Plans",
    "options": "Subscription Plan Detail",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "eval:['Customer', 'Supplier'].includes(doc.party_type)",
    "fieldname": "sb_1",
    "fieldtype": "Section Break",
-   "label": "Taxes"
+   "label": "Taxes",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "sb_2",
    "fieldtype": "Section Break",
-   "label": "Discounts"
+   "label": "Discounts",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "apply_additional_discount",
    "fieldtype": "Select",
    "label": "Apply Additional Discount On",
-   "options": "\nGrand Total\nNet Total"
+   "options": "\nGrand Total\nNet Total",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "cb_2",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "additional_discount_percentage",
    "fieldtype": "Percent",
-   "label": "Additional DIscount Percentage"
+   "label": "Additional DIscount Percentage",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "additional_discount_amount",
    "fieldtype": "Currency",
-   "label": "Additional DIscount Amount"
+   "label": "Additional DIscount Amount",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "eval:doc.invoices",
    "fieldname": "sb_3",
    "fieldtype": "Section Break",
-   "label": "Invoices"
+   "label": "Invoices",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "invoices",
    "fieldtype": "Table",
    "label": "Invoices",
-   "options": "Subscription Invoice"
+   "options": "Subscription Invoice",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "accounting_dimensions_section",
    "fieldtype": "Section Break",
-   "label": "Accounting Dimensions"
+   "label": "Accounting Dimensions",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "dimension_col_break",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "party_type",
@@ -188,7 +238,9 @@
    "label": "Party Type",
    "options": "DocType",
    "reqd": 1,
-   "set_only_once": 1
+   "set_only_once": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "party",
@@ -197,21 +249,27 @@
    "label": "Party",
    "options": "party_type",
    "reqd": 1,
-   "set_only_once": 1
+   "set_only_once": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "eval:doc.party_type === 'Customer'",
    "fieldname": "sales_tax_template",
    "fieldtype": "Link",
    "label": "Sales Taxes and Charges Template",
-   "options": "Sales Taxes and Charges Template"
+   "options": "Sales Taxes and Charges Template",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "eval:doc.party_type === 'Supplier'",
    "fieldname": "purchase_tax_template",
    "fieldtype": "Link",
    "label": "Purchase Taxes and Charges Template",
-   "options": "Purchase Taxes and Charges Template"
+   "options": "Purchase Taxes and Charges Template",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
@@ -219,36 +277,55 @@
    "fieldname": "follow_calendar_months",
    "fieldtype": "Check",
    "label": "Follow Calendar Months",
-   "set_only_once": 1
+   "set_only_once": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
    "description": "New invoices will be generated as per schedule even if current invoices are unpaid or past due date",
    "fieldname": "generate_new_invoices_past_due_date",
    "fieldtype": "Check",
-   "label": "Generate New Invoices Past Due Date"
+   "label": "Generate New Invoices Past Due Date",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "end_date",
    "fieldtype": "Date",
    "label": "Subscription End Date",
-   "set_only_once": 1
+   "set_only_once": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "start_date",
    "fieldtype": "Date",
    "label": "Subscription Start Date",
-   "set_only_once": 1
+   "set_only_once": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "cost_center",
    "fieldtype": "Link",
    "label": "Cost Center",
-   "options": "Cost Center"
+   "options": "Cost Center",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "show_days": 1,
+   "show_seconds": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-06-25 10:52:52.265105",
+ "modified": "2021-02-09 15:44:20.024789",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Subscription",
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index e023b47..826044a 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -1,3 +1,4 @@
+
 # -*- coding: utf-8 -*-
 # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
@@ -5,12 +6,13 @@
 from __future__ import unicode_literals
 
 import frappe
+import erpnext
 from frappe import _
 from frappe.model.document import Document
 from frappe.utils.data import nowdate, getdate, cstr, cint, add_days, date_diff, get_last_day, add_to_date, flt
 from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
-
+from erpnext import get_default_company
 
 class Subscription(Document):
 	def before_insert(self):
@@ -243,6 +245,7 @@
 		self.validate_plans_billing_cycle(self.get_billing_cycle_and_interval())
 		self.validate_end_date()
 		self.validate_to_follow_calendar_months()
+		self.cost_center = erpnext.get_default_cost_center(self.get('company'))
 
 	def validate_trial_period(self):
 		"""
@@ -304,6 +307,14 @@
 		doctype = 'Sales Invoice' if self.party_type == 'Customer' else 'Purchase Invoice'
 
 		invoice = frappe.new_doc(doctype)
+
+		# For backward compatibility
+		# Earlier subscription didn't had any company field
+		company = self.get('company') or get_default_company()
+		if not company:
+			frappe.throw(_("Company is mandatory was generating invoice. Please set default company in Global Defaults"))
+
+		invoice.company = company
 		invoice.set_posting_time = 1
 		invoice.posting_date = self.current_invoice_start if self.generate_invoice_at_period_start \
 			else self.current_invoice_end
@@ -330,6 +341,7 @@
 		# for that reason
 		items_list = self.get_items_from_plans(self.plans, prorate)
 		for item in items_list:
+			item['cost_center'] = self.cost_center
 			invoice.append('items', item)
 
 		# Taxes
@@ -380,7 +392,8 @@
 		Returns the `Item`s linked to `Subscription Plan`
 		"""
 		if prorate:
-			prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start)
+			prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start,
+				self.generate_invoice_at_period_start)
 
 		items = []
 		party = self.party
@@ -583,10 +596,13 @@
 
 	return calendar_months
 
-def get_prorata_factor(period_end, period_start):
-	diff = flt(date_diff(nowdate(), period_start) + 1)
-	plan_days = flt(date_diff(period_end, period_start) + 1)
-	prorate_factor = diff / plan_days
+def get_prorata_factor(period_end, period_start, is_prepaid):
+	if is_prepaid:
+		prorate_factor = 1
+	else:
+		diff = flt(date_diff(nowdate(), period_start) + 1)
+		plan_days = flt(date_diff(period_end, period_start) + 1)
+		prorate_factor = diff / plan_days
 
 	return prorate_factor
 
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index c17fccd..7c58e98 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -321,7 +321,8 @@
 
 		self.assertEqual(
 			flt(
-				get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start),
+				get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start,
+					subscription.generate_invoice_at_period_start),
 				2),
 			flt(prorate_factor, 2)
 		)
@@ -561,9 +562,7 @@
 		current_inv = subscription.get_current_invoice()
 		self.assertEqual(current_inv.status, "Unpaid")
 
-		diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
-		plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
-		prorate_factor = flt(diff / plan_days)
+		prorate_factor = 1
 
 		self.assertEqual(flt(current_inv.grand_total, 2), flt(prorate_factor * 900, 2))
 
diff --git a/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.json b/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.json
index f54e887..8a0d1de 100644
--- a/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.json
+++ b/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.json
@@ -13,21 +13,28 @@
    "fieldname": "document_type",
    "fieldtype": "Link",
    "label": "Document Type ",
+   "no_copy": 1,
    "options": "DocType",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "invoice",
    "fieldtype": "Dynamic Link",
    "in_list_view": 1,
    "label": "Invoice",
+   "no_copy": 1,
    "options": "document_type",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-06-01 22:23:54.462718",
+ "modified": "2021-02-09 15:43:32.026233",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Subscription Invoice",
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 32ad4cb..961bdb1 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -12,37 +12,62 @@
 class TaxWithholdingCategory(Document):
 	pass
 
-def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None):
+def get_party_details(inv):
+	party_type, party = '', ''
 
+	if inv.doctype == 'Sales Invoice':
+		party_type = 'Customer'
+		party = inv.customer
+	else:
+		party_type = 'Supplier'
+		party = inv.supplier
+	
+	return party_type, party
+
+def get_party_tax_withholding_details(inv, tax_withholding_category=None):
 	pan_no = ''
-	suppliers = []
+	parties = []
+	party_type, party = get_party_details(inv)
 
 	if not tax_withholding_category:
-		tax_withholding_category, pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, ['tax_withholding_category', 'pan'])
+		tax_withholding_category, pan_no = frappe.db.get_value(party_type, party, ['tax_withholding_category', 'pan'])
 
 	if not tax_withholding_category:
 		return
 
+	# if tax_withholding_category passed as an argument but not pan_no
 	if not pan_no:
-		pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, 'pan')
+		pan_no = frappe.db.get_value(party_type, party, 'pan')
 
 	# Get others suppliers with the same PAN No
 	if pan_no:
-		suppliers = [d.name for d in  frappe.get_all('Supplier', fields=['name'], filters={'pan': pan_no})]
+		parties = frappe.get_all(party_type, filters={ 'pan': pan_no }, pluck='name')
 
-	if not suppliers:
-		suppliers.append(ref_doc.supplier)
+	if not parties:
+		parties.append(party)
 
-	fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
-	tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
+	fiscal_year = get_fiscal_year(inv.posting_date, company=inv.company)
+	tax_details = get_tax_withholding_details(tax_withholding_category, fiscal_year[0], inv.company)
+
 	if not tax_details:
 		frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
-			.format(tax_withholding_category, ref_doc.company))
+			.format(tax_withholding_category, inv.company))
 
-	tds_amount = get_tds_amount(suppliers, ref_doc.net_total, ref_doc.company,
-		tax_details, fy,  ref_doc.posting_date, pan_no)
+	if party_type == 'Customer' and not tax_details.cumulative_threshold:
+		# TCS is only chargeable on sum of invoiced value
+		frappe.throw(_('Tax Withholding Category {} against Company {} for Customer {} should have Cumulative Threshold value.')
+			.format(tax_withholding_category, inv.company, party))
 
-	tax_row = get_tax_row(tax_details, tds_amount)
+	tax_amount, tax_deducted = get_tax_amount(
+		party_type, parties,
+		inv, tax_details,
+		fiscal_year, pan_no
+	)
+
+	if party_type == 'Supplier':
+		tax_row = get_tax_row_for_tds(tax_details, tax_amount)
+	else:
+		tax_row = get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted)
 
 	return tax_row
 
@@ -69,147 +94,254 @@
 
 	frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
 
-def get_tax_row(tax_details, tds_amount):
-
-	return {
+def get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted):
+	row = {
 		"category": "Total",
-		"add_deduct_tax": "Deduct",
 		"charge_type": "Actual",
-		"account_head": tax_details.account_head,
+		"tax_amount": tax_amount,
 		"description": tax_details.description,
-		"tax_amount": tds_amount
+		"account_head": tax_details.account_head
 	}
 
-def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_details, posting_date, pan_no=None):
-	fiscal_year, year_start_date, year_end_date = fiscal_year_details
-	tds_amount = 0
-	tds_deducted = 0
+	if tax_deducted:
+		# TCS already deducted on previous invoices
+		# So, TCS will be calculated by 'Previous Row Total'
 
-	def _get_tds(amount, rate):
-		if amount <= 0:
-			return 0
-
-		return amount * rate / 100
-
-	ldc_name = frappe.db.get_value('Lower Deduction Certificate',
-		{
-			'pan_no': pan_no,
-			'fiscal_year': fiscal_year
-		}, 'name')
-	ldc = ''
-
-	if ldc_name:
-		ldc = frappe.get_doc('Lower Deduction Certificate', ldc_name)
-
-	entries = frappe.db.sql("""
-			select voucher_no, credit
-			from `tabGL Entry`
-			where company = %s and
-			party in %s and fiscal_year=%s and credit > 0
-			and is_opening = 'No'
-		""", (company, tuple(suppliers), fiscal_year), as_dict=1)
-
-	vouchers = [d.voucher_no for d in entries]
-	advance_vouchers = get_advance_vouchers(suppliers, fiscal_year=fiscal_year, company=company)
-
-	tds_vouchers = vouchers + advance_vouchers
-
-	if tds_vouchers:
-		tds_deducted = frappe.db.sql("""
-			SELECT sum(credit) FROM `tabGL Entry`
-			WHERE
-				account=%s and fiscal_year=%s and credit > 0
-				and voucher_no in ({0})""". format(','.join(['%s'] * len(tds_vouchers))),
-				((tax_details.account_head, fiscal_year) + tuple(tds_vouchers)))
-
-		tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
-
-	if tds_deducted:
-		if ldc:
-			limit_consumed = frappe.db.get_value('Purchase Invoice',
-				{
-					'supplier': ('in', suppliers),
-					'apply_tds': 1,
-					'docstatus': 1
-				}, 'sum(net_total)')
-
-		if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total,
-			ldc.certificate_limit):
-
-			tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
+		taxes_excluding_tcs = [d for d in inv.taxes if d.account_head != tax_details.account_head]
+		if taxes_excluding_tcs:
+			# chargeable amount is the total amount after other charges are applied
+			row.update({
+				"charge_type": "On Previous Row Total",
+				"row_id": len(taxes_excluding_tcs),
+				"rate": tax_details.rate
+			})
 		else:
-			tds_amount = _get_tds(net_total, tax_details.rate)
-	else:
-		supplier_credit_amount = frappe.get_all('Purchase Invoice',
-			fields = ['sum(net_total)'],
-			filters = {'name': ('in', vouchers), 'docstatus': 1, "apply_tds": 1}, as_list=1)
+			# if only TCS is to be charged, then net total is chargeable amount
+			row.update({
+				"charge_type": "On Net Total",
+				"rate": tax_details.rate
+			})
 
-		supplier_credit_amount = (supplier_credit_amount[0][0]
-			if supplier_credit_amount and supplier_credit_amount[0][0] else 0)
+	return row
 
-		jv_supplier_credit_amt = frappe.get_all('Journal Entry Account',
-			fields = ['sum(credit_in_account_currency)'],
-			filters = {
-				'parent': ('in', vouchers), 'docstatus': 1,
-				'party': ('in', suppliers),
-				'reference_type': ('not in', ['Purchase Invoice'])
-			}, as_list=1)
+def get_tax_row_for_tds(tax_details, tax_amount):
+	return {
+		"category": "Total",
+		"charge_type": "Actual",
+		"tax_amount": tax_amount,
+		"add_deduct_tax": "Deduct",
+		"description": tax_details.description,
+		"account_head": tax_details.account_head
+	}
 
-		supplier_credit_amount += (jv_supplier_credit_amt[0][0]
-			if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
+def get_lower_deduction_certificate(fiscal_year, pan_no):
+	ldc_name = frappe.db.get_value('Lower Deduction Certificate', { 'pan_no': pan_no, 'fiscal_year': fiscal_year }, 'name')
+	if ldc_name:
+		return frappe.get_doc('Lower Deduction Certificate', ldc_name)
 
-		supplier_credit_amount += net_total
+def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, pan_no=None):
+	fiscal_year = fiscal_year_details[0]
 
-		debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date)
-		supplier_credit_amount -= debit_note_amount
+	vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
+	advance_vouchers = get_advance_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
+	taxable_vouchers = vouchers + advance_vouchers
 
-		if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
-			or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
+	tax_deducted = 0
+	if taxable_vouchers:
+		tax_deducted = get_deducted_tax(taxable_vouchers, fiscal_year, tax_details)
 
-			if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total,
-				ldc.certificate_limit):
-				tds_amount = get_ltds_amount(supplier_credit_amount, 0, ldc.certificate_limit, ldc.rate,
-					tax_details)
+	tax_amount = 0
+	posting_date = inv.posting_date
+	if party_type == 'Supplier':
+		ldc = get_lower_deduction_certificate(fiscal_year, pan_no)
+		if tax_deducted:
+			net_total = inv.net_total
+			if ldc:
+				tax_amount = get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total)
 			else:
-				tds_amount = _get_tds(supplier_credit_amount, tax_details.rate)
+				tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
+		else:
+			tax_amount = get_tds_amount(
+				ldc, parties, inv, tax_details,
+				fiscal_year_details, tax_deducted, vouchers
+			)
+
+	elif party_type == 'Customer':
+		if tax_deducted:
+			# if already TCS is charged, then amount will be calculated based on 'Previous Row Total'
+			tax_amount = 0
+		else:
+			#  if no TCS has been charged in FY,
+			# then chargeable value is "prev invoices + advances" value which cross the threshold
+			tax_amount = get_tcs_amount(
+				parties, inv, tax_details,
+				fiscal_year_details, vouchers, advance_vouchers
+			)
+
+	return tax_amount, tax_deducted
+
+def get_invoice_vouchers(parties, fiscal_year, company, party_type='Supplier'):
+	dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit'
+
+	filters = {
+		dr_or_cr: ['>', 0],
+		'company': company,
+		'party_type': party_type,
+		'party': ['in', parties],
+		'fiscal_year': fiscal_year,
+		'is_opening': 'No',
+		'is_cancelled': 0
+	}
+
+	return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck="voucher_no") or [""]
+
+def get_advance_vouchers(parties, fiscal_year=None, company=None, from_date=None, to_date=None, party_type='Supplier'):
+	# for advance vouchers, debit and credit is reversed
+	dr_or_cr = 'debit' if party_type == 'Supplier' else 'credit'
+
+	filters = {
+		dr_or_cr: ['>', 0],
+		'is_opening': 'No',
+		'is_cancelled': 0,
+		'party_type': party_type,
+		'party': ['in', parties],
+		'against_voucher': ['is', 'not set']
+	}
+
+	if fiscal_year:
+		filters['fiscal_year'] = fiscal_year
+	if company:
+		filters['company'] = company
+	if from_date and to_date:
+		filters['posting_date'] = ['between', (from_date, to_date)]
+
+	return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck='voucher_no') or [""]
+
+def get_deducted_tax(taxable_vouchers, fiscal_year, tax_details):
+	# check if TDS / TCS account is already charged on taxable vouchers
+	filters = {
+		'is_cancelled': 0,
+		'credit': ['>', 0],
+		'fiscal_year': fiscal_year,
+		'account': tax_details.account_head,
+		'voucher_no': ['in', taxable_vouchers],
+	}
+	field = "sum(credit)"
+
+	return frappe.db.get_value('GL Entry', filters, field) or 0.0
+
+def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers):
+	tds_amount = 0
+
+	supp_credit_amt = frappe.db.get_value('Purchase Invoice', {
+		'name': ('in', vouchers), 'docstatus': 1, 'apply_tds': 1
+	}, 'sum(net_total)') or 0.0
+
+	supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', {
+		'parent': ('in', vouchers), 'docstatus': 1,
+		'party': ('in', parties), 'reference_type': ('!=', 'Purchase Invoice')
+	}, 'sum(credit_in_account_currency)') or 0.0
+
+	supp_credit_amt += supp_jv_credit_amt
+	supp_credit_amt += inv.net_total
+
+	debit_note_amount = get_debit_note_amount(parties, fiscal_year_details, inv.company)
+	supp_credit_amt -= debit_note_amount
+
+	threshold = tax_details.get('threshold', 0)
+	cumulative_threshold = tax_details.get('cumulative_threshold', 0)
+
+	if ((threshold and supp_credit_amt >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
+		if ldc and is_valid_certificate(
+			ldc.valid_from, ldc.valid_upto,
+			inv.posting_date, tax_deducted,
+			inv.net_total, ldc.certificate_limit
+		):
+			tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
+		else:
+			tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0
 
 	return tds_amount
 
-def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=None, to_date=None):
-	condition = "fiscal_year=%s" % fiscal_year
+def get_tcs_amount(parties, inv, tax_details, fiscal_year_details, vouchers, adv_vouchers):
+	tcs_amount = 0
+	fiscal_year, _, _ = fiscal_year_details
+
+	# sum of debit entries made from sales invoices
+	invoiced_amt = frappe.db.get_value('GL Entry', {
+		'is_cancelled': 0,
+		'party': ['in', parties],
+		'company': inv.company,
+		'voucher_no': ['in', vouchers],
+	}, 'sum(debit)') or 0.0
+
+	# sum of credit entries made from PE / JV with unset 'against voucher'
+	advance_amt = frappe.db.get_value('GL Entry', {
+		'is_cancelled': 0,
+		'party': ['in', parties],
+		'company': inv.company,
+		'voucher_no': ['in', adv_vouchers],
+	}, 'sum(credit)') or 0.0
+
+	# sum of credit entries made from sales invoice
+	credit_note_amt = frappe.db.get_value('GL Entry', {
+		'is_cancelled': 0,
+		'credit': ['>', 0],
+		'party': ['in', parties],
+		'fiscal_year': fiscal_year,
+		'company': inv.company,
+		'voucher_type': 'Sales Invoice',
+	}, 'sum(credit)') or 0.0
+
+	cumulative_threshold = tax_details.get('cumulative_threshold', 0)
+
+	current_invoice_total = get_invoice_total_without_tcs(inv, tax_details)
+	total_invoiced_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt
+
+	if ((cumulative_threshold and total_invoiced_amt >= cumulative_threshold)):
+		chargeable_amt = total_invoiced_amt - cumulative_threshold
+		tcs_amount = chargeable_amt * tax_details.rate / 100 if chargeable_amt > 0 else 0
+
+	return tcs_amount
+
+def get_invoice_total_without_tcs(inv, tax_details):
+	tcs_tax_row = [d for d in inv.taxes if d.account_head == tax_details.account_head]
+	tcs_tax_row_amount = tcs_tax_row[0].base_tax_amount if tcs_tax_row else 0
+
+	return inv.grand_total - tcs_tax_row_amount
+
+def get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total):
+	tds_amount = 0
+	limit_consumed = frappe.db.get_value('Purchase Invoice', {
+		'supplier': ('in', parties),
+		'apply_tds': 1,
+		'docstatus': 1
+	}, 'sum(net_total)')
+
+	if is_valid_certificate(
+		ldc.valid_from, ldc.valid_upto,
+		posting_date, limit_consumed,
+		net_total, ldc.certificate_limit
+	):
+		tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
+	
+	return tds_amount
+
+def get_debit_note_amount(suppliers, fiscal_year_details, company=None):
+	_, year_start_date, year_end_date = fiscal_year_details
+
+	filters = {
+		'supplier': ['in', suppliers],
+		'is_return': 1,
+		'docstatus': 1,
+		'posting_date': ['between', (year_start_date, year_end_date)]
+	}
+	fields = ['abs(sum(net_total)) as net_total']
 
 	if company:
-		condition += "and company =%s" % (company)
-	if from_date and to_date:
-		condition += "and posting_date between %s and %s" % (from_date, to_date)
+		filters['company'] = company
 
-	## Appending the same supplier again if length of suppliers list is 1
-	## since tuple of single element list contains None, For example ('Test Supplier 1', )
-	## and the below query fails
-	if len(suppliers) == 1:
-		suppliers.append(suppliers[0])
-
-	return frappe.db.sql_list("""
-		select distinct voucher_no
-		from `tabGL Entry`
-		where party in %s and %s and debit > 0
-		and is_opening = 'No'
-	""", (tuple(suppliers), condition)) or []
-
-def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
-	condition = "and 1=1"
-	if company:
-		condition = " and company=%s " % company
-
-	if len(suppliers) == 1:
-		suppliers.append(suppliers[0])
-
-	return flt(frappe.db.sql("""
-		select abs(sum(net_total))
-		from `tabPurchase Invoice`
-		where supplier in %s and is_return=1 and docstatus=1
-			and posting_date between %s and %s %s
-	""", (tuple(suppliers), year_start_date, year_end_date, condition)))
+	return frappe.get_all('Purchase Invoice', filters, fields)[0].get('net_total') or 0.0
 
 def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
 	if current_amount < (certificate_limit - deducted_amount):
@@ -227,4 +359,4 @@
 			certificate_limit > deducted_amount):
 		valid = True
 
-	return valid
\ No newline at end of file
+	return valid
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 ef77674..9ce8e3f 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
@@ -9,7 +9,7 @@
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.buying.doctype.supplier.test_supplier import create_supplier
 
-test_dependencies = ["Supplier Group"]
+test_dependencies = ["Supplier Group", "Customer Group"]
 
 class TestTaxWithholdingCategory(unittest.TestCase):
 	@classmethod
@@ -18,6 +18,9 @@
 		create_records()
 		create_tax_with_holding_category()
 
+	def tearDown(self):
+		cancel_invoices()
+
 	def test_cumulative_threshold_tds(self):
 		frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS")
 		invoices = []
@@ -128,9 +131,59 @@
 		for d in invoices:
 			d.cancel()
 
+	def test_cumulative_threshold_tcs(self):
+		frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS")
+		invoices = []
+
+		# create invoices for lower than single threshold tax rate
+		for _ in range(2):
+			si = create_sales_invoice(customer = "Test TCS Customer")
+			si.submit()
+			invoices.append(si)
+
+		# create another invoice whose total when added to previously created invoice,
+		# surpasses cumulative threshhold
+		si = create_sales_invoice(customer = "Test TCS Customer", rate=12000)
+		si.submit()
+
+		# assert tax collection on total invoice amount created until now
+		tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC'])
+		self.assertEqual(tcs_charged, 200)
+		self.assertEqual(si.grand_total, 12200)
+		invoices.append(si)
+
+		# TCS is already collected once, so going forward system will collect TCS on every invoice
+		si = create_sales_invoice(customer = "Test TCS Customer", rate=5000)
+		si.submit()
+
+		tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC'])
+		self.assertEqual(tcs_charged, 500)
+		invoices.append(si)
+
+		#delete invoices to avoid clashing
+		for d in invoices:
+			d.cancel()
+
+def cancel_invoices():
+	purchase_invoices = frappe.get_all("Purchase Invoice", {
+		'supplier': ['in', ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']],
+		'docstatus': 1
+	}, pluck="name")
+
+	sales_invoices = frappe.get_all("Sales Invoice", {
+		'customer': 'Test TCS Customer',
+		'docstatus': 1
+	}, pluck="name")
+
+	for d in purchase_invoices:
+		frappe.get_doc('Purchase Invoice', d).cancel()
+	
+	for d in sales_invoices:
+		frappe.get_doc('Sales Invoice', d).cancel()
+
 def create_purchase_invoice(**args):
 	# return sales invoice doc object
-	item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
+	item = frappe.db.get_value('Item', {'item_name': 'TDS Item'}, "name")
 
 	args = frappe._dict(args)
 	pi = frappe.get_doc({
@@ -145,7 +198,7 @@
 		"taxes": [],
 		"items": [{
 			'doctype': 'Purchase Invoice Item',
-			'item_code': item.name,
+			'item_code': item,
 			'qty': args.qty or 1,
 			'rate': args.rate or 10000,
 			'cost_center': 'Main - _TC',
@@ -156,6 +209,33 @@
 	pi.save()
 	return pi
 
+def create_sales_invoice(**args):
+	# return sales invoice doc object
+	item = frappe.db.get_value('Item', {'item_name': 'TCS Item'}, "name")
+
+	args = frappe._dict(args)
+	si = frappe.get_doc({
+		"doctype": "Sales Invoice",
+		"posting_date": today(),
+		"customer": args.customer,
+		"company": '_Test Company',
+		"taxes_and_charges": "",
+		"currency": "INR",
+		"debit_to": "Debtors - _TC",
+		"taxes": [],
+		"items": [{
+			'doctype': 'Sales Invoice Item',
+			'item_code': item,
+			'qty': args.qty or 1,
+			'rate': args.rate or 10000,
+			'cost_center': 'Main - _TC',
+			'expense_account': 'Cost of Goods Sold - _TC'
+		}]
+	})
+
+	si.save()
+	return si
+
 def create_records():
 	# create a new suppliers
 	for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']:
@@ -168,7 +248,17 @@
 			"doctype": "Supplier",
 		}).insert()
 
-	# create an item
+	for name in ['Test TCS Customer']:
+		if frappe.db.exists('Customer', name):
+			continue
+
+		frappe.get_doc({
+			"customer_group": "_Test Customer Group",
+			"customer_name": name,
+			"doctype": "Customer"
+		}).insert()
+
+	# create item
 	if not frappe.db.exists('Item', "TDS Item"):
 		frappe.get_doc({
 			"doctype": "Item",
@@ -178,7 +268,16 @@
 			"is_stock_item": 0,
 		}).insert()
 
-	# create an account
+	if not frappe.db.exists('Item', "TCS Item"):
+		frappe.get_doc({
+			"doctype": "Item",
+			"item_code": "TCS Item",
+			"item_name": "TCS Item",
+			"item_group": "All Item Groups",
+			"is_stock_item": 1
+		}).insert()
+
+	# create tds account
 	if not frappe.db.exists("Account", "TDS - _TC"):
 		frappe.get_doc({
 			'doctype': 'Account',
@@ -189,6 +288,17 @@
 			'root_type': 'Asset'
 		}).insert()
 
+	# create tcs account
+	if not frappe.db.exists("Account", "TCS - _TC"):
+		frappe.get_doc({
+			'doctype': 'Account',
+			'company': '_Test Company',
+			'account_name': 'TCS',
+			'parent_account': 'Duties and Taxes - _TC',
+			'report_type': 'Balance Sheet',
+			'root_type': 'Liability'
+		}).insert()
+
 def create_tax_with_holding_category():
 	fiscal_year = get_fiscal_year(today(), company="_Test Company")[0]
 
@@ -210,6 +320,23 @@
 			}]
 		}).insert()
 
+	if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TCS"):
+		frappe.get_doc({
+			"doctype": "Tax Withholding Category",
+			"name": "Cumulative Threshold TCS",
+			"category_name": "10% TCS",
+			"rates": [{
+				'fiscal_year': fiscal_year,
+				'tax_withholding_rate': 10,
+				'single_threshold': 0,
+				'cumulative_threshold': 30000.00
+			}],
+			"accounts": [{
+				'company': '_Test Company',
+				'account': 'TCS - _TC'
+			}]
+		}).insert()
+
 	# Single thresold
 	if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"):
 		frappe.get_doc({
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index b42c0c6..dac0c21 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -196,7 +196,7 @@
 
 	if not round_off_gle:
 		for k in ["voucher_type", "voucher_no", "company",
-			"posting_date", "remarks", "is_opening"]:
+			"posting_date", "remarks"]:
 				round_off_gle[k] = gl_map[0][k]
 
 	round_off_gle.update({
@@ -208,6 +208,7 @@
 		"cost_center": round_off_cost_center,
 		"party_type": None,
 		"party": None,
+		"is_opening": "No",
 		"against_voucher_type": None,
 		"against_voucher": None
 	})
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 5eb2aab..89a05b1 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -897,17 +897,18 @@
 		frappe.db.sql("""delete from `tabGL Entry`
 			where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
 
-
 	if not warehouse_account:
 		warehouse_account = get_warehouse_account_map(company)
 
+	precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit")) or 2
+
 	gle = get_voucherwise_gl_entries(stock_vouchers, posting_date)
 	for voucher_type, voucher_no in stock_vouchers:
 		existing_gle = gle.get((voucher_type, voucher_no), [])
 		voucher_obj = frappe.get_cached_doc(voucher_type, voucher_no)
 		expected_gle = voucher_obj.get_gl_entries(warehouse_account)
 		if expected_gle:
-			if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
+			if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
 				_delete_gl_entries(voucher_type, voucher_no)
 				voucher_obj.make_gl_entries(gl_entries=expected_gle, from_repost=True)
 		else:
@@ -953,16 +954,17 @@
 
 	return gl_entries
 
-def compare_existing_and_expected_gle(existing_gle, expected_gle):
+def compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
 	matched = True
 	for entry in expected_gle:
 		account_existed = False
 		for e in existing_gle:
 			if entry.account == e.account:
 				account_existed = True
-			if entry.account == e.account and entry.against_account == e.against_account \
-					and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center) \
-					and (entry.debit != e.debit or entry.credit != e.credit):
+			if (entry.account == e.account and entry.against_account == e.against_account
+					and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center)
+					and ( flt(entry.debit, precision) != flt(e.debit, precision) or
+						flt(entry.credit, precision) != flt(e.credit, precision))):
 				matched = False
 				break
 		if not account_existed:
diff --git a/erpnext/assets/doctype/asset_category/asset_category.json b/erpnext/assets/doctype/asset_category/asset_category.json
index b7d1226..a25f546 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.json
+++ b/erpnext/assets/doctype/asset_category/asset_category.json
@@ -19,7 +19,6 @@
  ],
  "fields": [
   {
-   "depends_on": "eval:!doc.asset_category_name",
    "fieldname": "asset_category_name",
    "fieldtype": "Data",
    "in_list_view": 1,
@@ -67,7 +66,7 @@
   }
  ],
  "links": [],
- "modified": "2021-01-22 12:31:14.425319",
+ "modified": "2021-02-24 15:05:38.621803",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Category",
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
index cc75a0a..148c1a6 100644
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
@@ -117,7 +117,7 @@
 			return response
 		except Exception as e:
 			delay = math.pow(4, x) * 125
-			frappe.log_error(message=e, title=str(mws_method))
+			frappe.log_error(message=e, title=f'Method "{mws_method.__name__}" failed')
 			time.sleep(delay)
 			continue
 
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 39d3659..59639ff 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -393,6 +393,15 @@
 
 communication_doctypes = ["Customer", "Supplier"]
 
+accounting_dimension_doctypes = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
+	"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
+	"Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
+	"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
+	"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
+	"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
+	"Subscription Plan"
+]
+
 regional_overrides = {
 	'France': {
 		'erpnext.tests.test_regional.test_method': 'erpnext.regional.france.utils.test_method'
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index f999635..d8aae66 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -138,7 +138,7 @@
  "idx": 1,
  "issingle": 1,
  "links": [],
- "modified": "2020-08-27 14:30:28.995324",
+ "modified": "2021-02-25 12:31:14.947865",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "HR Settings",
@@ -155,5 +155,6 @@
   }
  ],
  "sort_field": "modified",
- "sort_order": "ASC"
-}
+ "sort_order": "ASC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/skill/skill.json b/erpnext/hr/doctype/skill/skill.json
index 5182973..a10381f 100644
--- a/erpnext/hr/doctype/skill/skill.json
+++ b/erpnext/hr/doctype/skill/skill.json
@@ -3,7 +3,7 @@
  "allow_events_in_timeline": 0,
  "allow_guest_to_view": 0,
  "allow_import": 0,
- "allow_rename": 0,
+ "allow_rename": 1,
  "autoname": "field:skill_name",
  "beta": 0,
  "creation": "2019-04-16 09:54:39.486915",
@@ -56,7 +56,7 @@
  "issingle": 0,
  "istable": 0,
  "max_attachments": 0,
- "modified": "2019-04-16 09:55:00.536328",
+ "modified": "2021-02-24 09:55:00.536328",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Skill",
@@ -110,4 +110,4 @@
  "track_changes": 1,
  "track_seen": 0,
  "track_views": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
index 06f7f98..3200363 100644
--- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
+++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
@@ -1,12 +1,13 @@
 import frappe
 from frappe import _
+from frappe.utils import getdate, get_time
 from erpnext.stock.stock_ledger import update_entries_after
 from erpnext.accounts.utils import update_gl_entries_after
 
 def execute():
 	frappe.reload_doc('stock', 'doctype', 'repost_item_valuation')
 
-	reposting_project_deployed_on = frappe.db.get_value("DocType", "Repost Item Valuation", "creation")
+	reposting_project_deployed_on = get_creation_time()
 
 	data = frappe.db.sql('''
 		SELECT
@@ -40,7 +41,14 @@
 
 
 	print("Reposting General Ledger Entries...")
+	posting_date = getdate(reposting_project_deployed_on)
+	posting_time = get_time(reposting_project_deployed_on)
+
 	for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
-		update_gl_entries_after('2020-12-25', '01:58:55', company=row.name)
+		update_gl_entries_after(posting_date, posting_time, company=row.name)
 
 	frappe.db.auto_commit_on_many_writes = 0
+
+def get_creation_time():
+	return frappe.db.sql(''' SELECT create_time FROM
+		INFORMATION_SCHEMA.TABLES where TABLE_NAME = "tabRepost Item Valuation" ''', as_list=1)[0][0]
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 3570a0f..077011a 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -75,24 +75,27 @@
 			frm.add_custom_button(__('Cancelled'), () => {
 				frm.events.set_status(frm, 'Cancelled');
 			}, __('Set Status'));
-		}
 
-		if (frappe.model.can_read("Task")) {
-			frm.add_custom_button(__("Gantt Chart"), function () {
-				frappe.route_options = {
-					"project": frm.doc.name
-				};
-				frappe.set_route("List", "Task", "Gantt");
-			});
 
-			frm.add_custom_button(__("Kanban Board"), () => {
-				frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
-					project: frm.doc.project_name
-				}).then(() => {
-					frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name);
+			if (frappe.model.can_read("Task")) {
+				frm.add_custom_button(__("Gantt Chart"), function () {
+					frappe.route_options = {
+						"project": frm.doc.name
+					};
+					frappe.set_route("List", "Task", "Gantt");
 				});
-			});
+
+				frm.add_custom_button(__("Kanban Board"), () => {
+					frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
+						project: frm.doc.project_name
+					}).then(() => {
+						frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name);
+					});
+				});
+			}
 		}
+
+
 	},
 
 	create_duplicate: function(frm) {
@@ -135,4 +138,4 @@
 		frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
 	});
 
-}
\ No newline at end of file
+}
diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py
index d85c826..6290538 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.py
@@ -37,7 +37,7 @@
 
 		task1 = task_exists("Test Template Task Parent")
 		if not task1:
-			task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=1)
+			task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=4)
 
 		task2 = task_exists("Test Template Task Child 1")
 		if not task2:
@@ -52,7 +52,7 @@
 		tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name', 'parent_task'], dict(project=project.name), order_by='creation asc')
 
 		self.assertEqual(tasks[0].subject, 'Test Template Task Parent')
-		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 1))
+		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 4))
 
 		self.assertEqual(tasks[1].subject, 'Test Template Task Child 1')
 		self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3))
diff --git a/erpnext/projects/doctype/project_template_task/project_template_task.json b/erpnext/projects/doctype/project_template_task/project_template_task.json
index 69530b1..16caaa2 100644
--- a/erpnext/projects/doctype/project_template_task/project_template_task.json
+++ b/erpnext/projects/doctype/project_template_task/project_template_task.json
@@ -20,6 +20,7 @@
   },
   {
    "columns": 6,
+   "fetch_from": "task.subject",
    "fieldname": "subject",
    "fieldtype": "Read Only",
    "in_list_view": 1,
@@ -28,7 +29,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-01-07 15:13:40.995071",
+ "modified": "2021-02-24 15:18:49.095071",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Project Template Task",
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 16368ae..855ff5f 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -30,6 +30,7 @@
 
 	def validate(self):
 		self.validate_dates()
+		self.validate_parent_expected_end_date()
 		self.validate_parent_project_dates()
 		self.validate_progress()
 		self.validate_status()
@@ -45,6 +46,12 @@
 			frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
 				frappe.bold("Actual End Date")))
 
+	def validate_parent_expected_end_date(self):
+		if self.parent_task:
+			parent_exp_end_date = frappe.db.get_value("Task", self.parent_task, "exp_end_date")
+			if parent_exp_end_date and getdate(self.get("exp_end_date")) > getdate(parent_exp_end_date):
+				frappe.throw(_("Expected End Date should be less than or equal to parent task's Expected End Date {0}.").format(getdate(parent_exp_end_date)))
+
 	def validate_parent_project_dates(self):
 		if not self.project or frappe.flags.in_test:
 			return
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index c39609b..e5bd4d7 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -513,6 +513,7 @@
 	}, {
 		fieldtype:'Currency',
 		fieldname:"rate",
+		options: "currency",
 		default: 0,
 		read_only: 0,
 		in_list_view: 1,
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 322fa18..eea85cd 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -10,6 +10,7 @@
 import json
 import base64
 import frappe
+import six
 import traceback
 import io
 from frappe import _, bold
@@ -108,11 +109,13 @@
 		pincode = 999999
 
 	return frappe._dict(dict(
-		gstin=d.gstin, legal_name=d.address_title,
-		location=d.city, pincode=d.pincode,
+		gstin=d.gstin,
+		legal_name=sanitize_for_json(d.address_title),
+		location=sanitize_for_json(d.city),
+		pincode=d.pincode,
 		state_code=d.gst_state_number,
-		address_line1=d.address_line1,
-		address_line2=d.address_line2
+		address_line1=sanitize_for_json(d.address_line1),
+		address_line2=sanitize_for_json(d.address_line2)
 	))
 
 def get_gstin_details(gstin):
@@ -146,8 +149,11 @@
 		)
 
 	return frappe._dict(dict(
-		gstin='URP', legal_name=address_title, location=city,
-		address_line1=address_line1, address_line2=address_line2,
+		gstin='URP',
+		legal_name=sanitize_for_json(address_title),
+		location=city,
+		address_line1=sanitize_for_json(address_line1),
+		address_line2=sanitize_for_json(address_line2),
 		pincode=999999, state_code=96, place_of_supply=96
 	))
 
@@ -160,7 +166,7 @@
 		item.update(d.as_dict())
 
 		item.sr_no = d.idx
-		item.description = json.dumps(d.item_name)[1:-1]
+		item.description = sanitize_for_json(d.item_name)
 
 		item.qty = abs(item.qty)
 		item.discount_amount = 0
@@ -326,7 +332,7 @@
 		buyer_details = get_overseas_address_details(invoice.customer_address)
 	else:
 		buyer_details = get_party_details(invoice.customer_address)
-		place_of_supply = get_place_of_supply(invoice, invoice.doctype) or invoice.billing_address_gstin
+		place_of_supply = get_place_of_supply(invoice, invoice.doctype) or sanitize_for_json(invoice.billing_address_gstin)
 		place_of_supply = place_of_supply[:2]
 		buyer_details.update(dict(place_of_supply=place_of_supply))
 
@@ -356,7 +362,7 @@
 		period_details=period_details, prev_doc_details=prev_doc_details,
 		export_details=export_details, eway_bill_details=eway_bill_details
 	)
-	einvoice = json.loads(einvoice)
+	einvoice = safe_json_load(einvoice)
 
 	validations = json.loads(read_json('einv_validation'))
 	errors = validate_einvoice(validations, einvoice)
@@ -371,6 +377,18 @@
 
 	return einvoice
 
+def safe_json_load(json_string):
+	JSONDecodeError = ValueError if six.PY2 else json.JSONDecodeError
+
+	try:
+		return json.loads(json_string)
+	except JSONDecodeError as e:
+		# print a snippet of 40 characters around the location where error occured
+		pos = e.pos
+		start, end = max(0, pos-20), min(len(json_string)-1, pos+20)
+		snippet = json_string[start:end]
+		frappe.throw(_("Error in input data. Please check for any special characters near following input: <br> {}").format(snippet))
+
 def validate_einvoice(validations, einvoice, errors=[]):
 	for fieldname, field_validation in validations.items():
 		value = einvoice.get(fieldname, None)
@@ -798,6 +816,13 @@
 		self.invoice.flags.ignore_validate = True
 		self.invoice.save()
 
+
+def sanitize_for_json(string):
+	"""Escape JSON specific characters from a string."""
+
+	# json.dumps adds double-quotes to the string. Indexing to remove them.
+	return json.dumps(string)[1:-1]
+
 @frappe.whitelist()
 def get_einvoice(doctype, docname):
 	invoice = frappe.get_doc(doctype, docname)
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 3c0652eb..7d5e84d 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -16,6 +16,8 @@
   "customer_name",
   "gender",
   "customer_type",
+  "pan",
+  "tax_withholding_category",
   "default_bank_account",
   "lead_name",
   "image",
@@ -479,13 +481,25 @@
    "fieldname": "dn_required",
    "fieldtype": "Check",
    "label": "Allow Sales Invoice Creation Without Delivery Note"
+  },
+  {
+   "fieldname": "pan",
+   "fieldtype": "Data",
+   "label": "PAN"
+  },
+  {
+   "fieldname": "tax_withholding_category",
+   "fieldtype": "Link",
+   "label": "Tax Withholding Category",
+   "options": "Tax Withholding Category"
   }
  ],
  "icon": "fa fa-user",
  "idx": 363,
  "image_field": "image",
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2021-01-06 19:35:25.418017",
+ "modified": "2021-01-28 12:54:57.258959",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Customer",
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 338a3cc..74b4bb0 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -498,10 +498,11 @@
 
 	async on_cart_update(args) {
 		frappe.dom.freeze();
+		let item_row = undefined;
 		try {
 			let { field, value, item } = args;
 			const { item_code, batch_no, serial_no, uom } = item;
-			let item_row = this.get_item_from_frm(item_code, batch_no, uom);
+			item_row = this.get_item_from_frm(item_code, batch_no, uom);
 
 			const item_selected_from_selector = field === 'qty' && value === "+1"
 
@@ -553,10 +554,12 @@
 				this.check_serial_batch_selection_needed(item_row) && this.edit_item_details_of(item_row);
 				this.update_cart_html(item_row);
 			}
+
 		} catch (error) {
 			console.log(error);
 		} finally {
 			frappe.dom.unfreeze();
+			return item_row;
 		}
 	}
 
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 044e803..9ab9eef 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -472,7 +472,8 @@
 		if (!frm) frm = this.events.get_frm();
 
 		this.render_net_total(frm.doc.net_total);
-		this.render_grand_total(frm.doc.grand_total);
+		const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? frm.doc.grand_total : frm.doc.rounded_total;
+		this.render_grand_total(grand_total);
 
 		const taxes = frm.doc.taxes.map(t => {
 			return {
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index 7c116e9..e0d5b73 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -152,6 +152,10 @@
 		this.item_group_field.toggle_label(false);
 	}
 
+	set_search_value(value) {
+		$(this.search_field.$input[0]).val(value).trigger("input");
+	}
+
 	bind_events() {
 		const me = this;
 		window.onScan = onScan;
@@ -159,7 +163,7 @@
 			onScan: (sScancode) => {
 				if (this.search_field && this.$component.is(':visible')) {
 					this.search_field.set_focus();
-					$(this.search_field.$input[0]).val(sScancode).trigger("input");
+					this.set_search_value(sScancode);
 					this.barcode_scanned = true;
 				}
 			}
@@ -178,6 +182,7 @@
 			uom = uom === "undefined" ? undefined : uom;
 
 			me.events.item_selected({ field: 'qty', value: "+1", item: { item_code, batch_no, serial_no, uom }});
+			me.set_search_value('');
 		});
 
 		this.search_field.$input.on('input', (e) => {
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index bcbac3b..22a279d 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -223,7 +223,8 @@
 
 			if (success) {
 				title = __("Payment Received");
-				if (amount >= doc.grand_total) {
+				const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? doc.grand_total : doc.rounded_total;
+				if (amount >= grand_total) {
 					frappe.dom.unfreeze();
 					message = __("Payment of {0} received successfully.", [format_currency(amount, doc.currency, 0)]);
 					this.events.submit_invoice();
@@ -243,7 +244,8 @@
 
 	auto_set_remaining_amount() {
 		const doc = this.events.get_frm().doc;
-		const remaining_amount = doc.grand_total - doc.paid_amount;
+		const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? doc.grand_total : doc.rounded_total;
+		const remaining_amount = grand_total - doc.paid_amount;
 		const current_value = this.selected_mode ? this.selected_mode.get_value() : undefined;
 		if (!current_value && remaining_amount > 0 && this.selected_mode) {
 			this.selected_mode.set_value(remaining_amount);
@@ -389,7 +391,7 @@
 	}
 
 	attach_cash_shortcuts(doc) {
-		const grand_total = doc.grand_total;
+		const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? doc.grand_total : doc.rounded_total;
 		const currency = doc.currency;
 
 		const shortcuts = this.get_cash_shortcuts(flt(grand_total));
@@ -499,7 +501,8 @@
 	update_totals_section(doc) {
 		if (!doc) doc = this.events.get_frm().doc;
 		const paid_amount = doc.paid_amount;
-		const remaining = doc.grand_total - doc.paid_amount;
+		const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? doc.grand_total : doc.rounded_total;
+		const remaining = grand_total - doc.paid_amount;
 		const change = doc.change_amount || remaining <= 0 ? -1 * remaining : undefined;
 		const currency = doc.currency;
 		const label = change ? __('Change') : __('To Be Paid');
@@ -507,7 +510,7 @@
 		this.$totals.html(
 			`<div class="col">
 				<div class="total-label">Grand Total</div>
-				<div class="value">${format_currency(doc.grand_total, currency)}</div>
+				<div class="value">${format_currency(grand_total, currency)}</div>
 			</div>
 			<div class="seperator-y"></div>
 			<div class="col">
diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py
index 9e240cc..283f7d5 100644
--- a/erpnext/stock/__init__.py
+++ b/erpnext/stock/__init__.py
@@ -38,7 +38,7 @@
 			frappe.flags.warehouse_account_map[company] = warehouse_account
 		else:
 			frappe.flags.warehouse_account_map = warehouse_account
-	
+
 	return frappe.flags.warehouse_account_map.get(company) or frappe.flags.warehouse_account_map
 
 def get_warehouse_account(warehouse, warehouse_account=None):
@@ -64,6 +64,10 @@
 	if not account and warehouse.company:
 		account = get_company_default_inventory_account(warehouse.company)
 
+	if not account and warehouse.company:
+		account = frappe.db.get_value('Account',
+			{'account_type': 'Stock', 'is_group': 0, 'company': warehouse.company}, 'name')
+
 	if not account and warehouse.company and not warehouse.is_group:
 		frappe.throw(_("Please set Account in Warehouse {0} or Default Inventory Account in Company {1}")
 			.format(warehouse.name, warehouse.company))
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index fcf7c26..6886c1b 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -521,8 +521,7 @@
    "fieldname": "has_variants",
    "fieldtype": "Check",
    "in_standard_filter": 1,
-   "label": "Has Variants",
-   "no_copy": 1
+   "label": "Has Variants"
   },
   {
    "default": "Item Attribute",
@@ -538,7 +537,6 @@
    "fieldtype": "Table",
    "hidden": 1,
    "label": "Attributes",
-   "no_copy": 1,
    "options": "Item Variant Attribute"
   },
   {
@@ -1068,7 +1066,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "max_attachments": 1,
- "modified": "2021-01-25 20:49:50.222976",
+ "modified": "2021-02-18 14:00:19.668049",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 550c849..d721014 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -295,7 +295,8 @@
 								"against": warehouse_account[d.warehouse]["account"],
 								"cost_center": d.cost_center,
 								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-								"credit": flt(amount["base_amount"]),
+								"credit": (flt(amount["base_amount"]) if (amount["base_amount"] or
+									account_currency!=self.company_currency) else flt(amount["amount"])),
 								"credit_in_account_currency": flt(amount["amount"]),
 								"project": d.project
 							}, item=d))
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 9cdc3cf..ea1b387 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -276,9 +276,10 @@
 					item_wise_qty.setdefault(d.item_code, []).append(d.qty)
 
 		for item_code, qty_list in iteritems(item_wise_qty):
-			if self.fg_completed_qty != sum(qty_list):
+			total = flt(sum(qty_list), frappe.get_precision("Stock Entry Detail", "qty"))
+			if self.fg_completed_qty != total:
 				frappe.throw(_("The finished product {0} quantity {1} and For Quantity {2} cannot be different")
-					.format(frappe.bold(item_code), frappe.bold(sum(qty_list)), frappe.bold(self.fg_completed_qty)))
+					.format(frappe.bold(item_code), frappe.bold(total), frappe.bold(self.fg_completed_qty)))
 
 	def validate_difference_account(self):
 		if not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index 167e80f..9fe12f9 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -49,8 +49,8 @@
 	},
 
 	refresh: function (frm) {
-		if (frm.doc.status !== "Closed" && frm.doc.agreement_status === "Ongoing") {
-			if (frm.doc.service_level_agreement) {
+		if (frm.doc.status !== "Closed") {
+			if (frm.doc.service_level_agreement && frm.doc.agreement_status === "Ongoing") {
 				frappe.call({
 					"method": "frappe.client.get",
 					args: {