Merge pull request #21600 from anupamvs/lead-customer-issue

fix: adding Email and Phone in Contact child table
diff --git a/.travis.yml b/.travis.yml
index 213445b..77d427e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
-dist: trusty
-
 language: python
+dist: trusty
 
 git:
   depth: 1
@@ -14,21 +13,10 @@
 
 jobs:
   include:
-  - name: "Python 2.7 Server Side Test"
-    python: 2.7
-    script: bench --site test_site run-tests --app erpnext --coverage
-
   - name: "Python 3.6 Server Side Test"
     python: 3.6
     script: bench --site test_site run-tests --app erpnext --coverage
 
-  - name: "Python 2.7 Patch Test"
-    python: 2.7
-    before_script:
-      - wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
-      - bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz
-    script: bench --site test_site migrate
-
   - name: "Python 3.6 Patch Test"
     python: 3.6
     before_script:
@@ -40,8 +28,7 @@
   - cd ~
   - nvm install 10
 
-  - git clone https://github.com/frappe/bench --depth 1
-  - pip install -e ./bench
+  - pip install frappe-bench
 
   - git clone https://github.com/frappe/frappe --branch $TRAVIS_BRANCH --depth 1
   - bench init --skip-assets --frappe-path ~/frappe --python $(which python) frappe-bench
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 786b9cf..38d8a62 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
 from erpnext.hooks import regional_overrides
 from frappe.utils import getdate
 
-__version__ = '12.0.0-dev'
+__version__ = '13.0.0-dev'
 
 def get_default_company(user=None):
 	'''Get default company for user'''
diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
index c3e2f7d..39bf4b0 100644
--- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
@@ -6,7 +6,7 @@
 from frappe import _
 from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate, get_link_to_form
 from erpnext.accounts.report.general_ledger.general_ledger import execute
-from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan
+from frappe.utils.dashboard import cache_source, get_from_date_from_timespan
 from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending
 
 from frappe.utils.nestedset import get_descendants_of
@@ -14,7 +14,7 @@
 @frappe.whitelist()
 @cache_source
 def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
-	to_date = None, timespan = None, time_interval = None):
+	to_date = None, timespan = None, time_interval = None, heatmap_year = None):
 	if chart_name:
 		chart = frappe.get_doc('Dashboard Chart', chart_name)
 	else:
diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py
new file mode 100644
index 0000000..cdd1661
--- /dev/null
+++ b/erpnext/accounts/dashboard_fixtures.py
@@ -0,0 +1,127 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+from erpnext import get_default_company
+
+import frappe
+import json
+
+
+def get_data():
+	data = frappe._dict({
+		"dashboards": [],
+		"charts": []
+	})
+	company = get_company_for_dashboards()
+	if company:
+		company_doc = frappe.get_doc("Company", company)
+		data.dashboards = get_dashboards()
+		data.charts = get_charts(company_doc)
+	return data
+
+def get_dashboards():
+	return [{
+		"name": "Accounts",
+		"dashboard_name": "Accounts",
+		"charts": [
+			{ "chart": "Outgoing Bills (Sales Invoice)" },
+			{ "chart": "Incoming Bills (Purchase Invoice)" },
+			{ "chart": "Bank Balance" },
+			{ "chart": "Income" },
+			{ "chart": "Expenses" }
+		]
+	}]
+
+def get_charts(company):
+	income_account = company.default_income_account or get_account("Income Account", company.name)
+	expense_account = company.default_expense_account or get_account("Expense Account", company.name)
+	bank_account = company.default_bank_account or get_account("Bank", company.name)
+
+	return [
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Quarterly",
+			"name": "Income",
+			"chart_name": "Income",
+			"timespan": "Last Year",
+			"color": None,
+			"filters_json": json.dumps({"company": company.name, "account": income_account}),
+			"source": "Account Balance Timeline",
+			"chart_type": "Custom",
+			"timeseries": 1,
+			"owner": "Administrator",
+			"type": "Line"
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Quarterly",
+			"name": "Expenses",
+			"chart_name": "Expenses",
+			"timespan": "Last Year",
+			"color": None,
+			"filters_json": json.dumps({"company": company.name, "account": expense_account}),
+			"source": "Account Balance Timeline",
+			"chart_type": "Custom",
+			"timeseries": 1,
+			"owner": "Administrator",
+			"type": "Line"
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Quarterly",
+			"name": "Bank Balance",
+			"chart_name": "Bank Balance",
+			"timespan": "Last Year",
+			"color": "#ffb868",
+			"filters_json": json.dumps({"company": company.name, "account": bank_account}),
+			"source": "Account Balance Timeline",
+			"chart_type": "Custom",
+			"timeseries": 1,
+			"owner": "Administrator",
+			"type": "Line"
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Monthly",
+			"name": "Incoming Bills (Purchase Invoice)",
+			"chart_name": "Incoming Bills (Purchase Invoice)",
+			"timespan": "Last Year",
+			"color": "#a83333",
+			"value_based_on": "base_grand_total",
+			"filters_json": json.dumps({}),
+			"chart_type": "Sum",
+			"timeseries": 1,
+			"based_on": "posting_date",
+			"owner": "Administrator",
+			"document_type": "Purchase Invoice",
+			"type": "Bar"
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Monthly",
+			"name": "Outgoing Bills (Sales Invoice)",
+			"chart_name": "Outgoing Bills (Sales Invoice)",
+			"timespan": "Last Year",
+			"color": "#7b933d",
+			"value_based_on": "base_grand_total",
+			"filters_json": json.dumps({}),
+			"chart_type": "Sum",
+			"timeseries": 1,
+			"based_on": "posting_date",
+			"owner": "Administrator",
+			"document_type": "Sales Invoice",
+			"type": "Bar"
+		}
+	]
+
+def get_account(account_type, company):
+	accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company})
+	if accounts:
+		return accounts[0].name
+
+def get_company_for_dashboards():
+	company = get_default_company()
+	if not company:
+		company_list = frappe.get_list("Company")
+		if company_list:
+			company = company_list[0].name
+	return company
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index bcb22f0..83c670e 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -228,6 +228,8 @@
 			valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
 		elif self.party_type == "Employee":
 			valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance")
+		elif self.party_type == "Shareholder":
+			valid_reference_doctypes = ("Journal Entry")
 
 		for d in self.get("references"):
 			if not d.allocated_amount:
diff --git a/erpnext/accounts/doctype/payment_order/payment_order.py b/erpnext/accounts/doctype/payment_order/payment_order.py
index 3f3174a..7ecdc41 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/payment_order.py
@@ -80,7 +80,7 @@
 			paid_amt += d.amount
 
 	je.append('accounts', {
-		'account': doc.references[0].account,
+		'account': doc.account,
 		'credit_in_account_currency': paid_amt
 	})
 
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json
index 97ae5ff..7508683 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.json
+++ b/erpnext/accounts/doctype/payment_request/payment_request.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "naming_series:",
  "creation": "2015-12-15 22:23:24.745065",
  "doctype": "DocType",
@@ -210,13 +211,14 @@
    "label": "IBAN"
   },
   {
-   "fetch_from": "bank_account.branch_code",
+   "fetch_from": "bank.branch_code",
+   "fetch_if_empty": 1,
    "fieldname": "branch_code",
    "fieldtype": "Read Only",
    "label": "Branch Code"
   },
   {
-   "fetch_from": "bank_account.swift_number",
+   "fetch_from": "bank.swift_number",
    "fieldname": "swift_number",
    "fieldtype": "Read Only",
    "label": "SWIFT Number"
@@ -348,7 +350,8 @@
   }
  ],
  "is_submittable": 1,
- "modified": "2020-03-28 16:07:31.960798",
+ "links": [],
+ "modified": "2020-05-08 10:23:02.815237",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Request",
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index fb1a4f4..bfe35ab 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -297,7 +297,8 @@
 			fields = ["*"],
 			filters = {
 				"voucher_type": voucher_type,
-				"voucher_no": voucher_no
+				"voucher_no": voucher_no,
+				"is_cancelled": 0
 			})
 
 	if gl_entries:
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
index 39e218b..49c1d0f 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -2,16 +2,19 @@
 # License: GNU General Public License v3. See license.txt
 
 from __future__ import unicode_literals
+import datetime
+from six import iteritems
+
 import frappe
 from frappe import _
-from frappe.utils import flt
-from frappe.utils import formatdate
+from frappe.utils import flt, formatdate
+
 from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
 
-from six import iteritems
-from pprint import pprint
+
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns(filters)
 	if filters.get("budget_against_filter"):
@@ -43,20 +46,25 @@
 
 						period_data[0] += last_total
 
-						if(filters.get("show_cumulative")):
+						if filters.get("show_cumulative"):
 							last_total = period_data[0] - period_data[1]
 
 						period_data[2] = period_data[0] - period_data[1]
 						row += period_data
 				totals[2] = totals[0] - totals[1]
-				if filters["period"] != "Yearly" :
+				if filters["period"] != "Yearly":
 					row += totals
 				data.append(row)
 
 	return columns, data
 
+
 def get_columns(filters):
-	columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"]
+	columns = [
+		_(filters.get("budget_against"))
+		+ ":Link/%s:150" % (filters.get("budget_against")),
+		_("Account") + ":Link/Account:150"
+	]
 
 	group_months = False if filters["period"] == "Monthly" else True
 
@@ -65,84 +73,181 @@
 	for year in fiscal_year:
 		for from_date, to_date in get_period_date_ranges(filters["period"], year[0]):
 			if filters["period"] == "Yearly":
-				labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Variance ") + " " + str(year[0])]
+				labels = [
+					_("Budget") + " " + str(year[0]),
+					_("Actual ") + " " + str(year[0]),
+					_("Variance ") + " " + str(year[0])
+				]
 				for label in labels:
-					columns.append(label+":Float:150")
+					columns.append(label + ":Float:150")
 			else:
-				for label in [_("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), _("Variance") + " (%s)" + " " + str(year[0])]:
+				for label in [
+					_("Budget") + " (%s)" + " " + str(year[0]),
+					_("Actual") + " (%s)" + " " + str(year[0]),
+					_("Variance") + " (%s)" + " " + str(year[0])
+				]:
 					if group_months:
-						label = label % (formatdate(from_date, format_string="MMM") + "-" + formatdate(to_date, format_string="MMM"))
+						label = label % (
+							formatdate(from_date, format_string="MMM")
+							+ "-"
+							+ formatdate(to_date, format_string="MMM")
+						)
 					else:
 						label = label % formatdate(from_date, format_string="MMM")
 
-					columns.append(label+":Float:150")
+					columns.append(label + ":Float:150")
 
-	if filters["period"] != "Yearly" :
-		return columns + [_("Total Budget") + ":Float:150", _("Total Actual") + ":Float:150",
-			_("Total Variance") + ":Float:150"]
+	if filters["period"] != "Yearly":
+		return columns + [
+			_("Total Budget") + ":Float:150",
+			_("Total Actual") + ":Float:150",
+			_("Total Variance") + ":Float:150"
+		]
 	else:
 		return columns
 
+
 def get_cost_centers(filters):
-	cond = "and 1=1"
+	order_by = ""
 	if filters.get("budget_against") == "Cost Center":
-		cond = "order by lft"
+		order_by = "order by lft"
 
 	if filters.get("budget_against") in ["Cost Center", "Project"]:
-		return frappe.db.sql_list("""select name from `tab{tab}` where company=%s
-			{cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company"))
+		return frappe.db.sql_list(
+			"""
+				select
+					name
+				from
+					`tab{tab}`
+				where
+					company = %s
+				{order_by}
+			""".format(tab=filters.get("budget_against"), order_by=order_by),
+			filters.get("company"))
 	else:
-		return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec
+		return frappe.db.sql_list(
+			"""
+				select
+					name
+				from
+					`tab{tab}`
+			""".format(tab=filters.get("budget_against")))  # nosec
 
-#Get dimension & target details
+
+# Get dimension & target details
 def get_dimension_target_details(filters):
+	budget_against = frappe.scrub(filters.get("budget_against"))
 	cond = ""
 	if filters.get("budget_against_filter"):
-		cond += " and b.{budget_against} in (%s)".format(budget_against = \
-			frappe.scrub(filters.get('budget_against'))) % ', '.join(['%s']* len(filters.get('budget_against_filter')))
+		cond += """ and b.{budget_against} in (%s)""".format(
+			budget_against=budget_against) % ", ".join(["%s"] * len(filters.get("budget_against_filter")))
 
-	return frappe.db.sql("""
-			select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year
-			from `tabBudget` b, `tabBudget Account` ba
-			where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s
-			and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year
-		""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
-		tuple([filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company] + filters.get('budget_against_filter')), 
-		as_dict=True)
+	return frappe.db.sql(
+		"""
+			select
+				b.{budget_against} as budget_against,
+				b.monthly_distribution,
+				ba.account,
+				ba.budget_amount,
+				b.fiscal_year
+			from
+				`tabBudget` b,
+				`tabBudget Account` ba
+			where
+				b.name = ba.parent
+				and b.docstatus = 1
+				and b.fiscal_year between %s and %s
+				and b.budget_against = %s
+				and b.company = %s
+				{cond}
+			order by
+				b.fiscal_year
+		""".format(
+			budget_against=budget_against,
+			cond=cond,
+		),
+		tuple(
+			[
+				filters.from_fiscal_year,
+				filters.to_fiscal_year,
+				filters.budget_against,
+				filters.company,
+			]
+			+ filters.get("budget_against_filter")
+		), as_dict=True)
 
 
-#Get target distribution details of accounts of cost center
+# Get target distribution details of accounts of cost center
 def get_target_distribution_details(filters):
 	target_details = {}
-	for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation
-		from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
-		where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
-			target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
+	for d in frappe.db.sql(
+		"""
+			select
+				md.name,
+				mdp.month,
+				mdp.percentage_allocation
+			from
+				`tabMonthly Distribution Percentage` mdp,
+				`tabMonthly Distribution` md
+			where
+				mdp.parent = md.name
+				and md.fiscal_year between %s and %s
+			order by
+				md.fiscal_year
+		""",
+		(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
+		target_details.setdefault(d.name, {}).setdefault(
+			d.month, flt(d.percentage_allocation)
+		)
 
 	return target_details
 
-#Get actual details from gl entry
+# Get actual details from gl entry
 def get_actual_details(name, filters):
-	cond = "1=1"
-	budget_against=filters.get("budget_against").replace(" ", "_").lower()
+	budget_against = frappe.scrub(filters.get("budget_against"))
+	cond = ""
 
 	if filters.get("budget_against") == "Cost Center":
 		cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
-		cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt)
+		cond = """
+				and lft >= "{lft}"
+				and rgt <= "{rgt}"
+			""".format(lft=cc_lft, rgt=cc_rgt)
 
-	ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year,
-		MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against
-		from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b
-		where
-			b.name = ba.parent
-			and b.docstatus = 1
-			and ba.account=gl.account
-			and b.{budget_against} = gl.{budget_against}
-			and gl.fiscal_year between %s and %s
-			and b.{budget_against}=%s
-			and exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name order by gl.fiscal_year
-	""".format(tab = filters.budget_against, budget_against = budget_against, cond = cond,from_year=filters.from_fiscal_year,to_year=filters.to_fiscal_year),
-	(filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
+	ac_details = frappe.db.sql(
+		"""
+			select
+				gl.account,
+				gl.debit,
+				gl.credit,
+				gl.fiscal_year,
+				MONTHNAME(gl.posting_date) as month_name,
+				b.{budget_against} as budget_against
+			from
+				`tabGL Entry` gl,
+				`tabBudget Account` ba,
+				`tabBudget` b
+			where
+				b.name = ba.parent
+				and b.docstatus = 1
+				and ba.account=gl.account
+				and b.{budget_against} = gl.{budget_against}
+				and gl.fiscal_year between %s and %s
+				and b.{budget_against} = %s
+				and exists(
+					select
+						name
+					from
+						`tab{tab}`
+					where
+						name = gl.{budget_against}
+						{cond}
+				)
+				group by
+					gl.name
+				order by gl.fiscal_year
+		""".format(tab=filters.budget_against, budget_against=budget_against, cond=cond),
+		(filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
 
 	cc_actual_details = {}
 	for d in ac_details:
@@ -151,7 +256,6 @@
 	return cc_actual_details
 
 def get_dimension_account_month_map(filters):
-	import datetime
 	dimension_target_details = get_dimension_target_details(filters)
 	tdd = get_target_distribution_details(filters)
 
@@ -161,28 +265,43 @@
 		actual_details = get_actual_details(ccd.budget_against, filters)
 
 		for month_id in range(1, 13):
-			month = datetime.date(2013, month_id, 1).strftime('%B')
-			cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\
-				.setdefault(month, frappe._dict({
-					"target": 0.0, "actual": 0.0
-				}))
+			month = datetime.date(2013, month_id, 1).strftime("%B")
+			cam_map.setdefault(ccd.budget_against, {}).setdefault(
+				ccd.account, {}
+			).setdefault(ccd.fiscal_year, {}).setdefault(
+				month, frappe._dict({"target": 0.0, "actual": 0.0})
+			)
 
 			tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month]
-			month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \
-				if ccd.monthly_distribution else 100.0/12
+			month_percentage = (
+				tdd.get(ccd.monthly_distribution, {}).get(month, 0)
+				if ccd.monthly_distribution
+				else 100.0 / 12
+			)
 
 			tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100
 
 			for ad in actual_details.get(ccd.account, []):
-				if ad.month_name == month:
-						tav_dict.actual += flt(ad.debit) - flt(ad.credit)
+				if ad.month_name == month and ad.fiscal_year == ccd.fiscal_year:
+					tav_dict.actual += flt(ad.debit) - flt(ad.credit)
 
 	return cam_map
 
+
 def get_fiscal_years(filters):
 
-	fiscal_year = frappe.db.sql("""select name from `tabFiscal Year` where
-	name between %(from_fiscal_year)s and %(to_fiscal_year)s""",
-	{'from_fiscal_year': filters["from_fiscal_year"], 'to_fiscal_year': filters["to_fiscal_year"]})
+	fiscal_year = frappe.db.sql(
+		"""
+			select
+				name
+			from
+				`tabFiscal Year`
+			where
+				name between %(from_fiscal_year)s and %(to_fiscal_year)s
+		""",
+		{
+			"from_fiscal_year": filters["from_fiscal_year"],
+			"to_fiscal_year": filters["to_fiscal_year"]
+		})
 
 	return fiscal_year
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 1188bea..2aecd6b 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -53,7 +53,7 @@
 			"label": __("Voucher No"),
 			"fieldtype": "Data",
 			on_change: function() {
-				frappe.query_report.set_filter_value('group_by', "");
+				frappe.query_report.set_filter_value('group_by', "Group by Voucher (Consolidated)");
 			}
 		},
 		{
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 7af5fa8..6afe208 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -296,6 +296,9 @@
 		data[key].debit_in_account_currency += flt(gle.debit_in_account_currency)
 		data[key].credit_in_account_currency += flt(gle.credit_in_account_currency)
 
+		if data[key].against_voucher:
+			data[key].against_voucher += ', ' + gle.against_voucher
+
 	from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
 	for gle in gl_entries:
 		if (gle.posting_date < from_date or
diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
index 1c45810..714e48d 100644
--- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
+++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
@@ -9,8 +9,8 @@
 import copy
 
 def execute(filters=None):
-	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
-		filters.periodicity, filters.accumulated_values, filters.company)
+	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, filters.period_start_date,
+		filters.period_end_date, filters.filter_based_on, filters.periodicity, filters.accumulated_values, filters.company)
 
 	columns, data = [], []
 
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 06dfa19..a3200d5 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -32,7 +32,7 @@
 		self.validate_in_use_date()
 		self.set_status()
 		self.make_asset_movement()
-		if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.asset_category):
+		if not self.booked_fixed_asset and self.validate_make_gl_entry():
 			self.make_gl_entries()
 
 	def before_cancel(self):
@@ -455,18 +455,55 @@
 			for d in self.get('finance_books'):
 				if d.finance_book == self.default_finance_book:
 					return cint(d.idx) - 1
+	
+	def validate_make_gl_entry(self):
+		purchase_document = self.get_purchase_document()
+		asset_bought_with_invoice = purchase_document == self.purchase_invoice
+		fixed_asset_account, cwip_account = self.get_asset_accounts()
+		cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
+		# check if expense already has been booked in case of cwip was enabled after purchasing asset
+		expense_booked = False
+		cwip_booked = False
+
+		if asset_bought_with_invoice:
+			expense_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
+				(purchase_document, fixed_asset_account), as_dict=1)
+		else:
+			cwip_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
+				(purchase_document, cwip_account), as_dict=1)
+
+		if cwip_enabled and (expense_booked or not cwip_booked):
+			# if expense has already booked from invoice or cwip is booked from receipt
+			return False
+		elif not cwip_enabled and (not expense_booked or cwip_booked):
+			# if cwip is disabled but expense hasn't been booked yet
+			return True
+		elif cwip_enabled:
+			# default condition
+			return True
+
+	def get_purchase_document(self):
+		asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')
+		purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt
+
+		return purchase_document
+	
+	def get_asset_accounts(self):
+		fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
+					asset_category = self.asset_category, company = self.company)
+
+		cwip_account = get_asset_account("capital_work_in_progress_account",
+			self.name, self.asset_category, self.company)
+		
+		return fixed_asset_account, cwip_account
 
 	def make_gl_entries(self):
 		gl_entries = []
 
-		if ((self.purchase_receipt \
-			or (self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')))
-			and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
-			fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
-					asset_category = self.asset_category, company = self.company)
+		purchase_document = self.get_purchase_document()
+		fixed_asset_account, cwip_account = self.get_asset_accounts()
 
-			cwip_account = get_asset_account("capital_work_in_progress_account",
-				self.name, self.asset_category, self.company)
+		if (purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
 
 			gl_entries.append(self.get_gl_dict({
 				"account": cwip_account,
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index a0f8d15..aed78e7 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -560,6 +560,81 @@
 
 		self.assertEqual(gle, expected_gle)
 
+	def test_gle_with_cwip_toggling(self):
+		# TEST: purchase an asset with cwip enabled and then disable cwip and try submitting the asset
+		frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
+
+		pr = make_purchase_receipt(item_code="Macbook Pro",
+			qty=1, rate=5000, do_not_submit=True, location="Test Location")
+		pr.set('taxes', [{
+			'category': 'Total',
+			'add_deduct_tax': 'Add',
+			'charge_type': 'On Net Total',
+			'account_head': '_Test Account Service Tax - _TC',
+			'description': '_Test Account Service Tax',
+			'cost_center': 'Main - _TC',
+			'rate': 5.0
+		}, {
+			'category': 'Valuation and Total',
+			'add_deduct_tax': 'Add',
+			'charge_type': 'On Net Total',
+			'account_head': '_Test Account Shipping Charges - _TC',
+			'description': '_Test Account Shipping Charges',
+			'cost_center': 'Main - _TC',
+			'rate': 5.0
+		}])
+		pr.submit()
+		expected_gle = (
+			("Asset Received But Not Billed - _TC", 0.0, 5250.0),
+			("CWIP Account - _TC", 5250.0, 0.0)
+		)
+		pr_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+			where voucher_type='Purchase Receipt' and voucher_no = %s
+			order by account""", pr.name)
+		self.assertEqual(pr_gle, expected_gle)
+
+		pi = make_invoice(pr.name)
+		pi.submit()
+		expected_gle = (
+			("_Test Account Service Tax - _TC", 250.0, 0.0),
+			("_Test Account Shipping Charges - _TC", 250.0, 0.0),
+			("Asset Received But Not Billed - _TC", 5250.0, 0.0),
+			("Creditors - _TC", 0.0, 5500.0),
+			("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
+		)
+		pi_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+			where voucher_type='Purchase Invoice' and voucher_no = %s
+			order by account""", pi.name)
+		self.assertEqual(pi_gle, expected_gle)
+
+		asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
+		asset_doc = frappe.get_doc('Asset', asset)
+		month_end_date = get_last_day(nowdate())
+		asset_doc.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
+		self.assertEqual(asset_doc.gross_purchase_amount, 5250.0)
+		asset_doc.append("finance_books", {
+			"expected_value_after_useful_life": 200,
+			"depreciation_method": "Straight Line",
+			"total_number_of_depreciations": 3,
+			"frequency_of_depreciation": 10,
+			"depreciation_start_date": month_end_date
+		})
+
+		# disable cwip and try submitting
+		frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
+		asset_doc.submit()
+		# asset should have gl entries even if cwip is disabled
+		expected_gle = (
+			("_Test Fixed Asset - _TC", 5250.0, 0.0),
+			("CWIP Account - _TC", 0.0, 5250.0)
+		)
+		gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+			where voucher_type='Asset' and voucher_no = %s
+			order by account""", asset_doc.name)
+		self.assertEqual(gle, expected_gle)
+
+		frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
+
 	def test_expense_head(self):
 		pr = make_purchase_receipt(item_code="Macbook Pro",
 			qty=2, rate=200000.0, location="Test Location")
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py
index 3a08baa..3da355e 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.py
@@ -110,6 +110,7 @@
 				ORDER BY
 					asm.transaction_date asc
 				""", (d.asset, self.company, 'Receipt'), as_dict=1)
+
 			if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name:
 				frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \
 					auto generated for Asset {1}').format(self.name, d.asset))
diff --git a/erpnext/assets/doctype/location/location.json b/erpnext/assets/doctype/location/location.json
index 6a35130..f56fd05 100644
--- a/erpnext/assets/doctype/location/location.json
+++ b/erpnext/assets/doctype/location/location.json
@@ -141,7 +141,7 @@
  ],
  "is_tree": 1,
  "links": [],
- "modified": "2020-03-18 18:00:08.885805",
+ "modified": "2020-05-08 16:11:11.375701",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Location",
@@ -221,7 +221,6 @@
   }
  ],
  "quick_entry": 1,
- "restrict_to_domain": "Agriculture",
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 90ba8b3..1e0a48c 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -165,9 +165,9 @@
 				d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
 
 	def validate_selling_price(self):
-		def throw_message(item_name, rate, ref_rate_field):
-			frappe.throw(_("""Selling rate for item {0} is lower than its {1}. Selling rate should be atleast {2}""")
-				.format(item_name, ref_rate_field, rate))
+		def throw_message(idx, item_name, rate, ref_rate_field):
+			frappe.throw(_("""Row #{}: Selling rate for item {} is lower than its {}. Selling rate should be atleast {}""")
+				.format(idx, item_name, ref_rate_field, rate))
 
 		if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
 			return
@@ -181,8 +181,8 @@
 
 			last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
 			last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1)
-			if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom) and not self.get('is_internal_customer'):
-				throw_message(it.item_name, last_purchase_rate_in_sales_uom, "last purchase rate")
+			if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom):
+				throw_message(it.idx, frappe.bold(it.item_name), last_purchase_rate_in_sales_uom, "last purchase rate")
 
 			last_valuation_rate = frappe.db.sql("""
 				SELECT valuation_rate FROM `tabStock Ledger Entry` WHERE item_code = %s
@@ -193,7 +193,7 @@
 				last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] / (it.conversion_factor or 1)
 				if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom) \
 					and not self.get('is_internal_customer'):
-					throw_message(it.name, last_valuation_rate_in_sales_uom, "valuation rate")
+					throw_message(it.idx, frappe.bold(it.item_name), last_valuation_rate_in_sales_uom, "valuation rate")
 
 
 	def get_item_list(self):
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index 74b3582..ec7d14d 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -153,7 +153,7 @@
 		if not self.lead_name:
 			self.set_lead_name()
 
-		names = self.lead_name.split(" ")
+		names = self.lead_name.strip().split(" ")
 		if len(names) > 1:
 			first_name, last_name = names[0], " ".join(names[1:])
 		else:
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
index 50b98e9..263005e 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
@@ -62,6 +62,8 @@
 				callback : function(r) {
 					window.location.href = r.message;
 				}
+			}).fail(function() {
+				frappe.dom.unfreeze();
 			});
 		}
 	},
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
index 5df35df..377e061 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
@@ -15,7 +15,7 @@
 		params = urlencode({
 			"response_type":"code",
 			"client_id": self.consumer_key,
-			"redirect_uri": get_site_url(frappe.local.site) + "/?cmd=erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback",
+			"redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(frappe.utils.get_url()),
 			"scope": "r_emailaddress w_organization_social r_basicprofile r_liteprofile r_organization_social rw_organization_admin w_member_social"
 		})
 
@@ -30,7 +30,7 @@
 			"code": code,
 			"client_id": self.consumer_key,
 			"client_secret": self.get_password(fieldname="consumer_secret"),
-			"redirect_uri": get_site_url(frappe.local.site) + "/?cmd=erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback",
+			"redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(frappe.utils.get_url()),
 		}
 		headers = {
 			"Content-Type": "application/x-www-form-urlencoded"
@@ -154,7 +154,7 @@
 
 		return response
 
-@frappe.whitelist()
+@frappe.whitelist(allow_guest=True)
 def callback(code=None, error=None, error_description=None):
 	if not error:
 		linkedin_settings = frappe.get_doc("LinkedIn Settings")
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.js b/erpnext/crm/doctype/twitter_settings/twitter_settings.js
index b55946a..f6f431c 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.js
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.js
@@ -47,6 +47,8 @@
 				callback : function(r) {
 					window.location.href = r.message;
 				}
+			}).fail(function() {
+				frappe.dom.unfreeze();
 			});
 		}
 	},
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.json b/erpnext/crm/doctype/twitter_settings/twitter_settings.json
index f92e7f0..36776e5 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.json
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.json
@@ -11,8 +11,8 @@
   "consumer_key",
   "column_break_5",
   "consumer_secret",
-  "oauth_token",
-  "oauth_secret",
+  "access_token",
+  "access_token_secret",
   "session_status"
  ],
  "fields": [
@@ -42,20 +42,6 @@
    "reqd": 1
   },
   {
-   "fieldname": "oauth_token",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "OAuth Token",
-   "read_only": 1
-  },
-  {
-   "fieldname": "oauth_secret",
-   "fieldtype": "Password",
-   "hidden": 1,
-   "label": "OAuth Token Secret",
-   "read_only": 1
-  },
-  {
    "fieldname": "column_break_5",
    "fieldtype": "Column Break"
   },
@@ -72,12 +58,26 @@
    "label": "Session Status",
    "options": "Expired\nActive",
    "read_only": 1
+  },
+  {
+   "fieldname": "access_token",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Access Token",
+   "read_only": 1
+  },
+  {
+   "fieldname": "access_token_secret",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Access Token Secret",
+   "read_only": 1
   }
  ],
  "image_field": "profile_pic",
  "issingle": 1,
  "links": [],
- "modified": "2020-04-21 22:06:43.726798",
+ "modified": "2020-05-13 17:50:47.934776",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Twitter Settings",
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.py b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
index 64f53b5..976a23d 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.py
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
@@ -12,13 +12,12 @@
 
 class TwitterSettings(Document):
 	def get_authorize_url(self):
-		callback_url = "{0}/?cmd=erpnext.crm.doctype.twitter_settings.twitter_settings.callback".format(frappe.utils.get_url())
+		callback_url = "{0}/api/method/erpnext.crm.doctype.twitter_settings.twitter_settings.callback?".format(frappe.utils.get_url())
 		auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"), callback_url)
-
 		try:
 			redirect_url = auth.get_authorization_url()
 			return redirect_url
-		except:
+		except tweepy.TweepError as e:
 			frappe.msgprint(_("Error! Failed to get request token."))
 			frappe.throw(_('Invalid {0} or {1}').format(frappe.bold("Consumer Key"), frappe.bold("Consumer Secret Key")))
 
@@ -32,13 +31,13 @@
 
 		try:
 			auth.get_access_token(oauth_verifier)
-			api = self.get_api()
+			api = self.get_api(auth.access_token, auth.access_token_secret)
 			user = api.me()
 			profile_pic = (user._json["profile_image_url"]).replace("_normal","")
 
 			frappe.db.set_value(self.doctype, self.name, {
-				"oauth_token" : auth.access_token,
-				"oauth_secret" : auth.access_token_secret,
+				"access_token" : auth.access_token,
+				"access_token_secret" : auth.access_token_secret,
 				"account_name" : user._json["screen_name"],
 				"profile_pic" : profile_pic,
 				"session_status" : "Active"
@@ -50,11 +49,11 @@
 			frappe.msgprint(_("Error! Failed to get access token."))
 			frappe.throw(_('Invalid Consumer Key or Consumer Secret Key'))
 
-	def get_api(self):
+	def get_api(self, access_token, access_token_secret):
 		# authentication of consumer key and secret 
 		auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret")) 
 		# authentication of access token and secret 
-		auth.set_access_token(self.oauth_token, self.get_password(fieldname="oauth_secret")) 
+		auth.set_access_token(access_token, access_token_secret) 
 
 		return tweepy.API(auth)
 
@@ -68,13 +67,13 @@
 	
 	def upload_image(self, media):
 		media = get_file_path(media)
-		api = self.get_api()
+		api = self.get_api(self.access_token, self.access_token_secret)
 		media = api.media_upload(media)
 
 		return media.media_id
 
 	def send_tweet(self, text, media_id=None):
-		api = self.get_api()
+		api = self.get_api(self.access_token, self.access_token_secret)
 		try:
 			if media_id:
 				response = api.update_status(status = text, media_ids = [media_id])
@@ -91,8 +90,12 @@
 				frappe.db.commit()
 			frappe.throw(content["message"],title="Twitter Error {0} {1}".format(e.response.status_code, e.response.reason))
 
-@frappe.whitelist()
-def callback(oauth_token, oauth_verifier):
-	twitter_settings = frappe.get_single("Twitter Settings")
-	twitter_settings.get_access_token(oauth_token,oauth_verifier)
-	frappe.db.commit()
+@frappe.whitelist(allow_guest=True)
+def callback(oauth_token = None, oauth_verifier = None):
+	if oauth_token and oauth_verifier:
+		twitter_settings = frappe.get_single("Twitter Settings")
+		twitter_settings.get_access_token(oauth_token,oauth_verifier)
+		frappe.db.commit()
+	else:
+		frappe.local.response["type"] = "redirect"
+		frappe.local.response["location"] = get_url_to_form("Twitter Settings","Twitter Settings")
diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py
index 38bf79e..95b19ec 100644
--- a/erpnext/crm/utils.py
+++ b/erpnext/crm/utils.py
@@ -19,6 +19,5 @@
 					mobile_no = primary_mobile_nos[0]
 
 			lead = frappe.get_doc("Lead", contact_lead)
-			lead.phone = phone
-			lead.mobile_no = mobile_no
-			lead.save()
+			lead.db_set("phone", phone)
+			lead.db_set("mobile_no", mobile_no)
diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan.json b/erpnext/education/doctype/assessment_plan/assessment_plan.json
index bc39464..95ed853 100644
--- a/erpnext/education/doctype/assessment_plan/assessment_plan.json
+++ b/erpnext/education/doctype/assessment_plan/assessment_plan.json
@@ -1,790 +1,207 @@
 {
- "allow_copy": 0,
- "allow_guest_to_view": 0,
+ "actions": [],
  "allow_import": 1,
- "allow_rename": 0,
  "autoname": "EDU-ASP-.YYYY.-.#####",
- "beta": 0,
  "creation": "2015-11-12 16:34:34.658092",
- "custom": 0,
- "docstatus": 0,
  "doctype": "DocType",
  "document_type": "Setup",
- "editable_grid": 0,
  "engine": "InnoDB",
+ "field_order": [
+  "student_group",
+  "assessment_name",
+  "assessment_group",
+  "grading_scale",
+  "column_break_2",
+  "course",
+  "program",
+  "academic_year",
+  "academic_term",
+  "section_break_5",
+  "schedule_date",
+  "room",
+  "examiner",
+  "examiner_name",
+  "column_break_4",
+  "from_time",
+  "to_time",
+  "supervisor",
+  "supervisor_name",
+  "section_break_20",
+  "maximum_assessment_score",
+  "assessment_criteria",
+  "amended_from"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "student_group",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
    "in_global_search": 1,
    "in_list_view": 1,
    "in_standard_filter": 1,
    "label": "Student Group",
-   "length": 0,
-   "no_copy": 0,
    "options": "Student Group",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "assessment_name",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
    "in_global_search": 1,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Assessment Name",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Assessment Name"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "assessment_group",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
    "in_standard_filter": 1,
    "label": "Assessment Group",
-   "length": 0,
-   "no_copy": 0,
    "options": "Assessment Group",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fetch_from": "course.default_grading_scale",
+   "fetch_if_empty": 1,
    "fieldname": "grading_scale",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
    "in_standard_filter": 1,
    "label": "Grading Scale",
-   "length": 0,
-   "no_copy": 0,
    "options": "Grading Scale",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "column_break_2",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fetch_from": "student_group.course",
+   "fetch_if_empty": 1,
    "fieldname": "course",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
    "in_global_search": 1,
-   "in_list_view": 0,
    "in_standard_filter": 1,
    "label": "Course",
-   "length": 0,
-   "no_copy": 0,
    "options": "Course",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fetch_from": "student_group.program",
    "fieldname": "program",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
    "in_global_search": 1,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Program",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Program",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Program"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fetch_from": "student_group.academic_year",
    "fieldname": "academic_year",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Academic Year",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Academic Year",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Academic Year"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fetch_from": "student_group.academic_term",
    "fieldname": "academic_term",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Academic Term",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Academic Term",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Academic Term"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "collapsible_depends_on": "",
-   "columns": 0,
-   "depends_on": "",
    "fieldname": "section_break_5",
    "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Schedule",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Schedule"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "default": "Today",
    "fieldname": "schedule_date",
    "fieldtype": "Date",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
    "in_list_view": 1,
-   "in_standard_filter": 0,
    "label": "Schedule Date",
-   "length": 0,
    "no_copy": 1,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "room",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Room",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Room",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Room"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "examiner",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Examiner",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Instructor",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Instructor"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fetch_from": "examiner.instructor_name",
    "fieldname": "examiner_name",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Examiner Name",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "read_only": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "column_break_4",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "from_time",
    "fieldtype": "Time",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "From Time",
-   "length": 0,
    "no_copy": 1,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "to_time",
    "fieldtype": "Time",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "To Time",
-   "length": 0,
    "no_copy": 1,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "supervisor",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Supervisor",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Instructor",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Instructor"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fetch_from": "supervisor.instructor_name",
    "fieldname": "supervisor_name",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
    "in_global_search": 1,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Supervisor Name",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "read_only": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "section_break_20",
    "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Evaluate",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Evaluate"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "maximum_assessment_score",
    "fieldtype": "Float",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Maximum Assessment Score",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "assessment_criteria",
    "fieldtype": "Table",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Assessment Criteria",
-   "length": 0,
-   "no_copy": 0,
    "options": "Assessment Plan Criteria",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "amended_from",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Amended From",
-   "length": 0,
    "no_copy": 1,
    "options": "Assessment Plan",
-   "permlevel": 0,
    "print_hide": 1,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "read_only": 1
   }
  ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
  "is_submittable": 1,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "menu_index": 0,
- "modified": "2018-08-30 00:48:03.475522",
+ "links": [],
+ "modified": "2020-05-09 14:56:26.746988",
  "modified_by": "Administrator",
  "module": "Education",
  "name": "Assessment Plan",
- "name_case": "",
  "owner": "Administrator",
  "permissions": [
   {
@@ -794,28 +211,17 @@
    "delete": 1,
    "email": 1,
    "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
    "role": "Academics User",
-   "set_user_permissions": 0,
    "share": 1,
    "submit": 1,
    "write": 1
   }
  ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
  "restrict_to_domain": "Education",
- "show_name_in_global_search": 0,
  "sort_field": "modified",
  "sort_order": "DESC",
- "title_field": "assessment_name",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "title_field": "assessment_name"
 }
\ No newline at end of file
diff --git a/erpnext/education/doctype/education_settings/education_settings.json b/erpnext/education/doctype/education_settings/education_settings.json
index 967a030..0e548db 100644
--- a/erpnext/education/doctype/education_settings/education_settings.json
+++ b/erpnext/education/doctype/education_settings/education_settings.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2017-04-05 13:33:04.519313",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -42,12 +43,14 @@
    "fieldtype": "Column Break"
   },
   {
+   "default": "0",
    "description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.",
    "fieldname": "validate_batch",
    "fieldtype": "Check",
    "label": "Validate Batch for Students in Student Group"
   },
   {
+   "default": "0",
    "description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.",
    "fieldname": "validate_course",
    "fieldtype": "Check",
@@ -74,13 +77,13 @@
   {
    "fieldname": "web_academy_settings_section",
    "fieldtype": "Section Break",
-   "label": "LMS Settings"
+   "label": "Learning Management System Settings"
   },
   {
    "depends_on": "eval: doc.enable_lms",
    "fieldname": "portal_title",
    "fieldtype": "Data",
-   "label": "LMS Title"
+   "label": "Learning Management System Title"
   },
   {
    "depends_on": "eval: doc.enable_lms",
@@ -89,9 +92,10 @@
    "label": "Description"
   },
   {
+   "default": "0",
    "fieldname": "enable_lms",
    "fieldtype": "Check",
-   "label": "Enable LMS"
+   "label": "Enable Learning Management System"
   },
   {
    "default": "0",
@@ -102,7 +106,8 @@
   }
  ],
  "issingle": 1,
- "modified": "2019-05-13 18:36:13.127563",
+ "links": [],
+ "modified": "2020-05-07 19:18:10.639356",
  "modified_by": "Administrator",
  "module": "Education",
  "name": "Education Settings",
@@ -141,4 +146,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py
new file mode 100644
index 0000000..fc3d62f
--- /dev/null
+++ b/erpnext/healthcare/dashboard_fixtures.py
@@ -0,0 +1,41 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+import json
+
+
+def get_data():
+	return frappe._dict({
+		"dashboards": get_dashboards(),
+		"charts": get_charts(),
+	})
+
+def get_dashboards():
+	return [{
+		"name": "Healthcare",
+		"dashboard_name": "Healthcare",
+		"charts": [
+			{ "chart": "Patient Appointments" }
+		]
+	}]
+
+def get_charts():
+	return [
+			{
+				"doctype": "Dashboard Chart",
+				"time_interval": "Daily",
+				"name": "Patient Appointments",
+				"chart_name": "Patient Appointments",
+				"timespan": "Last Month",
+				"color": "#77ecca",
+				"filters_json": json.dumps({}),
+				"chart_type": "Count",
+				"timeseries": 1,
+				"based_on": "appointment_datetime",
+				"owner": "Administrator",
+				"document_type": "Patient Appointment",
+				"type": "Line",
+				"width": "Half"
+			}
+		]
diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json
index 24c6d6f..5cf09b3 100644
--- a/erpnext/healthcare/desk_page/healthcare/healthcare.json
+++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json
@@ -47,7 +47,12 @@
   }
  ],
  "category": "Domains",
- "charts": [],
+ "charts": [
+  {
+   "chart_name": "Patient Appointments",
+   "label": "Patient Appointments"
+  }
+ ],
  "charts_label": "",
  "creation": "2020-03-02 17:23:17.919682",
  "developer_mode_only": 0,
@@ -58,7 +63,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Healthcare",
- "modified": "2020-04-20 11:42:43.889576",
+ "modified": "2020-04-25 22:31:36.576444",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Healthcare",
diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.js b/erpnext/hr/doctype/additional_salary/additional_salary.js
index 18f6b8b..fb42b6f 100644
--- a/erpnext/hr/doctype/additional_salary/additional_salary.js
+++ b/erpnext/hr/doctype/additional_salary/additional_salary.js
@@ -13,5 +13,5 @@
 				}
 			};
 		});
-	}
+	},
 });
diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.json b/erpnext/hr/doctype/additional_salary/additional_salary.json
index 7d69f7e..bfb543f 100644
--- a/erpnext/hr/doctype/additional_salary/additional_salary.json
+++ b/erpnext/hr/doctype/additional_salary/additional_salary.json
@@ -13,10 +13,14 @@
   "salary_component",
   "overwrite_salary_structure_amount",
   "deduct_full_tax_on_selected_payroll_date",
+  "ref_doctype",
+  "ref_docname",
   "column_break_5",
   "company",
+  "is_recurring",
+  "from_date",
+  "to_date",
   "payroll_date",
-  "salary_slip",
   "type",
   "department",
   "amount",
@@ -74,12 +78,13 @@
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "eval:(doc.is_recurring==0)",
    "description": "Date on which this component is applied",
    "fieldname": "payroll_date",
    "fieldtype": "Date",
    "in_list_view": 1,
    "label": "Payroll Date",
-   "reqd": 1,
+   "mandatory_depends_on": "eval:(doc.is_recurring==0)",
    "search_index": 1
   },
   {
@@ -106,13 +111,6 @@
    "reqd": 1
   },
   {
-   "fieldname": "salary_slip",
-   "fieldtype": "Link",
-   "label": "Salary Slip",
-   "options": "Salary Slip",
-   "read_only": 1
-  },
-  {
    "fetch_from": "salary_component.type",
    "fieldname": "type",
    "fieldtype": "Data",
@@ -127,11 +125,45 @@
    "options": "Additional Salary",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "is_recurring",
+   "fieldtype": "Check",
+   "label": "Is Recurring"
+  },
+  {
+   "depends_on": "eval:(doc.is_recurring==1)",
+   "fieldname": "from_date",
+   "fieldtype": "Date",
+   "label": "From Date",
+   "mandatory_depends_on": "eval:(doc.is_recurring==1)"
+  },
+  {
+   "depends_on": "eval:(doc.is_recurring==1)",
+   "fieldname": "to_date",
+   "fieldtype": "Date",
+   "label": "To Date",
+   "mandatory_depends_on": "eval:(doc.is_recurring==1)"
+  },
+   {
+   "fieldname": "ref_doctype",
+   "fieldtype": "Link",
+   "label": "Reference Document Type",
+   "options": "DocType",
+   "read_only": 1
+  },
+  {
+   "fieldname": "ref_docname",
+   "fieldtype": "Dynamic Link",
+   "label": "Reference Document",
+   "options": "ref_doctype",
+   "read_only": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2019-12-12 19:07:23.635901",
+ "modified": "2020-04-04 18:06:29.170878",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Additional Salary",
diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py
index bc7dcee..bab6fb5 100644
--- a/erpnext/hr/doctype/additional_salary/additional_salary.py
+++ b/erpnext/hr/doctype/additional_salary/additional_salary.py
@@ -9,6 +9,11 @@
 from frappe.utils import getdate, date_diff
 
 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}):
@@ -21,10 +26,19 @@
 			frappe.throw(_("Amount should not be less than zero."))
 
 	def validate_dates(self):
- 		date_of_joining, relieving_date = frappe.db.get_value("Employee", self.employee,
+		date_of_joining, relieving_date = frappe.db.get_value("Employee", self.employee,
 			["date_of_joining", "relieving_date"])
- 		if date_of_joining and getdate(self.payroll_date) < getdate(date_of_joining):
- 			frappe.throw(_("Payroll date can not be less than employee's joining date"))
+
+		if getdate(self.from_date) > getdate(self.to_date):
+			frappe.throw(_("From Date can not be greater than To Date."))
+
+		if date_of_joining:
+			if getdate(self.payroll_date) < getdate(date_of_joining):
+				frappe.throw(_("Payroll date can not be less than employee's joining date."))
+			elif getdate(self.from_date) < getdate(date_of_joining):
+				frappe.throw(_("From date can not be less than employee's joining date."))
+			elif getdate(self.to_date) > getdate(relieving_date):
+				frappe.throw(_("To date can not be greater than employee's relieving date."))
 
 	def get_amount(self, sal_start_date, sal_end_date):
 		start_date = getdate(sal_start_date)
@@ -40,15 +54,18 @@
 
 @frappe.whitelist()
 def get_additional_salary_component(employee, start_date, end_date, component_type):
-	additional_components = frappe.db.sql("""
-		select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
+	additional_salaries = frappe.db.sql("""
+		select name, salary_component, type, amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
 		from `tabAdditional Salary`
 		where employee=%(employee)s
 			and docstatus = 1
-			and payroll_date between %(from_date)s and %(to_date)s
-			and type = %(component_type)s
-		group by salary_component, overwrite_salary_structure_amount
-		order by salary_component, overwrite_salary_structure_amount
+			and (
+					payroll_date between %(from_date)s and %(to_date)s
+				or
+					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
 	""", {
 		'employee': employee,
 		'from_date': start_date,
@@ -56,21 +73,38 @@
 		'component_type': "Earning" if component_type == "earnings" else "Deduction"
 	}, as_dict=1)
 
-	additional_components_list = []
+	existing_salary_components= []
+	salary_components_details = {}
+	additional_salary_details = []
+
+	overwrites_components = [ele.salary_component for ele in additional_salaries if ele.overwrite_salary_structure_amount == 1]
+
 	component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
-	for d in additional_components:
-		struct_row = frappe._dict({'salary_component': d.salary_component})
-		component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
-		if component:
-			struct_row.update(component[0])
+	for d in additional_salaries:
 
-		struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
-		struct_row['is_additional_component'] = 1
+		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_components_list.append(frappe._dict({
-			'amount': d.amount,
-			'type': component[0].type,
-			'struct_row': struct_row,
-			'overwrite': d.overwrite_salary_structure_amount,
-		}))
-	return additional_components_list
\ No newline at end of file
+			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
\ No newline at end of file
diff --git a/erpnext/hr/doctype/additional_salary/test_additional_salary.py b/erpnext/hr/doctype/additional_salary/test_additional_salary.py
index 949ba20..6f93fb5 100644
--- a/erpnext/hr/doctype/additional_salary/test_additional_salary.py
+++ b/erpnext/hr/doctype/additional_salary/test_additional_salary.py
@@ -3,6 +3,44 @@
 # See license.txt
 from __future__ import unicode_literals
 import unittest
+import frappe, erpnext
+from frappe.utils import nowdate, add_days
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.salary_component.test_salary_component import create_salary_component
+from erpnext.hr.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test
+
 
 class TestAdditionalSalary(unittest.TestCase):
-	pass
+
+	def setUp(self):
+		setup_test()
+
+	def test_recurring_additional_salary(self):
+		emp_id = make_employee("test_additional@salary.com")
+		frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(nowdate(), 1800))
+		add_sal = get_additional_salary(emp_id)
+
+		ss = make_employee_salary_slip("test_additional@salary.com", "Monthly")
+		for earning in ss.earnings:
+			if earning.salary_component == "Recurring Salary Component":
+				amount = earning.amount
+				salary_component = earning.salary_component
+
+		self.assertEqual(amount, add_sal.amount)
+		self.assertEqual(salary_component, add_sal.salary_component)
+
+
+
+def get_additional_salary(emp_id):
+	create_salary_component("Recurring Salary Component")
+	add_sal = frappe.new_doc("Additional Salary")
+	add_sal.employee = emp_id
+	add_sal.salary_component = "Recurring Salary Component"
+	add_sal.is_recurring = 1
+	add_sal.from_date = add_days(nowdate(), -50)
+	add_sal.to_date = add_days(nowdate(), 180)
+	add_sal.amount = 5000
+	add_sal.save()
+	add_sal.submit()
+
+	return add_sal
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js
index 3896603..6cc49cf 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.js
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.js
@@ -23,6 +23,14 @@
 				}
 			};
 		});
+
+		frm.set_query('salary_component', function(doc) {
+			return {
+				filters: {
+					"type": "Deduction"
+				}
+			};
+		});
 	},
 
 	refresh: function(frm) {
@@ -47,19 +55,37 @@
 		}
 
 		if (frm.doc.docstatus === 1
-			&& (flt(frm.doc.claimed_amount) + flt(frm.doc.return_amount) < flt(frm.doc.paid_amount))
-			&& frappe.model.can_create("Journal Entry")) {
+			&& (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) && flt(frm.doc.paid_amount) != flt(frm.doc.return_amount))) {
 
-			frm.add_custom_button(__("Return"),  function() {
-				frm.trigger('make_return_entry');
-			}, __('Create'));
+			if (frm.doc.repay_unclaimed_amount_from_salary == 0 && frappe.model.can_create("Journal Entry")){
+				frm.add_custom_button(__("Return"),  function() {
+					frm.trigger('make_return_entry');
+				}, __('Create'));
+			}else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")){
+				frm.add_custom_button(__("Deduction from salary"),  function() {
+					frm.events.make_deduction_via_additional_salary(frm)
+				}, __('Create'));
+			}
 		}
 	},
 
+	make_deduction_via_additional_salary: function(frm){
+		frappe.call({
+			method: "erpnext.hr.doctype.employee_advance.employee_advance.create_return_through_additional_salary",
+			args: {
+				doc: frm.doc
+			},
+			callback: function (r){
+				var doclist = frappe.model.sync(r.message);
+				frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+			}
+		});
+	},
+
 	make_payment_entry: function(frm) {
 		var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
 		if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) {
-			method = "erpnext.hr.doctype.employee_advance.employee_advance.make_bank_entry"
+			method = "erpnext.hr.doctype.employee_advance.employee_advance.make_bank_entry";
 		}
 		return frappe.call({
 			method: method,
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json
index d233a2b..8c5ce42 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.json
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.json
@@ -10,9 +10,10 @@
   "naming_series",
   "employee",
   "employee_name",
+  "department",
   "column_break_4",
   "posting_date",
-  "department",
+  "repay_unclaimed_amount_from_salary",
   "section_break_8",
   "purpose",
   "column_break_11",
@@ -164,16 +165,23 @@
    "options": "Mode of Payment"
   },
   {
+   "allow_on_submit": 1,
    "fieldname": "return_amount",
    "fieldtype": "Currency",
    "label": "Returned Amount",
    "options": "Company:company:default_currency",
    "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "repay_unclaimed_amount_from_salary",
+   "fieldtype": "Check",
+   "label": "Repay unclaimed amount from salary"
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2019-12-15 19:04:07.044505",
+ "modified": "2020-03-06 15:11:33.747535",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee Advance",
@@ -210,4 +218,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py
index f0663ae..23e4992 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.py
@@ -133,8 +133,20 @@
 	return je.as_dict()
 
 @frappe.whitelist()
-def make_return_entry(employee, company, employee_advance_name,
-		return_amount, advance_account, mode_of_payment=None):
+def create_return_through_additional_salary(doc):
+	import json
+	doc = frappe._dict(json.loads(doc))
+	additional_salary = frappe.new_doc('Additional Salary')
+	additional_salary.employee = doc.employee
+	additional_salary.amount = doc.paid_amount - doc.claimed_amount
+	additional_salary.company = doc.company
+	additional_salary.ref_doctype = doc.doctype
+	additional_salary.ref_docname = doc.name
+
+	return additional_salary
+
+@frappe.whitelist()
+def make_return_entry(employee_name, company, employee_advance_name, return_amount, mode_of_payment, advance_account):
 	return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
 
 	mode_of_payment_type = ''
diff --git a/erpnext/hr/doctype/employee_incentive/employee_incentive.json b/erpnext/hr/doctype/employee_incentive/employee_incentive.json
index ce8e1ea..e2d8a11 100644
--- a/erpnext/hr/doctype/employee_incentive/employee_incentive.json
+++ b/erpnext/hr/doctype/employee_incentive/employee_incentive.json
@@ -9,10 +9,9 @@
   "employee",
   "incentive_amount",
   "employee_name",
-  "additional_salary",
+  "salary_component",
   "column_break_5",
   "payroll_date",
-  "salary_component",
   "department",
   "amended_from"
  ],
@@ -66,14 +65,6 @@
    "read_only": 1
   },
   {
-   "fieldname": "additional_salary",
-   "fieldtype": "Link",
-   "label": "Additional Salary",
-   "no_copy": 1,
-   "options": "Additional Salary",
-   "read_only": 1
-  },
-  {
    "fieldname": "salary_component",
    "fieldtype": "Link",
    "label": "Salary Component",
@@ -83,7 +74,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2019-12-12 13:24:44.761540",
+ "modified": "2020-03-05 18:59:40.526014",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee Incentive",
diff --git a/erpnext/hr/doctype/employee_incentive/employee_incentive.py b/erpnext/hr/doctype/employee_incentive/employee_incentive.py
index 2e138f8..44763fc 100644
--- a/erpnext/hr/doctype/employee_incentive/employee_incentive.py
+++ b/erpnext/hr/doctype/employee_incentive/employee_incentive.py
@@ -9,37 +9,13 @@
 class EmployeeIncentive(Document):
 	def on_submit(self):
 		company = frappe.db.get_value('Employee', self.employee, 'company')
-		additional_salary = frappe.db.exists('Additional Salary', {
-				'employee': self.employee, 
-				'salary_component': self.salary_component,
-				'payroll_date': self.payroll_date, 
-				'company': company,
-				'docstatus': 1
-			})
 
-		if not additional_salary:
-			additional_salary = frappe.new_doc('Additional Salary')
-			additional_salary.employee = self.employee
-			additional_salary.salary_component = self.salary_component
-			additional_salary.amount = self.incentive_amount
-			additional_salary.payroll_date = self.payroll_date
-			additional_salary.company = company
-			additional_salary.submit()
-			self.db_set('additional_salary', additional_salary.name)
-
-		else:
-			incentive_added = frappe.db.get_value('Additional Salary', additional_salary, 'amount') + self.incentive_amount
-			frappe.db.set_value('Additional Salary', additional_salary, 'amount', incentive_added)
-			self.db_set('additional_salary', additional_salary)
-
-	def on_cancel(self):
-		if self.additional_salary:
-			incentive_removed = frappe.db.get_value('Additional Salary', self.additional_salary, 'amount') - self.incentive_amount
-			if incentive_removed == 0:
-				frappe.get_doc('Additional Salary', self.additional_salary).cancel()
-			else:
-				frappe.db.set_value('Additional Salary', self.additional_salary, 'amount', incentive_removed)
-
-			self.db_set('additional_salary', '')
-
-		
+		additional_salary = frappe.new_doc('Additional Salary')
+		additional_salary.employee = self.employee
+		additional_salary.salary_component = self.salary_component
+		additional_salary.amount = self.incentive_amount
+		additional_salary.payroll_date = self.payroll_date
+		additional_salary.company = company
+		additional_salary.ref_doctype = self.doctype
+		additional_salary.ref_docname = self.name
+		additional_salary.submit()
diff --git a/erpnext/hr/doctype/holiday/holiday.json b/erpnext/hr/doctype/holiday/holiday.json
index 6498530..6bd0ab0 100644
--- a/erpnext/hr/doctype/holiday/holiday.json
+++ b/erpnext/hr/doctype/holiday/holiday.json
@@ -1,87 +1,60 @@
 {
- "allow_copy": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2013-02-22 01:27:46", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Setup", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2013-02-22 01:27:46",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "holiday_date",
+  "column_break_2",
+  "weekly_off",
+  "section_break_4",
+  "description"
+ ],
  "fields": [
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "holiday_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "holiday_date", 
-   "oldfieldtype": "Date", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "holiday_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Date",
+   "oldfieldname": "holiday_date",
+   "oldfieldtype": "Date",
+   "reqd": 1
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "description", 
-   "fieldtype": "Text Editor", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Description", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "300px", 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "fieldname": "description",
+   "fieldtype": "Text Editor",
+   "in_list_view": 1,
+   "label": "Description",
+   "print_width": "300px",
+   "reqd": 1,
    "width": "300px"
+  },
+  {
+   "default": "0",
+   "fieldname": "weekly_off",
+   "fieldtype": "Check",
+   "label": "Weekly Off"
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break"
   }
- ], 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
-
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2016-07-11 03:28:00.660849", 
- "modified_by": "Administrator", 
- "module": "HR", 
- "name": "Holiday", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "sort_order": "ASC", 
- "track_seen": 0
+ ],
+ "idx": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-04-18 19:03:23.507845",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Holiday",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "ASC"
 }
\ No newline at end of file
diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py
index 8c7b6f7..76dc942 100644
--- a/erpnext/hr/doctype/holiday_list/holiday_list.py
+++ b/erpnext/hr/doctype/holiday_list/holiday_list.py
@@ -23,6 +23,7 @@
 			ch = self.append('holidays', {})
 			ch.description = self.weekly_off
 			ch.holiday_date = d
+			ch.weekly_off = 1
 			ch.idx = last_idx + i + 1
 
 	def validate_values(self):
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 9161ed8..ebf8723 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -13,12 +13,12 @@
   "stop_birthday_reminders",
   "expense_approver_mandatory_in_expense_claim",
   "payroll_settings",
-  "payroll_based_on", 
-  "max_working_hours_against_timesheet", 
+  "payroll_based_on",
+  "max_working_hours_against_timesheet",
   "include_holidays_in_total_working_days",
   "disable_rounded_total",
   "column_break_11",
-  "daily_wages_fraction_for_half_day", 
+  "daily_wages_fraction_for_half_day",
   "email_salary_slip_to_employee",
   "encrypt_salary_slips_in_emails",
   "password_policy",
@@ -191,7 +191,7 @@
    "default": "Leave",
    "fieldname": "payroll_based_on",
    "fieldtype": "Select",
-   "label": "Calculate Working Days in Payroll based on",
+   "label": "Calculate Payroll Working Days Based On",
    "options": "Leave\nAttendance"
   },
   {
@@ -206,7 +206,7 @@
  "idx": 1,
  "issingle": 1,
  "links": [],
- "modified": "2020-04-13 21:20:59.382394",
+ "modified": "2020-05-11 13:02:51.274347",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "HR Settings",
diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
index 7d6fd42..50a08b1 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
@@ -30,13 +30,16 @@
 		additional_salary = frappe.new_doc("Additional Salary")
 		additional_salary.company = frappe.get_value("Employee", self.employee, "company")
 		additional_salary.employee = self.employee
-		additional_salary.salary_component = frappe.get_value("Leave Type", self.leave_type, "earning_component")
+		earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component")
+		if not earning_component:
+			frappe.throw(_("Please set Earning Component for Leave type: {0}.".format(self.leave_type)))
+		additional_salary.salary_component = earning_component
 		additional_salary.payroll_date = self.encashment_date
 		additional_salary.amount = self.encashment_amount
+		additional_salary.ref_doctype = self.doctype
+		additional_salary.ref_docname = self.name
 		additional_salary.submit()
 
-		self.db_set("additional_salary", additional_salary.name)
-
 		# Set encashed leaves in Allocation
 		frappe.db.set_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed",
 				frappe.db.get_value('Leave Allocation', self.leave_allocation, 'total_leaves_encashed') + self.encashable_days)
@@ -118,4 +121,4 @@
 			leave_type=allocation.leave_type,
 			encashment_date=allocation.to_date
 		))
-		leave_encashment.insert(ignore_permissions=True)
+		leave_encashment.insert(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
index e5bd170..ac7755b 100644
--- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
@@ -53,7 +53,10 @@
 		self.assertEqual(leave_encashment.encashment_amount, 250)
 
 		leave_encashment.submit()
-		self.assertTrue(frappe.db.get_value("Leave Encashment", leave_encashment.name, "additional_salary"))
+
+		# assert links
+		add_sal = frappe.get_all("Additional Salary", filters = {"ref_docname": leave_encashment.name})[0]
+		self.assertTrue(add_sal)
 
 	def test_creation_of_leave_ledger_entry_on_submit(self):
 		frappe.db.sql('''delete from `tabLeave Encashment`''')
@@ -75,5 +78,8 @@
 		self.assertEquals(leave_ledger_entry[0].leaves, leave_encashment.encashable_days *  -1)
 
 		# check if leave ledger entry is deleted on cancellation
+
+		frappe.db.sql("Delete from `tabAdditional Salary` WHERE ref_docname = %s", (leave_encashment.name) )
+
 		leave_encashment.cancel()
 		self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_encashment.name}))
diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py
index 49671d5..e43f744 100644
--- a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py
@@ -17,7 +17,7 @@
 
 class TestPayrollEntry(unittest.TestCase):
 	def setUp(self):
-		for dt in ["Salary Slip", "Salary Component", "Salary Component Account", "Payroll Entry"]:
+		for dt in ["Salary Slip", "Salary Component", "Salary Component Account", "Payroll Entry", "Salary Structure"]:
 			frappe.db.sql("delete from `tab%s`" % dt)
 
 		make_earning_salary_component(setup=True, company_list=["_Test Company"])
diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.json b/erpnext/hr/doctype/salary_detail/salary_detail.json
index 545f56a..fe5f83b 100644
--- a/erpnext/hr/doctype/salary_detail/salary_detail.json
+++ b/erpnext/hr/doctype/salary_detail/salary_detail.json
@@ -26,6 +26,7 @@
   "tax_on_flexible_benefit",
   "tax_on_additional_salary",
   "section_break_11",
+  "additional_salary",
   "condition_and_formula_help"
  ],
  "fields": [
@@ -193,6 +194,12 @@
    "options": "<h3>Condition and Formula Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Use field name from Salary Slip in conditions and formulas. <code>Payment Days = payment_days</code><code>Leave without pay = leave_without_pay</code></li>\n<li>Direct Amount can also be entered based on Condtion. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base &lt; 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS &gt; 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
   },
   {
+   "fieldname": "additional_salary",
+   "fieldtype": "Link",
+   "label": "Additional Salary ",
+   "options": "Additional Salary"
+  },
+  {
    "default": "0",
    "depends_on": "eval:doc.parentfield=='deductions'",
    "fetch_from": "salary_component.exempted_from_income_tax",
@@ -204,7 +211,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-04-24 20:00:16.475295",
+ "modified": "2020-04-04 20:00:16.475295",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Salary Detail",
@@ -213,4 +220,4 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC"
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index db93f31..4d5c843 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -66,7 +66,6 @@
 		else:
 			self.set_status()
 			self.update_status(self.name)
-			self.update_salary_slip_in_additional_salary()
 			self.make_loan_repayment_entry()
 			if (frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry:
 				self.email_salary_slip()
@@ -74,7 +73,6 @@
 	def on_cancel(self):
 		self.set_status()
 		self.update_status()
-		self.update_salary_slip_in_additional_salary()
 		self.cancel_loan_repayment_entry()
 
 	def on_trash(self):
@@ -464,14 +462,15 @@
 						self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
 
 	def add_additional_salary_components(self, component_type):
-		additional_components = get_additional_salary_component(self.employee,
+		salary_components_details, additional_salary_details = get_additional_salary_component(self.employee,
 			self.start_date, self.end_date, component_type)
-		if additional_components:
-			for additional_component in additional_components:
-				amount = additional_component.amount
-				overwrite = additional_component.overwrite
-				self.update_component_row(frappe._dict(additional_component.struct_row), amount,
-					component_type, overwrite=overwrite)
+		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)
 
 	def add_tax_components(self, payroll_period):
 		# Calculate variable_based_on_taxable_salary after all components updated in salary slip
@@ -491,13 +490,12 @@
 			tax_row = self.get_salary_slip_row(d)
 			self.update_component_row(tax_row, tax_amount, "deductions")
 
-	def update_component_row(self, struct_row, amount, key, overwrite=1):
+	def update_component_row(self, struct_row, amount, key, overwrite=1, additional_salary = ''):
 		component_row = None
 		for d in self.get(key):
 			if d.salary_component == struct_row.salary_component:
 				component_row = d
-
-		if not component_row:
+		if not component_row or (struct_row.get("is_additional_component") and not overwrite):
 			if amount:
 				self.append(key, {
 					'amount': amount,
@@ -505,6 +503,7 @@
 					'depends_on_payment_days' : struct_row.depends_on_payment_days,
 					'salary_component' : struct_row.salary_component,
 					'abbr' : struct_row.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,
@@ -517,6 +516,7 @@
 			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
 
@@ -936,14 +936,6 @@
 				"repay_from_salary": 1,
 			})
 
-
-	def update_salary_slip_in_additional_salary(self):
-		salary_slip = self.name if self.docstatus==1 else None
-		frappe.db.sql("""
-			update `tabAdditional Salary` set salary_slip=%s
-			where employee=%s and payroll_date between %s and %s and docstatus=1
-		""", (salary_slip, self.employee, self.start_date, self.end_date))
-
 	def make_loan_repayment_entry(self):
 		for loan in self.loans:
 			repayment_entry = create_repayment_entry(loan.loan, self.employee,
diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py
index fc687a3..a7dcb94 100644
--- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py
@@ -18,19 +18,7 @@
 
 class TestSalarySlip(unittest.TestCase):
 	def setUp(self):
-		make_earning_salary_component(setup=True, company_list=["_Test Company"])
-		make_deduction_salary_component(setup=True, company_list=["_Test Company"])
-
-		for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance"]:
-			frappe.db.sql("delete from `tab%s`" % dt)
-
-		self.make_holiday_list()
-
-		frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
-		frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0)
-		frappe.db.set_value('HR Settings', None, 'leave_status_notification_template', None)
-		frappe.db.set_value('HR Settings', None, 'leave_approval_notification_template', None)
-		
+		setup_test()
 	def tearDown(self):
 		frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
 		frappe.set_user("Administrator")
@@ -374,19 +362,6 @@
 		# undelete fixture data
 		frappe.db.rollback()
 
-	def make_holiday_list(self):
-		fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())
-		if not frappe.db.get_value("Holiday List", "Salary Slip Test Holiday List"):
-			holiday_list = frappe.get_doc({
-				"doctype": "Holiday List",
-				"holiday_list_name": "Salary Slip Test Holiday List",
-				"from_date": fiscal_year[1],
-				"to_date": fiscal_year[2],
-				"weekly_off": "Sunday"
-			}).insert()
-			holiday_list.get_weekly_off_dates()
-			holiday_list.save()
-
 	def make_activity_for_employee(self):
 		activity_type = frappe.get_doc("Activity Type", "_Test Activity Type")
 		activity_type.billing_rate = 50
@@ -702,4 +677,31 @@
 		status = "Approved",
 		leave_approver = 'test@example.com'
 	))
-	leave_application.submit()
\ No newline at end of file
+	leave_application.submit()
+
+def setup_test():
+	make_earning_salary_component(setup=True, company_list=["_Test Company"])
+	make_deduction_salary_component(setup=True, company_list=["_Test Company"])
+
+	for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance"]:
+		frappe.db.sql("delete from `tab%s`" % dt)
+
+	make_holiday_list()
+
+	frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
+	frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0)
+	frappe.db.set_value('HR Settings', None, 'leave_status_notification_template', None)
+	frappe.db.set_value('HR Settings', None, 'leave_approval_notification_template', None)
+
+def make_holiday_list():
+	fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())
+	if not frappe.db.get_value("Holiday List", "Salary Slip Test Holiday List"):
+		holiday_list = frappe.get_doc({
+			"doctype": "Holiday List",
+			"holiday_list_name": "Salary Slip Test Holiday List",
+			"from_date": fiscal_year[1],
+			"to_date": fiscal_year[2],
+			"weekly_off": "Sunday"
+		}).insert()
+		holiday_list.get_weekly_off_dates()
+		holiday_list.save()
diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
index f75bb41..61faea1 100644
--- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py
+++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
@@ -145,7 +145,7 @@
 
 	def remove_holidays(rows):
 		rows = [ row for row in rows if row[4] != "Holiday"]
-		return
+		return rows
 
 	from frappe.modules import scrub
 
diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js
index 348c5e7..bd4ed3c 100644
--- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js
+++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js
@@ -31,6 +31,18 @@
 			"options": "Company",
 			"default": frappe.defaults.get_user_default("Company"),
 			"reqd": 1
+		},
+		{
+			"fieldname":"group_by",
+			"label": __("Group By"),
+			"fieldtype": "Select",
+			"options": ["","Branch","Grade","Department","Designation"]
+		},
+		{
+			"fieldname":"summarized_view",
+			"label": __("Summarized View"),
+			"fieldtype": "Check",
+			"Default": 0,
 		}
 	],
 
diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
index 9a9e42e..82ed277 100644
--- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
+++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
@@ -7,65 +7,127 @@
 from frappe import msgprint, _
 from calendar import monthrange
 
+status_map = {
+	"Absent": "A",
+	"Half Day": "HD",
+	"Holiday": "<b>H</b>",
+	"Weekly Off": "<b>WO</b>",
+	"On Leave": "L",
+	"Present": "P",
+	"Work From Home": "WFH"
+	}
+
+day_abbr = [
+	"Mon",
+	"Tue",
+	"Wed",
+	"Thu",
+	"Fri",
+	"Sat",
+	"Sun"
+]
+
 def execute(filters=None):
 	if not filters: filters = {}
 
 	conditions, filters = get_conditions(filters)
 	columns = get_columns(filters)
 	att_map = get_attendance_list(conditions, filters)
-	emp_map = get_employee_details(filters)
 
-	holiday_list = [emp_map[d]["holiday_list"] for d in emp_map if emp_map[d]["holiday_list"]]
+	if filters.group_by:
+		emp_map, group_by_parameters = get_employee_details(filters.group_by, filters.company)
+		holiday_list = []
+		for parameter in group_by_parameters:
+			h_list = [emp_map[parameter][d]["holiday_list"] for d in emp_map[parameter] if emp_map[parameter][d]["holiday_list"]]
+			holiday_list += h_list
+	else:
+		emp_map = get_employee_details(filters.group_by, filters.company)
+		holiday_list = [emp_map[d]["holiday_list"] for d in emp_map if emp_map[d]["holiday_list"]]
+
+
 	default_holiday_list = frappe.get_cached_value('Company',  filters.get("company"),  "default_holiday_list")
 	holiday_list.append(default_holiday_list)
 	holiday_list = list(set(holiday_list))
 	holiday_map = get_holiday(holiday_list, filters["month"])
 
 	data = []
-	leave_types = frappe.db.sql("""select name from `tabLeave Type`""", as_list=True)
-	leave_list = [d[0] for d in leave_types]
-	columns.extend(leave_list)
-	columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"])
 
-	for emp in sorted(att_map):
-		emp_det = emp_map.get(emp)
-		if not emp_det:
+	leave_list = None
+	if filters.summarized_view:
+		leave_types = frappe.db.sql("""select name from `tabLeave Type`""", as_list=True)
+		leave_list = [d[0] + ":Float:120" for d in leave_types]
+		columns.extend(leave_list)
+		columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"])
+
+	if filters.group_by:
+		for parameter in group_by_parameters:
+			data.append([ "<b>"+ parameter + "</b>"])
+			record = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list)
+			data += record
+	else:
+		record = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list)
+		data += record
+
+	return columns, data
+
+
+def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list=None):
+
+	record = []
+	for emp in employee_map:
+		emp_det = employee_map.get(emp)
+		if not emp_det or emp not in att_map:
 			continue
 
-		row = [emp, emp_det.employee_name, emp_det.branch, emp_det.department, emp_det.designation,
-			emp_det.company]
+		row = []
+		if filters.group_by:
+			row += [" "]
+		row += [emp, emp_det.employee_name]
 
-		total_p = total_a = total_l = 0.0
+		total_p = total_a = total_l = total_h = total_um= 0.0
 		for day in range(filters["total_days_in_month"]):
+			status = None
 			status = att_map.get(emp).get(day + 1)
-			status_map = {
-				"Absent": "A",
-				"Half Day": "HD",
-				"Holiday":"<b>H</b>",
-				"On Leave": "L",
-				"Present": "P",
-				"Work From Home": "WFH"
-			}
 
 			if status is None and holiday_map:
 				emp_holiday_list = emp_det.holiday_list if emp_det.holiday_list else default_holiday_list
-				if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list]:
-					status = "Holiday"
 
-			row.append(status_map.get(status, ""))
+				if emp_holiday_list in holiday_map:
+					for idx, ele in enumerate(holiday_map[emp_holiday_list]):
+						if day+1 == holiday_map[emp_holiday_list][idx][0]:
+							if holiday_map[emp_holiday_list][idx][1]:
+								status = "Weekly Off"
+							else:
+								status = "Holiday"
+							total_h += 1
 
-			if status == "Present":
-				total_p += 1
-			elif status == "Absent":
-				total_a += 1
-			elif status == "On Leave":
-				total_l += 1
-			elif status == "Half Day":
-				total_p += 0.5
-				total_a += 0.5
-				total_l += 0.5
 
-		row += [total_p, total_l, total_a]
+				# if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list][0]:
+				# 	if holiday_map[emp_holiday_list][1]:
+				# 		status= "Weekly Off"
+				# 	else:
+				# 		status = "Holiday"
+
+				# 	 += 1
+
+			if not filters.summarized_view:
+				row.append(status_map.get(status, ""))
+			else:
+				if status == "Present":
+					total_p += 1
+				elif status == "Absent":
+					total_a += 1
+				elif status == "On Leave":
+					total_l += 1
+				elif status == "Half Day":
+					total_p += 0.5
+					total_a += 0.5
+					total_l += 0.5
+				elif not status:
+					total_um += 1
+
+		if filters.summarized_view:
+			row += [total_p, total_l, total_a, total_h, total_um]
 
 		if not filters.get("employee"):
 			filters.update({"employee": emp})
@@ -73,43 +135,53 @@
 		elif not filters.get("employee") == emp:
 			filters.update({"employee": emp})
 
-		leave_details = frappe.db.sql("""select leave_type, status, count(*) as count from `tabAttendance`\
-			where leave_type is not NULL %s group by leave_type, status""" % conditions, filters, as_dict=1)
+		if filters.summarized_view:
+			leave_details = frappe.db.sql("""select leave_type, status, count(*) as count from `tabAttendance`\
+				where leave_type is not NULL %s group by leave_type, status""" % conditions, filters, as_dict=1)
 
-		time_default_counts = frappe.db.sql("""select (select count(*) from `tabAttendance` where \
-			late_entry = 1 %s) as late_entry_count, (select count(*) from tabAttendance where \
-			early_exit = 1 %s) as early_exit_count""" % (conditions, conditions), filters)
+			time_default_counts = frappe.db.sql("""select (select count(*) from `tabAttendance` where \
+				late_entry = 1 %s) as late_entry_count, (select count(*) from tabAttendance where \
+				early_exit = 1 %s) as early_exit_count""" % (conditions, conditions), filters)
 
-		leaves = {}
-		for d in leave_details:
-			if d.status == "Half Day":
-				d.count = d.count * 0.5
-			if d.leave_type in leaves:
-				leaves[d.leave_type] += d.count
-			else:
-				leaves[d.leave_type] = d.count
+			leaves = {}
+			for d in leave_details:
+				if d.status == "Half Day":
+					d.count = d.count * 0.5
+				if d.leave_type in leaves:
+					leaves[d.leave_type] += d.count
+				else:
+					leaves[d.leave_type] = d.count
 
-		for d in leave_list:
-			if d in leaves:
-				row.append(leaves[d])
-			else:
-				row.append("0.0")
+			for d in leave_list:
+				if d in leaves:
+					row.append(leaves[d])
+				else:
+					row.append("0.0")
 
-		row.extend([time_default_counts[0][0],time_default_counts[0][1]])
-		data.append(row)
-	return columns, data
+			row.extend([time_default_counts[0][0],time_default_counts[0][1]])
+		record.append(row)
+
+
+	return record
 
 def get_columns(filters):
-	columns = [
-		_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Branch")+ ":Link/Branch:120",
-		_("Department") + ":Link/Department:120", _("Designation") + ":Link/Designation:120",
-		 _("Company") + ":Link/Company:120"
+
+	columns = []
+
+	if filters.group_by:
+		columns = [_(filters.group_by)+ ":Link/Branch:120"]
+
+	columns += [
+		_("Employee") + ":Link/Employee:120", _("Employee Name") + ":Link/Employee:120"
 	]
 
-	for day in range(filters["total_days_in_month"]):
-		columns.append(cstr(day+1) +"::20")
-
-	columns += [_("Total Present") + ":Float:80", _("Total Leaves") + ":Float:80",  _("Total Absent") + ":Float:80"]
+	if not filters.summarized_view:
+		for day in range(filters["total_days_in_month"]):
+			date = str(filters.year) + "-" + str(filters.month)+ "-" + str(day+1)
+			day_name = day_abbr[getdate(date).weekday()]
+			columns.append(cstr(day+1)+ " " +day_name +"::65")
+	else:
+		columns += [_("Total Present") + ":Float:120", _("Total Leaves") + ":Float:120",  _("Total Absent") + ":Float:120", _("Total Holidays") + ":Float:120", _("Unmarked Days")+ ":Float:120"]
 	return columns
 
 def get_attendance_list(conditions, filters):
@@ -140,19 +212,43 @@
 
 	return conditions, filters
 
-def get_employee_details(filters):
-	emp_map = frappe._dict()
-	for d in frappe.db.sql("""select name, employee_name, designation, department, branch, company,
-		holiday_list from tabEmployee where company = %s""", (filters.get("company")), as_dict=1):
-		emp_map.setdefault(d.name, d)
+def get_employee_details(group_by, company):
+	emp_map = {}
+	query = """select name, employee_name, designation, department, branch, company,
+		holiday_list from `tabEmployee` where company = %s """ % frappe.db.escape(company)
 
-	return emp_map
+	if group_by:
+		group_by = group_by.lower()
+		query += " order by " + group_by + " ASC"
+
+	employee_details = frappe.db.sql(query , as_dict=1)
+
+	group_by_parameters = []
+	if group_by:
+
+		group_by_parameters = list(set(detail.get(group_by, "") for detail in employee_details if detail.get(group_by, "")))
+		for parameter in group_by_parameters:
+				emp_map[parameter] = {}
+
+
+	for d in employee_details:
+		if group_by and len(group_by_parameters):
+			if d.get(group_by, None):
+
+				emp_map[d.get(group_by)][d.name] = d
+		else:
+			emp_map[d.name] = d
+
+	if not group_by:
+		return emp_map
+	else:
+		return emp_map, group_by_parameters
 
 def get_holiday(holiday_list, month):
 	holiday_map = frappe._dict()
 	for d in holiday_list:
 		if d:
-			holiday_map.setdefault(d, frappe.db.sql_list('''select day(holiday_date) from `tabHoliday`
+			holiday_map.setdefault(d, frappe.db.sql('''select day(holiday_date), weekly_off from `tabHoliday`
 				where parent=%s and month(holiday_date)=%s''', (d, month)))
 
 	return holiday_map
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index c7a2fba..76e10e5 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -233,7 +233,7 @@
 		return repayment_entry
 
 @frappe.whitelist()
-def create_loan_security_unpledge(loan, applicant_type, applicant, company):
+def create_loan_security_unpledge(loan, applicant_type, applicant, company, as_dict=1):
 	loan_security_pledge_details = frappe.db.sql("""
 		SELECT p.parent, p.loan_security, p.qty as qty FROM `tabLoan Security Pledge` lsp , `tabPledge` p
 		WHERE p.parent = lsp.name AND lsp.loan = %s AND lsp.docstatus = 1
@@ -248,11 +248,13 @@
 	for loan_security in loan_security_pledge_details:
 		unpledge_request.append('securities', {
 			"loan_security": loan_security.loan_security,
-			"qty": loan_security.qty,
-			"against_pledge": loan_security.parent
+			"qty": loan_security.qty
 		})
 
-	return unpledge_request.as_dict()
+	if as_dict:
+		return unpledge_request.as_dict()
+	else:
+		return unpledge_request
 
 
 
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 90b8534..364e2ff 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -14,6 +14,8 @@
 	process_loan_interest_accrual_for_term_loans)
 from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year
 from erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall import create_process_loan_security_shortfall
+from erpnext.loan_management.doctype.loan.loan import create_loan_security_unpledge
+from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
 
 class TestLoan(unittest.TestCase):
 	def setUp(self):
@@ -151,7 +153,7 @@
 		repayment_entry.save()
 		repayment_entry.submit()
 
-		penalty_amount = (accrued_interest_amount * 5 * 25) / (100 * days_in_year(get_datetime(first_date).year))
+		penalty_amount = (accrued_interest_amount * 4 * 25) / (100 * days_in_year(get_datetime(first_date).year))
 		self.assertEquals(flt(repayment_entry.penalty_amount, 2), flt(penalty_amount, 2))
 
 		amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
@@ -276,6 +278,55 @@
 		frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
 			where loan_security='Test Security 2'""")
 
+	def test_loan_security_unpledge(self):
+		pledges = []
+		pledges.append({
+			"loan_security": "Test Security 1",
+			"qty": 4000.00,
+			"haircut": 50
+		})
+
+		loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges)
+		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_security_pledge.name,
+			posting_date=get_first_day(nowdate()))
+		loan.submit()
+
+		self.assertEquals(loan.loan_amount, 1000000)
+
+		first_date = '2019-10-01'
+		last_date = '2019-10-30'
+
+		no_of_days = date_diff(last_date, first_date) + 1
+
+		no_of_days += 6
+
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
+			/ (days_in_year(get_datetime(first_date).year) * 100)
+
+		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+
+		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 6),
+			"Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
+		repayment_entry.submit()
+
+		amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
+			'paid_principal_amount'])
+
+		loan.load_from_db()
+		self.assertEquals(loan.status, "Loan Closure Requested")
+
+		unpledge_request = create_loan_security_unpledge(loan.name, loan.applicant_type, loan.applicant, loan.company, as_dict=0)
+		unpledge_request.submit()
+		unpledge_request.status = 'Approved'
+		unpledge_request.save()
+		loan.load_from_db()
+
+		pledged_qty = get_pledged_security_qty(loan.name)
+
+		self.assertEqual(loan.status, 'Closed')
+		self.assertEquals(sum(pledged_qty.values()), 0)
+
 
 def create_loan_accounts():
 	if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 5979ee3..2ab668a 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -78,7 +78,10 @@
 				(flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual))
 
 		if flt(loan.total_principal_paid + self.principal_amount_paid, 2) >= flt(loan.total_payment, 2):
-			frappe.db.set_value("Loan", self.against_loan, "status", "Loan Closure Requested")
+			if loan.is_secured_loan:
+				frappe.db.set_value("Loan", self.against_loan, "status", "Loan Closure Requested")
+			else:
+				frappe.db.set_value("Loan", self.against_loan, "status", "Closed")
 
 		frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
 			WHERE name = %s """, (loan.total_amount_paid + self.amount_paid,
@@ -261,6 +264,7 @@
 	penalty_amount = 0
 	payable_principal_amount = 0
 	final_due_date = ''
+	due_date = ''
 
 	for entry in accrued_interest_entries:
 		# Loan repayment due date is one day after the loan interest is accrued
@@ -269,7 +273,7 @@
 
 		due_date = add_days(entry.posting_date, 1)
 		no_of_late_days = date_diff(posting_date,
-					add_days(due_date, loan_type_details.grace_period_in_days)) + 1
+					add_days(due_date, loan_type_details.grace_period_in_days))
 
 		if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary):
 			penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days)/365
@@ -287,9 +291,9 @@
 
 	pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid - against_loan_doc.total_interest_payable
 
-	if payment_type == "Loan Closure" and not payable_principal_amount:
-		if final_due_date:
-			pending_days = date_diff(posting_date, final_due_date)
+	if payment_type == "Loan Closure":
+		if due_date:
+			pending_days = date_diff(posting_date, due_date) + 1
 		else:
 			pending_days = date_diff(posting_date, against_loan_doc.disbursement_date) + 1
 
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
index eb61358..961c05c 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
+++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
@@ -13,6 +13,7 @@
 class LoanSecurityPledge(Document):
 	def validate(self):
 		self.set_pledge_amount()
+		self.validate_duplicate_securities()
 
 	def on_submit(self):
 		if self.loan:
@@ -21,6 +22,15 @@
 			update_shortfall_status(self.loan, self.total_security_value)
 			update_loan(self.loan, self.maximum_loan_value)
 
+	def validate_duplicate_securities(self):
+		security_list = []
+		for security in self.securities:
+			if security.loan_security not in security_list:
+				security_list.append(security.loan_security)
+			else:
+				frappe.throw(_('Loan Security {0} added multiple times').format(frappe.bold(
+					security.loan_security)))
+
 	def set_pledge_amount(self):
 		total_security_value = 0
 		maximum_loan_value = 0
@@ -28,7 +38,7 @@
 		for pledge in self.securities:
 
 			if not pledge.qty and not pledge.amount:
-				frappe.throw(_("Qty or Amount is mandatroy for loan security"))
+				frappe.throw(_("Qty or Amount is mandatory for loan security!"))
 
 			if not (self.loan_application and pledge.loan_security_price):
 				pledge.loan_security_price = get_loan_security_price(pledge.loan_security)
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 ab040f1..308c438 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
@@ -53,7 +53,7 @@
 
 	loans = frappe.db.sql(""" SELECT l.name, l.loan_amount, l.total_principal_paid, lp.loan_security, lp.haircut, lp.qty, lp.loan_security_type
 		FROM `tabLoan` l, `tabPledge` lp , `tabLoan Security Pledge`p WHERE lp.parent = p.name and p.loan = l.name and l.docstatus = 1
-		and l.is_secured_loan and l.status = 'Disbursed' and p.status in ('Pledged', 'Partially Unpledged')""", as_dict=1)
+		and l.is_secured_loan and l.status = 'Disbursed' and p.status = 'Pledged'""", as_dict=1)
 
 	loan_security_map = {}
 
@@ -69,7 +69,7 @@
 		loan_security_map[loan.name]['security_value'] += current_loan_security_amount - (current_loan_security_amount * loan.haircut/100)
 
 	for loan, value in iteritems(loan_security_map):
-		if (value["security_value"]/value["loan_amount"]) < ltv_ratio:
+		if (value["loan_amount"]/value['security_value'] * 100) > ltv_ratio:
 			create_loan_security_shortfall(loan, value, process_loan_security_shortfall)
 
 def create_loan_security_shortfall(loan, value, process_loan_security_shortfall):
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js
index 72c5f38..8223206 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js
@@ -4,10 +4,8 @@
 frappe.ui.form.on('Loan Security Unpledge', {
 	refresh: function(frm) {
 
-		frm.set_query("against_pledge", "securities", () => {
-			return {
-				filters : [["status", "in", ["Pledged", "Partially Pledged"]]]
-			};
-		});
+		if (frm.doc.docstatus == 1 && frm.doc.status == 'Approved') {
+			frm.set_df_property('status', 'read_only', 1);
+		}
 	}
 });
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json
index ba94855..aece46f 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "LSU-.{applicant}.-.#####",
  "creation": "2019-09-21 13:23:16.117028",
  "doctype": "DocType",
@@ -15,7 +16,6 @@
   "status",
   "loan_security_details_section",
   "securities",
-  "unpledge_type",
   "amended_from"
  ],
  "fields": [
@@ -47,6 +47,7 @@
   {
    "allow_on_submit": 1,
    "default": "Requested",
+   "depends_on": "eval:doc.docstatus == 1",
    "fieldname": "status",
    "fieldtype": "Select",
    "label": "Status",
@@ -81,13 +82,6 @@
    "reqd": 1
   },
   {
-   "fieldname": "unpledge_type",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "Unpledge Type",
-   "read_only": 1
-  },
-  {
    "fieldname": "company",
    "fieldtype": "Link",
    "label": "Company",
@@ -104,7 +98,8 @@
   }
  ],
  "is_submittable": 1,
- "modified": "2019-10-28 07:41:47.084882",
+ "links": [],
+ "modified": "2020-05-05 07:23:18.440058",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Security Unpledge",
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
index 02b1ecb..5e9d82a 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
@@ -8,78 +8,114 @@
 from frappe.model.document import Document
 from frappe.utils import get_datetime, flt
 import json
+from six import iteritems
 from erpnext.loan_management.doctype.loan_security_price.loan_security_price import get_loan_security_price
 
 class LoanSecurityUnpledge(Document):
 	def validate(self):
-		self.validate_pledges()
+		self.validate_duplicate_securities()
+		self.validate_unpledge_qty()
 
-	def validate_pledges(self):
-		pledge_details = self.get_pledge_details()
+	def on_cancel(self):
+		self.update_loan_security_pledge(cancel=1)
+		self.update_loan_status(cancel=1)
+		self.db_set('status', 'Requested')
 
-		loan = frappe.get_doc("Loan", self.loan)
+	def validate_duplicate_securities(self):
+		security_list = []
+		for d in self.securities:
+			if d.loan_security not in security_list:
+				security_list.append(d.loan_security)
+			else:
+				frappe.throw(_("Row {0}: Loan Security {1} added multiple times").format(
+					d.idx, frappe.bold(d.loan_security)))
 
-		pledge_qty_map = {}
-		remaining_qty = 0
-		unpledge_value = 0
+	def validate_unpledge_qty(self):
+		pledge_qty_map = get_pledged_security_qty(self.loan)
 
-		for pledge in pledge_details:
-			pledge_qty_map.setdefault((pledge.parent, pledge.loan_security), pledge.qty)
+		ltv_ratio_map = frappe._dict(frappe.get_all("Loan Security Type",
+			fields=["name", "loan_to_value_ratio"], as_list=1))
+
+		loan_security_price_map = frappe._dict(frappe.get_all("Loan Security Price",
+			fields=["loan_security", "loan_security_price"],
+			filters = {
+				"valid_from": ("<=", get_datetime()),
+				"valid_upto": (">=", get_datetime())
+			}, as_list=1))
+
+		loan_amount, principal_paid = frappe.get_value("Loan", self.loan, ['loan_amount', 'total_principal_paid'])
+		pending_principal_amount = loan_amount - principal_paid
+		security_value = 0
 
 		for security in self.securities:
-			pledged_qty = pledge_qty_map.get((security.against_pledge, security.loan_security), 0)
-			if not pledged_qty:
-				frappe.throw(_("Zero qty of {0} pledged against loan {0}").format(frappe.bold(security.loan_security),
-					frappe.bold(self.loan)))
+			pledged_qty = pledge_qty_map.get(security.loan_security)
 
-			unpledge_qty = pledged_qty - security.qty
-			security_price = security.qty * get_loan_security_price(security.loan_security)
+			if security.qty > pledged_qty:
+				frappe.throw(_("""Row {0}: {1} {2} of {3} is pledged against Loan {4}.
+					You are trying to unpledge more""").format(security.idx, pledged_qty, security.uom,
+					frappe.bold(security.loan_security), frappe.bold(self.loan)))
 
-			if unpledge_qty < 0:
-				frappe.throw(_("Cannot unpledge more than {0} qty of {0}").format(frappe.bold(pledged_qty),
-					frappe.bold(security.loan_security)))
+			qty_after_unpledge = pledged_qty - security.qty
+			ltv_ratio = ltv_ratio_map.get(security.loan_security_type)
 
-			remaining_qty += unpledge_qty
-			unpledge_value += security_price - flt(security_price * security.haircut/100)
+			security_value += qty_after_unpledge * loan_security_price_map.get(security.loan_security)
 
-		if unpledge_value > loan.total_principal_paid:
-			frappe.throw(_("Cannot Unpledge, loan security value is greater than the repaid amount"))
+		if not security_value and pending_principal_amount > 0:
+			frappe.throw("Cannot Unpledge, loan to value ratio is breaching")
 
-		if not remaining_qty:
-			self.db_set('unpledge_type', 'Unpledged')
-		else:
-			self.db_set('unpledge_type', 'Partially Pledged')
-
-
-	def get_pledge_details(self):
-		pledge_details = frappe.db.sql("""
-			SELECT p.parent, p.loan_security, p.qty as qty FROM
-				`tabLoan Security Pledge` lsp,
-				`tabPledge` p
-			WHERE
-				p.parent = lsp.name
-				AND lsp.loan = %s
-				AND lsp.docstatus = 1
-				AND lsp.status = "Pledged"
-		""",(self.loan), as_dict=1)
-
-		return pledge_details
+		if security_value and (pending_principal_amount/security_value) * 100 > ltv_ratio:
+			frappe.throw("Cannot Unpledge, loan to value ratio is breaching")
 
 	def on_update_after_submit(self):
 		if self.status == "Approved":
-			frappe.db.sql("""
-				UPDATE
-					`tabPledge` p, `tabUnpledge` u, `tabLoan Security Pledge` lsp,
-					`tabLoan Security Unpledge` lsu SET p.qty = (p.qty - u.qty)
-				WHERE
-					lsp.loan = %s
-					AND lsu.status = 'Requested'
-					AND u.parent = %s
-					AND p.parent = u.against_pledge
-					AND p.loan_security = u.loan_security""",(self.loan, self.name))
+			self.update_loan_status()
+			self.db_set('unpledge_time', get_datetime())
 
-			frappe.db.sql("""UPDATE `tabLoan Security Pledge`
-				SET status = %s WHERE loan = %s""", (self.unpledge_type, self.loan))
+	def update_loan_status(self, cancel=0):
+		if cancel:
+			loan_status = frappe.get_value('Loan', self.loan, 'status')
+			if loan_status == 'Closed':
+				frappe.db.set_value('Loan', self.loan, 'status', 'Loan Closure Requested')
+		else:
+			pledged_qty = 0
+			current_pledges = get_pledged_security_qty(self.loan)
 
-			if self.unpledge_type == 'Unpledged':
-				frappe.db.set_value("Loan", self.loan, 'status', 'Closed')
+			for security, qty in iteritems(current_pledges):
+				pledged_qty += qty
+
+			if not pledged_qty:
+				frappe.db.set_value('Loan', self.loan, 'status', 'Closed')
+
+@frappe.whitelist()
+def get_pledged_security_qty(loan):
+
+	current_pledges = {}
+
+	unpledges = frappe._dict(frappe.db.sql("""
+		SELECT u.loan_security, sum(u.qty) as qty
+		FROM `tabLoan Security Unpledge` up, `tabUnpledge` u
+		WHERE up.loan = %s
+		AND u.parent = up.name
+		AND up.status = 'Approved'
+		GROUP BY u.loan_security
+	""", (loan)))
+
+	pledges = frappe._dict(frappe.db.sql("""
+		SELECT p.loan_security, sum(p.qty) as qty
+		FROM `tabLoan Security Pledge` lp, `tabPledge`p
+		WHERE lp.loan = %s
+		AND p.parent = lp.name
+		AND lp.status = 'Pledged'
+		GROUP BY p.loan_security
+	""", (loan)))
+
+	for security, qty in iteritems(pledges):
+		current_pledges.setdefault(security, qty)
+		current_pledges[security] -= unpledges.get(security, 0.0)
+
+	return current_pledges
+
+
+
+
+
diff --git a/erpnext/loan_management/doctype/unpledge/unpledge.json b/erpnext/loan_management/doctype/unpledge/unpledge.json
index 9e6277d..ee192d7 100644
--- a/erpnext/loan_management/doctype/unpledge/unpledge.json
+++ b/erpnext/loan_management/doctype/unpledge/unpledge.json
@@ -1,11 +1,11 @@
 {
+ "actions": [],
  "creation": "2019-09-21 13:22:19.793797",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
   "loan_security",
-  "against_pledge",
   "loan_security_type",
   "loan_security_code",
   "haircut",
@@ -55,14 +55,6 @@
    "reqd": 1
   },
   {
-   "fieldname": "against_pledge",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Against Pledge",
-   "options": "Loan Security Pledge",
-   "reqd": 1
-  },
-  {
    "fetch_from": "loan_security.haircut",
    "fieldname": "haircut",
    "fieldtype": "Percent",
@@ -71,7 +63,8 @@
   }
  ],
  "istable": 1,
- "modified": "2019-10-02 12:48:18.588236",
+ "links": [],
+ "modified": "2020-05-06 10:50:18.448552",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Unpledge",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index e9627a5..e43b98a 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -206,30 +206,31 @@
 		for_quantity, time_in_mins = 0, 0
 		from_time_list, to_time_list = [], []
 
-
+		field = "operation_id" if self.operation_id else "operation"
 		data = frappe.get_all('Job Card',
 			fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
 			filters = {"docstatus": 1, "work_order": self.work_order,
-				"workstation": self.workstation, "operation": self.operation})
+				"workstation": self.workstation, field: self.get(field)})
 
 		if data and len(data) > 0:
 			for_quantity = data[0].completed_qty
 			time_in_mins = data[0].time_in_mins
 
-		if for_quantity:
+		if self.get(field):
 			time_data = frappe.db.sql("""
 				SELECT
 					min(from_time) as start_time, max(to_time) as end_time
 				FROM `tabJob Card` jc, `tabJob Card Time Log` jctl
 				WHERE
 					jctl.parent = jc.name and jc.work_order = %s
-					and jc.workstation = %s and jc.operation = %s and jc.docstatus = 1
-			""", (self.work_order, self.workstation, self.operation), as_dict=1)
+					and jc.workstation = %s and jc.{0} = %s and jc.docstatus = 1
+			""".format(field), (self.work_order, self.workstation, self.get(field)), as_dict=1)
 
 			wo = frappe.get_doc('Work Order', self.work_order)
 
+			work_order_field = "name" if field == "operation_id" else field
 			for data in wo.operations:
-				if data.workstation == self.workstation and data.operation == self.operation:
+				if data.get(work_order_field) == self.get(field) and data.workstation == self.workstation:
 					data.completed_qty = for_quantity
 					data.actual_operation_time = time_in_mins
 					data.actual_start_time = time_data[0].start_time if time_data else None
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 84bfab2..8301f30 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -421,6 +421,9 @@
 		return holidays[holiday_list]
 
 	def update_operation_status(self):
+		allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", "overproduction_percentage_for_work_order"))
+		max_allowed_qty_for_wo = flt(self.qty) + (allowance_percentage/100 * flt(self.qty))
+
 		for d in self.get("operations"):
 			if not d.completed_qty:
 				d.status = "Pending"
@@ -428,6 +431,8 @@
 				d.status = "Work in Progress"
 			elif flt(d.completed_qty) == flt(self.qty):
 				d.status = "Completed"
+			elif flt(d.completed_qty) <= max_allowed_qty_for_wo:
+				d.status = "Completed"
 			else:
 				frappe.throw(_("Completed Qty can not be greater than 'Qty to Manufacture'"))
 
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 8a31cf3..0edadcc 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -495,6 +495,7 @@
 execute:frappe.delete_doc('DocType', 'Production Planning Tool', ignore_missing=True)
 erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group # 24-12-2018
 erpnext.patches.v10_0.add_default_cash_flow_mappers
+erpnext.patches.v11_0.rename_duplicate_item_code_values
 erpnext.patches.v11_0.make_quality_inspection_template
 erpnext.patches.v10_0.update_status_for_multiple_source_in_po
 erpnext.patches.v10_0.set_auto_created_serial_no_in_stock_entry
@@ -630,7 +631,6 @@
 execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_source')
 execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart')
 execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_field')
-erpnext.patches.v12_0.add_default_dashboards
 erpnext.patches.v12_0.remove_bank_remittance_custom_fields
 erpnext.patches.v12_0.generate_leave_ledger_entries
 execute:frappe.delete_doc_if_exists("Report", "Loan Repayment")
@@ -678,3 +678,7 @@
 erpnext.patches.v12_0.fix_quotation_expired_status
 erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
 erpnext.patches.v12_0.retain_permission_rules_for_video_doctype
+erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
+execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
+execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
+erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price
diff --git a/erpnext/patches/v11_0/rename_duplicate_item_code_values.py b/erpnext/patches/v11_0/rename_duplicate_item_code_values.py
new file mode 100644
index 0000000..00ab562
--- /dev/null
+++ b/erpnext/patches/v11_0/rename_duplicate_item_code_values.py
@@ -0,0 +1,8 @@
+import frappe
+
+def execute():
+	items = []
+	items = frappe.db.sql("""select item_code from `tabItem` group by item_code having count(*) > 1""", as_dict=True)
+	if items:
+		for item in items:
+			frappe.db.sql("""update `tabItem` set item_code=name where item_code = %s""", (item.item_code))
diff --git a/erpnext/patches/v11_0/set_default_email_template_in_hr.py b/erpnext/patches/v11_0/set_default_email_template_in_hr.py
index 14954fb..4622376 100644
--- a/erpnext/patches/v11_0/set_default_email_template_in_hr.py
+++ b/erpnext/patches/v11_0/set_default_email_template_in_hr.py
@@ -1,8 +1,9 @@
 from __future__ import unicode_literals
+from frappe import _
 import frappe
 
 def execute():
 	hr_settings = frappe.get_single("HR Settings")
-	hr_settings.leave_approval_notification_template = "Leave Approval Notification"
-	hr_settings.leave_status_notification_template = "Leave Status Notification"
-	hr_settings.save()
\ No newline at end of file
+	hr_settings.leave_approval_notification_template = _("Leave Approval Notification")
+	hr_settings.leave_status_notification_template = _("Leave Status Notification")
+	hr_settings.save()
diff --git a/erpnext/patches/v12_0/add_default_dashboards.py b/erpnext/patches/v12_0/add_default_dashboards.py
deleted file mode 100644
index 0c3f2f8..0000000
--- a/erpnext/patches/v12_0/add_default_dashboards.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-from erpnext.setup.setup_wizard.operations.install_fixtures import add_dashboards
-
-def execute():
-	frappe.reload_doc("desk", "doctype", "number_card_link")
-	add_dashboards()
diff --git a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py
index f5bd8c3..6f843cd 100644
--- a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py
+++ b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py
@@ -3,6 +3,10 @@
 from collections import defaultdict
 
 def execute():
+
+	frappe.reload_doc('stock', 'doctype', 'delivery_note_item', force=True)
+	frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item', force=True)
+
 	def map_rows(doc_row, return_doc_row, detail_field, doctype):
 		"""Map rows after identifying similar ones."""
 
diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py
new file mode 100644
index 0000000..60aec05
--- /dev/null
+++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py
@@ -0,0 +1,15 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+    invalid_selling_item_price = frappe.db.sql(
+        """SELECT name FROM `tabItem Price` WHERE selling = 1 and buying = 0 and (supplier IS NOT NULL or supplier = '')"""
+    )
+    invalid_buying_item_price = frappe.db.sql(
+        """SELECT name FROM `tabItem Price` WHERE selling = 0 and buying = 1 and (customer IS NOT NULL or customer = '')"""
+    )
+    docs_to_modify = invalid_buying_item_price + invalid_selling_item_price
+    for d in docs_to_modify:
+        # saving the doc will auto reset invalid customer/supplier field
+        doc = frappe.get_doc("Item Price", d[0])
+        doc.save()
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py
index 179be2c..ec94cd0 100644
--- a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py
+++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py
@@ -7,7 +7,7 @@
 from frappe.model.utils.rename_field import rename_field
 
 def execute():
-	if not frappe.db.table_exists("Payroll Period"):
+	if not (frappe.db.table_exists("Payroll Period") and frappe.db.table_exists("Taxable Salary Slab")):
 		return
 
 	for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income", "income_tax_slab_other_charges"):
@@ -60,6 +60,9 @@
 				""", (income_tax_slab.name, company.name, period.start_date))
 
 	# move other incomes to separate document
+	if not frappe.db.table_exists("Employee Tax Exemption Proof Submission"):
+		return
+
 	migrated = []
 	proofs = frappe.get_all("Employee Tax Exemption Proof Submission",
 		filters = {'docstatus': 1},
@@ -79,6 +82,9 @@
 			except:
 				pass
 
+	if not frappe.db.table_exists("Employee Tax Exemption Declaration"):
+		return
+
 	declerations = frappe.get_all("Employee Tax Exemption Declaration",
 		filters = {'docstatus': 1},
 		fields =['payroll_period', 'employee', 'company', 'income_from_other_sources']
diff --git a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py
new file mode 100644
index 0000000..ddcadcb
--- /dev/null
+++ b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py
@@ -0,0 +1,52 @@
+from __future__ import unicode_literals
+
+import frappe
+
+def execute():
+	if not frappe.db.table_exists("Additional Salary"):
+		return
+
+	for doctype in ("Additional Salary", "Leave Encashment", "Employee Incentive", "Salary Detail"):
+		frappe.reload_doc("hr", "doctype", doctype)
+
+	additional_salaries = frappe.get_all("Additional Salary",
+		fields = ['name', "salary_slip", "type", "salary_component"],
+		filters = {'salary_slip': ['!=', '']},
+		group_by = 'salary_slip'
+	)
+	leave_encashments = frappe.get_all("Leave Encashment",
+		fields = ["name","additional_salary"],
+		filters = {'additional_salary': ['!=', '']}
+	)
+	employee_incentives = frappe.get_all("Employee Incentive",
+		fields= ["name", "additional_salary"],
+		filters = {'additional_salary': ['!=', '']}
+	)
+
+	for incentive in employee_incentives:
+		frappe.db.sql(""" UPDATE `tabAdditional Salary`
+			SET ref_doctype = 'Employee Incentive', ref_docname = %s
+			WHERE name = %s
+		""", (incentive['name'], incentive['additional_salary']))
+
+
+	for leave_encashment in leave_encashments:
+		frappe.db.sql(""" UPDATE `tabAdditional Salary`
+			SET ref_doctype = 'Leave Encashment', ref_docname = %s
+			WHERE name = %s
+		""", (leave_encashment['name'], leave_encashment['additional_salary']))
+
+	salary_slips = [sal["salary_slip"] for sal in additional_salaries]
+
+	for salary in additional_salaries:
+		comp_type = "earnings" if salary['type'] == 'Earning' else 'deductions'
+		if salary["salary_slip"] and salary_slips.count(salary["salary_slip"]) == 1:
+			frappe.db.sql("""
+				UPDATE `tabSalary Detail`
+				SET additional_salary = %s
+				WHERE parenttype = 'Salary Slip'
+					and parentfield = %s
+					and parent = %s
+					and salary_component = %s
+			""", (salary["name"], comp_type, salary["salary_slip"], salary["salary_component"]))
+
diff --git a/erpnext/regional/address_template/templates/taiwan.html b/erpnext/regional/address_template/templates/taiwan.html
new file mode 100644
index 0000000..1715bea
--- /dev/null
+++ b/erpnext/regional/address_template/templates/taiwan.html
@@ -0,0 +1,4 @@
+{{ country }}<br>{% if pincode %}{{ pincode }}<br>{% endif -%}{{ county }}{{ city }}{{ address_line1 }}{% if address_line2 %}{{ address_line2 }}{% endif -%}
+{% if phone %}<br>Phone: {{ phone }}{% endif -%}
+{% if fax %}<br>Fax: {{ fax }}{% endif -%}
+{% if email_id %}<br>Email: {{ email_id }}{% endif -%}
diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js
index a854fa9..d93ffb7 100644
--- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js
+++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js
@@ -4,6 +4,14 @@
 frappe.query_reports["Customer Acquisition and Loyalty"] = {
 	"filters": [
 		{
+			"fieldname": "view_type",
+			"label": __("View Type"),
+			"fieldtype": "Select",
+			"options": ["Monthly", "Territory Wise"],
+			"default": "Monthly",
+			"reqd": 1
+		},
+		{
 			"fieldname":"company",
 			"label": __("Company"),
 			"fieldtype": "Link",
@@ -24,6 +32,13 @@
 			"fieldtype": "Date",
 			"default": frappe.defaults.get_user_default("year_end_date"),
 			"reqd": 1
-		},
-	]
-}
+		}
+	],
+	'formatter': function(value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+		if (data && data.bold) {
+			value = value.bold();
+		}
+		return value;
+	}
+}
\ No newline at end of file
diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
index aa57665..88bd9c1 100644
--- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
+++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
@@ -2,65 +2,186 @@
 # License: GNU General Public License v3. See license.txt
 
 from __future__ import unicode_literals
+import calendar
 import frappe
 from frappe import _
-from frappe.utils import getdate, cint, cstr
-import calendar
+from frappe.utils import cint, cstr
 
 def execute(filters=None):
-	# key yyyy-mm
-	new_customers_in = {}
-	repeat_customers_in = {}
-	customers = []
-	company_condition = ""
+    common_columns = [
+        {
+            'label': _('New Customers'),
+            'fieldname': 'new_customers',
+            'fieldtype': 'Int',
+            'default': 0,
+            'width': 125
+        },
+        {
+            'label': _('Repeat Customers'),
+            'fieldname': 'repeat_customers',
+            'fieldtype': 'Int',
+            'default': 0,
+            'width': 125
+        },
+        {
+            'label': _('Total'),
+            'fieldname': 'total',
+            'fieldtype': 'Int',
+            'default': 0,
+            'width': 100
+        },
+        {
+            'label': _('New Customer Revenue'),
+            'fieldname': 'new_customer_revenue',
+            'fieldtype': 'Currency',
+            'default': 0.0,
+            'width': 175
+        },
+        {
+            'label': _('Repeat Customer Revenue'),
+            'fieldname': 'repeat_customer_revenue',
+            'fieldtype': 'Currency',
+            'default': 0.0,
+            'width': 175
+        },
+        {
+            'label': _('Total Revenue'),
+            'fieldname': 'total_revenue',
+            'fieldtype': 'Currency',
+            'default': 0.0,
+            'width': 175
+        }
+    ]
+    if filters.get('view_type') == 'Monthly':
+        return get_data_by_time(filters, common_columns)
+    else:
+        return get_data_by_territory(filters, common_columns)
 
-	if filters.get("company"):
-		company_condition = ' and company=%(company)s'
+def get_data_by_time(filters, common_columns):
+    # key yyyy-mm
+    columns = [
+        {
+            'label': _('Year'),
+            'fieldname': 'year',
+            'fieldtype': 'Data',
+            'width': 100
+        },
+        {
+            'label': _('Month'),
+            'fieldname': 'month',
+            'fieldtype': 'Data',
+            'width': 100
+        },
+    ]
+    columns += common_columns
 
-	for si in frappe.db.sql("""select posting_date, customer, base_grand_total from `tabSales Invoice`
-		where docstatus=1 and posting_date <= %(to_date)s
-		{company_condition} order by posting_date""".format(company_condition=company_condition),
-		filters, as_dict=1):
+    customers_in = get_customer_stats(filters)
 
-		key = si.posting_date.strftime("%Y-%m")
-		if not si.customer in customers:
-			new_customers_in.setdefault(key, [0, 0.0])
-			new_customers_in[key][0] += 1
-			new_customers_in[key][1] += si.base_grand_total
-			customers.append(si.customer)
-		else:
-			repeat_customers_in.setdefault(key, [0, 0.0])
-			repeat_customers_in[key][0] += 1
-			repeat_customers_in[key][1] += si.base_grand_total
+    # time series
+    from_year, from_month, temp = filters.get('from_date').split('-')
+    to_year, to_month, temp = filters.get('to_date').split('-')
 
-	# time series
-	from_year, from_month, temp = filters.get("from_date").split("-")
-	to_year, to_month, temp = filters.get("to_date").split("-")
+    from_year, from_month, to_year, to_month = \
+        cint(from_year), cint(from_month), cint(to_year), cint(to_month)
 
-	from_year, from_month, to_year, to_month = \
-		cint(from_year), cint(from_month), cint(to_year), cint(to_month)
+    out = []
+    for year in range(from_year, to_year+1):
+        for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13):
+            key = '{year}-{month:02d}'.format(year=year, month=month)
+            data = customers_in.get(key)
+            new = data['new'] if data else [0, 0.0]
+            repeat = data['repeat'] if data else [0, 0.0]
+            out.append({
+                'year': cstr(year),
+                'month': calendar.month_name[month],
+                'new_customers': new[0],
+                'repeat_customers': repeat[0],
+                'total': new[0] + repeat[0],
+                'new_customer_revenue': new[1],
+                'repeat_customer_revenue': repeat[1],
+                'total_revenue': new[1] + repeat[1]
+            })
+    return columns, out
 
-	out = []
-	for year in range(from_year, to_year+1):
-		for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13):
-			key = "{year}-{month:02d}".format(year=year, month=month)
+def get_data_by_territory(filters, common_columns):
+    columns = [{
+        'label': 'Territory',
+        'fieldname': 'territory',
+        'fieldtype': 'Link',
+        'options': 'Territory',
+        'width': 150
+    }]
+    columns += common_columns
 
-			new = new_customers_in.get(key, [0,0.0])
-			repeat = repeat_customers_in.get(key, [0,0.0])
+    customers_in = get_customer_stats(filters, tree_view=True)
 
-			out.append([cstr(year), calendar.month_name[month],
-				new[0], repeat[0], new[0] + repeat[0],
-				new[1], repeat[1], new[1] + repeat[1]])
+    territory_dict = {}
+    for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1):
+        territory_dict.update({
+            t.name: {
+                'parent': t.parent_territory,
+                'is_group': t.is_group
+            }
+        })
 
-	return [
-		_("Year") + "::100",
-		_("Month") + "::100",
-		_("New Customers") + ":Int:100",
-		_("Repeat Customers") + ":Int:100",
-		_("Total") + ":Int:100",
-		_("New Customer Revenue") + ":Currency:150",
-		_("Repeat Customer Revenue") + ":Currency:150",
-		_("Total Revenue") + ":Currency:150"
-	], out
+    depth_map = frappe._dict()
+    for name, info in territory_dict.items():
+        default = depth_map.get(info['parent']) + 1 if info['parent'] else 0
+        depth_map.setdefault(name, default)
 
+    data = []
+    for name, indent in depth_map.items():
+        condition = customers_in.get(name)
+        new = customers_in[name]['new'] if condition else [0, 0.0]
+        repeat = customers_in[name]['repeat'] if condition else [0, 0.0]
+        temp = {
+            'territory': name,
+            'parent_territory': territory_dict[name]['parent'],
+            'indent': indent,
+            'new_customers': new[0],
+            'repeat_customers': repeat[0],
+            'total': new[0] + repeat[0],
+            'new_customer_revenue': new[1],
+            'repeat_customer_revenue': repeat[1],
+            'total_revenue': new[1] + repeat[1],
+            'bold': 0 if indent else 1
+        }
+        data.append(temp)
 
+    loop_data = sorted(data, key=lambda k: k['indent'], reverse=True)
+
+    for ld in loop_data:
+        if ld['parent_territory']:
+            parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0]
+            for key in parent_data.keys():
+                if key not in  ['indent', 'territory', 'parent_territory', 'bold']:
+                    parent_data[key] += ld[key]
+
+    return columns, data, None, None, None, 1
+
+def get_customer_stats(filters, tree_view=False):
+    """ Calculates number of new and repeated customers. """
+    company_condition = ''
+    if filters.get('company'):
+        company_condition = ' and company=%(company)s'
+
+    customers = []
+    customers_in = {}
+
+    for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice`
+        where docstatus=1 and posting_date <= %(to_date)s and posting_date >= %(from_date)s
+        {company_condition} order by posting_date'''.format(company_condition=company_condition),
+        filters, as_dict=1):
+
+        key = si.territory if tree_view else si.posting_date.strftime('%Y-%m')
+        customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]})
+
+        if not si.customer in customers:
+            customers_in[key]['new'][0] += 1
+            customers_in[key]['new'][1] += si.base_grand_total
+            customers.append(si.customer)
+        else:
+            customers_in[key]['repeat'][0] += 1
+            customers_in[key]['repeat'][1] += si.base_grand_total
+
+    return customers_in
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json
index 7193261..bf9edd6 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.json
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.json
@@ -1,31 +1,31 @@
 {
- "add_total_row": 0, 
- "creation": "2018-09-21 12:46:29.451048", 
- "disable_prepared_report": 0, 
- "disabled": 0, 
- "docstatus": 0, 
- "doctype": "Report", 
- "idx": 0, 
- "is_standard": "Yes", 
- "modified": "2019-05-24 05:37:02.866139", 
- "modified_by": "Administrator", 
- "module": "Selling", 
- "name": "Sales Analytics", 
- "owner": "Administrator", 
- "prepared_report": 0, 
- "ref_doctype": "Sales Order", 
- "report_name": "Sales Analytics", 
- "report_type": "Script Report", 
+ "add_total_row": 0,
+ "creation": "2018-09-21 12:46:29.451048",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-04-30 19:49:02.303320",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Analytics",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Sales Order",
+ "report_name": "Sales Analytics",
+ "report_type": "Script Report",
  "roles": [
   {
    "role": "Stock User"
-  }, 
+  },
   {
    "role": "Maintenance User"
-  }, 
+  },
   {
    "role": "Accounts User"
-  }, 
+  },
   {
    "role": "Sales Manager"
   }
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py
index f1726ab..97d9322 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.py
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.py
@@ -194,6 +194,9 @@
 	def get_rows(self):
 		self.data = []
 		self.get_periodic_data()
+		total_row = {
+			"entity": "Total",
+		}
 
 		for entity, period_data in iteritems(self.entity_periodic_data):
 			row = {
@@ -207,6 +210,9 @@
 				row[scrub(period)] = amount
 				total += amount
 
+				if not total_row.get(scrub(period)): total_row[scrub(period)] = 0
+				total_row[scrub(period)] += amount
+
 			row["total"] = total
 
 			if self.filters.tree_type == "Item":
@@ -214,6 +220,8 @@
 
 			self.data.append(row)
 
+		self.data.append(total_row)
+
 	def get_rows_by_group(self):
 		self.get_periodic_data()
 		out = []
@@ -232,8 +240,10 @@
 					self.entity_periodic_data.setdefault(d.parent, frappe._dict()).setdefault(period, 0.0)
 					self.entity_periodic_data[d.parent][period] += amount
 				total += amount
+
 			row["total"] = total
 			out = [row] + out
+
 		self.data = out
 
 	def get_periodic_data(self):
diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py
index 4d81a1e..7e8501d 100644
--- a/erpnext/selling/report/sales_analytics/test_analytics.py
+++ b/erpnext/selling/report/sales_analytics/test_analytics.py
@@ -34,6 +34,21 @@
 
 		expected_data = [
 			{
+				'entity': 'Total',
+				'apr_2017': 0.0,
+				'may_2017': 0.0,
+				'jun_2017': 2000.0,
+				'jul_2017': 1000.0,
+				'aug_2017': 0.0,
+				'sep_2017': 1500.0,
+				'oct_2017': 1000.0,
+				'nov_2017': 0.0,
+				'dec_2017': 0.0,
+				'jan_2018': 0.0,
+				'feb_2018': 2000.0,
+				'mar_2018': 0.0
+  			},
+			{
 				"entity": "_Test Customer 1",
 				"entity_name": "_Test Customer 1",
 				"apr_2017": 0.0,
@@ -135,6 +150,21 @@
 
 		expected_data = [
 			{
+				'entity': 'Total',
+				'apr_2017': 0.0,
+				'may_2017': 0.0,
+				'jun_2017': 20.0,
+				'jul_2017': 10.0,
+				'aug_2017': 0.0,
+				'sep_2017': 15.0,
+				'oct_2017': 10.0,
+				'nov_2017': 0.0,
+				'dec_2017': 0.0,
+				'jan_2018': 0.0,
+				'feb_2018': 20.0,
+				'mar_2018': 0.0
+  			},
+			{
 				"entity": "_Test Customer 1",
 				"entity_name": "_Test Customer 1",
 				"apr_2017": 0.0,
diff --git a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
index f2db478..e883500 100644
--- a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
+++ b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
@@ -20,31 +20,36 @@
 			"label": _("Territory"),
 			"fieldname": "territory",
 			"fieldtype": "Link",
-			"options": "Territory"
+			"options": "Territory",
+			"width": 150
 		},
 		{
 			"label": _("Opportunity Amount"),
 			"fieldname": "opportunity_amount",
 			"fieldtype": "Currency",
-			"options": currency
+			"options": currency,
+			"width": 150
 		},
 		{
 			"label": _("Quotation Amount"),
 			"fieldname": "quotation_amount",
 			"fieldtype": "Currency",
-			"options": currency
+			"options": currency,
+			"width": 150
 		},
 		{
 			"label": _("Order Amount"),
 			"fieldname": "order_amount",
 			"fieldtype": "Currency",
-			"options": currency
+			"options": currency,
+			"width": 150
 		},
 		{
 			"label": _("Billing Amount"),
 			"fieldname": "billing_amount",
 			"fieldtype": "Currency",
-			"options": currency
+			"options": currency,
+			"width": 150
 		}
 	]
 
@@ -62,8 +67,7 @@
 			territory_opportunities = list(filter(lambda x: x.territory == territory.name, opportunities))
 		t_opportunity_names = []
 		if territory_opportunities:
-			t_opportunity_names = [t.name for t in territory_opportunities] 
-
+			t_opportunity_names = [t.name for t in territory_opportunities]
 		territory_quotations = []
 		if t_opportunity_names and quotations:
 			territory_quotations = list(filter(lambda x: x.opportunity in t_opportunity_names, quotations))
@@ -76,7 +80,7 @@
 			list(filter(lambda x: x.quotation in t_quotation_names, sales_orders))
 		t_order_names = []
 		if territory_orders:
-			t_order_names = [t.name for t in territory_orders] 
+			t_order_names = [t.name for t in territory_orders]
 
 		territory_invoices = list(filter(lambda x: x.sales_order in t_order_names, sales_invoices)) if t_order_names and sales_invoices else []
 
@@ -96,12 +100,12 @@
 
 	if filters.get('transaction_date'):
 		conditions = " WHERE transaction_date between {0} and {1}".format(
-			frappe.db.escape(filters['transaction_date'][0]), 
+			frappe.db.escape(filters['transaction_date'][0]),
 			frappe.db.escape(filters['transaction_date'][1]))
-	
+
 	if filters.company:
 		if conditions:
-			conditions += " AND" 
+			conditions += " AND"
 		else:
 			conditions += " WHERE"
 		conditions += " company = %(company)s"
@@ -115,7 +119,7 @@
 def get_quotations(opportunities):
 	if not opportunities:
 		return []
-	
+
 	opportunity_names = [o.name for o in opportunities]
 
 	return frappe.db.sql("""
@@ -155,5 +159,5 @@
 	total = 0
 	for doc in doclist:
 		total += doc.get(amount_field, 0)
-	
+
 	return total
diff --git a/erpnext/setup/desk_page/getting_started/getting_started.json b/erpnext/setup/desk_page/home/home.json
similarity index 96%
rename from erpnext/setup/desk_page/getting_started/getting_started.json
rename to erpnext/setup/desk_page/home/home.json
index 63d8984..63cd5c5 100644
--- a/erpnext/setup/desk_page/getting_started/getting_started.json
+++ b/erpnext/setup/desk_page/home/home.json
@@ -47,26 +47,20 @@
   }
  ],
  "category": "Modules",
- "charts": [
-  {
-   "chart_name": "Bank Balance",
-   "label": "Bank Balance"
-  }
- ],
+ "charts": [],
  "creation": "2020-01-23 13:46:38.833076",
  "developer_mode_only": 0,
  "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Desk Page",
  "extends_another_page": 0,
- "icon": "",
  "idx": 0,
  "is_standard": 1,
- "label": "Getting Started",
- "modified": "2020-04-01 11:30:19.763099",
+ "label": "Home",
+ "modified": "2020-05-11 10:20:37.358701",
  "modified_by": "Administrator",
  "module": "Setup",
- "name": "Getting Started",
+ "name": "Home",
  "owner": "Administrator",
  "pin_to_bottom": 0,
  "pin_to_top": 1,
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index b37cc17..29f6c37 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -47,9 +47,7 @@
 		frappe.delete_doc("Company", "COA from Existing Company")
 
 	def test_coa_based_on_country_template(self):
-		countries = ["India", "Brazil", "United Arab Emirates", "Canada", "Germany", "France",
-			"Guatemala", "Indonesia", "Italy", "Mexico", "Nicaragua", "Netherlands", "Singapore",
-			"Brazil", "Argentina", "Hungary", "Taiwan"]
+		countries = ["Canada", "Germany", "France"]
 
 		for country in countries:
 			templates = get_charts_for_country(country)
diff --git a/erpnext/setup/doctype/territory/territory.py b/erpnext/setup/doctype/territory/territory.py
index 095bd1c..808b538 100644
--- a/erpnext/setup/doctype/territory/territory.py
+++ b/erpnext/setup/doctype/territory/territory.py
@@ -3,8 +3,6 @@
 
 from __future__ import unicode_literals
 import frappe
-
-
 from frappe.utils import flt
 from frappe import _
 
@@ -14,6 +12,7 @@
 	nsm_parent_field = 'parent_territory'
 
 	def validate(self):
+
 		for d in self.get('targets') or []:
 			if not flt(d.target_qty) and not flt(d.target_amount):
 				frappe.throw(_("Either target qty or target amount is mandatory"))
diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py
deleted file mode 100644
index bb8c131..0000000
--- a/erpnext/setup/setup_wizard/data/dashboard_charts.py
+++ /dev/null
@@ -1,117 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-import frappe
-import json
-
-def get_company_for_dashboards():
-	company = frappe.defaults.get_defaults().company
-	if company:
-		return company
-	else:
-		company_list = frappe.get_list("Company")
-		if company_list:
-			return company_list[0].name
-	return None
-
-def get_default_dashboards():
-	company = frappe.get_doc("Company", get_company_for_dashboards())
-	income_account = company.default_income_account or get_account("Income Account", company.name)
-	expense_account = company.default_expense_account or get_account("Expense Account", company.name)
-	bank_account = company.default_bank_account or get_account("Bank", company.name)
-
-	return {
-		"Dashboards": [
-			{
-				"doctype": "Dashboard",
-				"dashboard_name": "Accounts",
-				"charts": [
-					{ "chart": "Outgoing Bills (Sales Invoice)" },
-					{ "chart": "Incoming Bills (Purchase Invoice)" },
-					{ "chart": "Bank Balance" },
-					{ "chart": "Income" },
-					{ "chart": "Expenses" }
-				]
-			}
-		],
-		"Charts": [
-			{
-				"doctype": "Dashboard Chart",
-				"time_interval": "Quarterly",
-				"chart_name": "Income",
-				"timespan": "Last Year",
-				"color": None,
-				"filters_json": json.dumps({"company": company.name, "account": income_account}),
-				"source": "Account Balance Timeline",
-				"chart_type": "Custom",
-				"timeseries": 1,
-				"owner": "Administrator",
-				"type": "Line",
-				"width": "Half"
-			},
-			{
-				"doctype": "Dashboard Chart",
-				"time_interval": "Quarterly",
-				"chart_name": "Expenses",
-				"timespan": "Last Year",
-				"color": None,
-				"filters_json": json.dumps({"company": company.name, "account": expense_account}),
-				"source": "Account Balance Timeline",
-				"chart_type": "Custom",
-				"timeseries": 1,
-				"owner": "Administrator",
-				"type": "Line",
-				"width": "Half"
-			},
-			{
-				"doctype": "Dashboard Chart",
-				"time_interval": "Quarterly",
-				"chart_name": "Bank Balance",
-				"timespan": "Last Year",
-				"color": "#ffb868",
-				"filters_json": json.dumps({"company": company.name, "account": bank_account}),
-				"source": "Account Balance Timeline",
-				"chart_type": "Custom",
-				"timeseries": 1,
-				"owner": "Administrator",
-				"type": "Line",
-				"width": "Half"
-			},
-			{
-				"doctype": "Dashboard Chart",
-				"time_interval": "Monthly",
-				"chart_name": "Incoming Bills (Purchase Invoice)",
-				"timespan": "Last Year",
-				"color": "#a83333",
-				"value_based_on": "base_grand_total",
-				"filters_json": json.dumps({}),
-				"chart_type": "Sum",
-				"timeseries": 1,
-				"based_on": "posting_date",
-				"owner": "Administrator",
-				"document_type": "Purchase Invoice",
-				"type": "Bar",
-				"width": "Half"
-			},
-			{
-				"doctype": "Dashboard Chart",
-				"time_interval": "Monthly",
-				"chart_name": "Outgoing Bills (Sales Invoice)",
-				"timespan": "Last Year",
-				"color": "#7b933d",
-				"value_based_on": "base_grand_total",
-				"filters_json": json.dumps({}),
-				"chart_type": "Sum",
-				"timeseries": 1,
-				"based_on": "posting_date",
-				"owner": "Administrator",
-				"document_type": "Sales Invoice",
-				"type": "Bar",
-				"width": "Half"
-			}
-		]
-	}
-
-def get_account(account_type, company):
-	accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company})
-	if accounts:
-		return accounts[0].name
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index 3be6f44..8bb0a05 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -485,8 +485,6 @@
 				# bank account same as a CoA entry
 				pass
 
-	add_dashboards()
-
 	# Now, with fixtures out of the way, onto concrete stuff
 	records = [
 
@@ -504,27 +502,6 @@
 
 	make_records(records)
 
-def add_dashboards():
-	from erpnext.setup.setup_wizard.data.dashboard_charts import get_company_for_dashboards
-
-	if not get_company_for_dashboards():
-		return
-
-	from erpnext.setup.setup_wizard.data.dashboard_charts import get_default_dashboards
-	from frappe.modules.import_file import import_file_by_path
-
-	dashboard_data = get_default_dashboards()
-
-	# create account balance timeline before creating dashbaord charts
-	doctype = "dashboard_chart_source"
-	docname = "account_balance_timeline"
-	folder = os.path.dirname(frappe.get_module("erpnext.accounts").__file__)
-	doc_path = os.path.join(folder, doctype, docname, docname) + ".json"
-	import_file_by_path(doc_path, force=0, for_sync=True)
-
-	make_records(dashboard_data["Charts"])
-	make_records(dashboard_data["Dashboards"])
-
 
 def get_fy_details(fy_start_date, fy_end_date):
 	start_year = getdate(fy_start_date).year
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index 9b7249e..a091ac7 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -7,7 +7,7 @@
 from frappe import _
 from frappe.model.document import Document
 from frappe.model.naming import make_autoname, revert_series_if_last
-from frappe.utils import flt, cint
+from frappe.utils import flt, cint, get_link_to_form
 from frappe.utils.jinja import render_template
 from frappe.utils.data import add_days
 from six import string_types
@@ -124,7 +124,7 @@
 		if has_expiry_date and not self.expiry_date:
 			frappe.throw(msg=_("Please set {0} for Batched Item {1}, which is used to set {2} on Submit.") \
 				.format(frappe.bold("Shelf Life in Days"),
-					frappe.utils.get_link_to_form("Item", self.item),
+					get_link_to_form("Item", self.item),
 					frappe.bold("Batch Expiry Date")),
 				title=_("Expiry Date Mandatory"))
 
@@ -264,16 +264,20 @@
 def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None):
 	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 	cond = ''
-	if serial_no:
+	if serial_no and frappe.get_cached_value('Item', item_code, 'has_batch_no'):
+		serial_nos = get_serial_nos(serial_no)
 		batch = frappe.get_all("Serial No",
 			fields = ["distinct batch_no"],
 			filters= {
 				"item_code": item_code,
 				"warehouse": warehouse,
-				"name": ("in", get_serial_nos(serial_no))
+				"name": ("in", serial_nos)
 			}
 		)
 
+		if not batch:
+			validate_serial_no_with_batch(serial_nos, item_code)
+
 		if batch and len(batch) > 1:
 			return []
 
@@ -288,4 +292,15 @@
 			and (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL) {0}
 		group by batch_id
 		order by `tabBatch`.expiry_date ASC, `tabBatch`.creation ASC
-	""".format(cond), (item_code, warehouse), as_dict=True)
\ No newline at end of file
+	""".format(cond), (item_code, warehouse), as_dict=True)
+
+def validate_serial_no_with_batch(serial_nos, item_code):
+	if frappe.get_cached_value("Serial No", serial_nos[0], "item_code") != item_code:
+		frappe.throw(_("The serial no {0} does not belong to item {1}")
+			.format(get_link_to_form("Serial No", serial_nos[0]), get_link_to_form("Item", item_code)))
+
+	serial_no_link = ','.join([get_link_to_form("Serial No", sn) for sn in serial_nos])
+
+	message = "Serial Nos" if len(serial_nos) > 1 else "Serial No"
+	frappe.throw(_("There is no batch found against the {0}: {1}")
+		.format(message, serial_no_link))
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index c62b3ab..4cc50bb 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -572,6 +572,13 @@
 							frappe.throw(_("Barcode {0} is not a valid {1} code").format(
 								item_barcode.barcode, item_barcode.barcode_type), InvalidBarcode)
 
+					if item_barcode.barcode != item_barcode.name:
+						# if barcode is getting updated , the row name has to reset.
+						# Delete previous old row doc and re-enter row as if new to reset name in db.
+						item_barcode.set("__islocal", True)
+						item_barcode.name = None
+						frappe.delete_doc("Item Barcode", item_barcode.name)
+
 	def validate_warehouse_for_reorder(self):
 		'''Validate Reorder level table for duplicate and conditional mandatory'''
 		warehouse = []
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index 957c415..8e39eb5 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -69,3 +69,10 @@
 			self.reference = self.customer
 		if self.buying:
 			self.reference = self.supplier
+		
+		if self.selling and not self.buying:
+			# if only selling then remove supplier
+			self.supplier = None
+		if self.buying and not self.selling:
+			# if only buying then remove customer
+			self.customer = None
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index ddf4ec0..62c9eb1 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -181,7 +181,7 @@
 		stock_items = self.get_stock_items()
 		serialized_items = self.get_serialized_items()
 		for item in self.get("items"):
-			if item.qty and item.qty < 0:
+			if flt(item.qty) and flt(item.qty) < 0:
 				frappe.throw(_("Row {0}: The item {1}, quantity must be positive number")
 					.format(item.idx, frappe.bold(item.item_code)))
 
@@ -470,7 +470,7 @@
 			"qty": item.s_warehouse and -1*flt(item.transfer_qty) or flt(item.transfer_qty),
 			"serial_no": item.serial_no,
 			"voucher_type": self.doctype,
-			"voucher_no": item.name,
+			"voucher_no": self.name,
 			"company": self.company,
 			"allow_zero_valuation": item.allow_zero_valuation_rate,
 		})
diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py
index eb4867d..cd86be3 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.py
+++ b/erpnext/stock/doctype/warehouse/warehouse.py
@@ -177,7 +177,7 @@
 	return frappe.get_doc("Warehouse", args.docname).convert_to_group_or_ledger()
 
 def get_child_warehouses(warehouse):
-	lft, rgt = frappe.get_cached_value("Warehouse", warehouse, [lft, rgt])
+	lft, rgt = frappe.get_cached_value("Warehouse", warehouse, ["lft", "rgt"])
 
 	return frappe.db.sql_list("""select name from `tabWarehouse`
 		where lft >= %s and rgt <= %s""", (lft, rgt))
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index d50712a..11b6403 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -630,7 +630,7 @@
 		elif args.get("supplier"):
 			conditions += " and supplier=%(supplier)s"
 		else:
-			conditions += " and (customer is null or customer = '') and (supplier is null or supplier = '')"
+			conditions += "and (customer is null or customer = '') and (supplier is null or supplier = '')"
 
 	if args.get('transaction_date'):
 		conditions += """ and %(transaction_date)s between
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index b4cb8ca..e1b3730 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -548,7 +548,16 @@
 	if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \
 			and cint(erpnext.is_perpetual_inventory_enabled(company)):
 		frappe.local.message_log = []
-		frappe.throw(_("Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting / cancelling this entry.")
-			.format(item_code, voucher_type, voucher_no))
+		form_link = frappe.utils.get_link_to_form("Item", item_code)
+
+		message = _("Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.").format(form_link, voucher_type, voucher_no)
+		message += "<br><br>" + _(" Here are the options to proceed:")
+		solutions = "<li>" + _("If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table.").format(voucher_type) + "</li>"
+		solutions += "<li>" + _("If not, you can Cancel / Submit this entry ") + _("{0}").format(frappe.bold("after")) + _(" performing either one below:") + "</li>"
+		sub_solutions = "<ul><li>" + _("Create an incoming stock transaction for the Item.") + "</li>"
+		sub_solutions += "<li>" + _("Mention Valuation Rate in the Item master.") + "</li></ul>"
+		msg = message + solutions + sub_solutions + "</li>"
+
+		frappe.throw(msg=msg, title=_("Valuation Rate Missing"))
 
 	return valuation_rate