Merge pull request #22096 from marination/valuation-rate

fix: '>=' not supported between instances of 'str' and 'int'
diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml
new file mode 100644
index 0000000..d36b115
--- /dev/null
+++ b/.github/workflows/docker-release.yml
@@ -0,0 +1,14 @@
+name: Trigger Docker build on release
+on:
+  release:
+    types: [created]
+jobs:
+  curl:
+    runs-on: ubuntu-latest
+    container:
+      image: alpine:latest
+    steps:
+    - name: curl
+      run: |
+        apk add curl bash
+        curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.org/repo/frappe%2Ffrappe_docker/requests
diff --git a/CODEOWNERS b/CODEOWNERS
index 5e1113d..7cf65a7 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -3,17 +3,16 @@
 # These owners will be the default owners for everything in
 # the repo. Unless a later match takes precedence,
 
-*                       @nabinhait
-manufacturing/          @rohitwaghchaure
+manufacturing/          @rohitwaghchaure @marination
 accounts/               @deepeshgarg007 @nextchamp-saqib
-loan_management/        @deepeshgarg007
-pos*                    @nextchamp-saqib
-assets/                 @nextchamp-saqib
+loan_management/        @deepeshgarg007 @rohitwaghchaure
+pos*                    @nextchamp-saqib @rohitwaghchaure
+assets/                 @nextchamp-saqib @deepeshgarg007
 stock/                  @marination @rohitwaghchaure
-buying/                 @marination @rohitwaghchaure
-hr/                     @Anurag810
-projects/               @hrwX
-support/                @hrwX
-healthcare/             @ruchamahabal
-erpnext_integrations/   @Mangesh-Khairnar 	
+buying/                 @marination @deepeshgarg007
+hr/                     @Anurag810 @rohitwaghchaure
+projects/               @hrwX @nextchamp-saqib
+support/                @hrwX @marination
+healthcare/             @ruchamahabal @marination
+erpnext_integrations/   @Mangesh-Khairnar @nextchamp-saqib
 requirements.txt        @gavindsouza
diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js
index 9e2f6ee..f341f78 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.js
+++ b/erpnext/accounts/doctype/cost_center/cost_center.js
@@ -14,7 +14,18 @@
 					is_group: 1
 				}
 			}
-		})
+		});
+
+		frm.set_query("cost_center", "distributed_cost_center", function() {
+			return {
+				filters: {
+					company: frm.doc.company,
+					is_group: 0,
+					enable_distributed_cost_center: 0,
+					name: ['!=', frm.doc.name]
+				}
+			};
+		});
 	},
 	refresh: function(frm) {
 		if (!frm.is_new()) {
diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json
index 5013c92..c9bbbab 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.json
+++ b/erpnext/accounts/doctype/cost_center/cost_center.json
@@ -16,6 +16,9 @@
   "cb0",
   "is_group",
   "disabled",
+  "section_break_9",
+  "enable_distributed_cost_center",
+  "distributed_cost_center",
   "lft",
   "rgt",
   "old_parent"
@@ -119,6 +122,24 @@
    "fieldname": "disabled",
    "fieldtype": "Check",
    "label": "Disabled"
+  },
+  {
+   "default": "0",
+   "fieldname": "enable_distributed_cost_center",
+   "fieldtype": "Check",
+   "label": "Enable Distributed Cost Center"
+  },
+  {
+   "depends_on": "eval:doc.is_group==0",
+   "fieldname": "section_break_9",
+   "fieldtype": "Section Break"
+  },
+  {
+   "depends_on": "enable_distributed_cost_center",
+   "fieldname": "distributed_cost_center",
+   "fieldtype": "Table",
+   "label": "Distributed Cost Center",
+   "options": "Distributed Cost Center"
   }
  ],
  "icon": "fa fa-money",
diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py
index 0294e78..12094d4 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.py
+++ b/erpnext/accounts/doctype/cost_center/cost_center.py
@@ -19,6 +19,24 @@
 	def validate(self):
 		self.validate_mandatory()
 		self.validate_parent_cost_center()
+		self.validate_distributed_cost_center()
+
+	def validate_distributed_cost_center(self):
+		if cint(self.enable_distributed_cost_center):
+			if not self.distributed_cost_center:
+				frappe.throw(_("Please enter distributed cost center"))
+			if sum(x.percentage_allocation for x in self.distributed_cost_center) != 100:
+				frappe.throw(_("Total percentage allocation for distributed cost center should be equal to 100"))
+			if not self.get('__islocal'):
+				if not cint(frappe.get_cached_value("Cost Center", {"name": self.name}, "enable_distributed_cost_center")) \
+					and self.check_if_part_of_distributed_cost_center():
+					frappe.throw(_("Cannot enable Distributed Cost Center for a Cost Center already allocated in another Distributed Cost Center"))
+				if next((True for x in self.distributed_cost_center if x.cost_center == x.parent), False):
+					frappe.throw(_("Parent Cost Center cannot be added in Distributed Cost Center"))
+			if check_if_distributed_cost_center_enabled(list(x.cost_center for x in self.distributed_cost_center)):
+				frappe.throw(_("A Distributed Cost Center cannot be added in the Distributed Cost Center allocation table."))
+		else:
+			self.distributed_cost_center = []
 
 	def validate_mandatory(self):
 		if self.cost_center_name != self.company and not self.parent_cost_center:
@@ -43,12 +61,15 @@
 			return 1
 
 	def convert_ledger_to_group(self):
+		if cint(self.enable_distributed_cost_center):
+			frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group"))
+		if self.check_if_part_of_distributed_cost_center():
+			frappe.throw(_("Cost Center Already Allocated in a Distributed Cost Center cannot be converted to group"))
 		if self.check_gle_exists():
 			frappe.throw(_("Cost Center with existing transactions can not be converted to group"))
-		else:
-			self.is_group = 1
-			self.save()
-			return 1
+		self.is_group = 1
+		self.save()
+		return 1
 
 	def check_gle_exists(self):
 		return frappe.db.get_value("GL Entry", {"cost_center": self.name})
@@ -57,6 +78,9 @@
 		return frappe.db.sql("select name from `tabCost Center` where \
 			parent_cost_center = %s and docstatus != 2", self.name)
 
+	def check_if_part_of_distributed_cost_center(self):
+		return frappe.db.get_value("Distributed Cost Center", {"cost_center": self.name})
+
 	def before_rename(self, olddn, newdn, merge=False):
 		# Add company abbr if not provided
 		from erpnext.setup.doctype.company.company import get_name_with_abbr
@@ -100,3 +124,7 @@
 	if account_number and not new_account[0].isdigit():
 		new_account = account_number + " - " + new_account
 	return new_account
+
+def check_if_distributed_cost_center_enabled(cost_center_list):
+	value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1)
+	return next((True for x in value_list if x[0]), False)
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py
index 8f23d90..b5fc7e3 100644
--- a/erpnext/accounts/doctype/cost_center/test_cost_center.py
+++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py
@@ -22,6 +22,33 @@
 
 		self.assertRaises(frappe.ValidationError, cost_center.save)
 
+	def test_validate_distributed_cost_center(self):
+
+		if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center - _TC'}):
+			frappe.get_doc(test_records[0]).insert()
+
+		if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}):
+			frappe.get_doc(test_records[1]).insert()
+
+		invalid_distributed_cost_center = frappe.get_doc({
+			"company": "_Test Company",
+			"cost_center_name": "_Test Distributed Cost Center",
+			"doctype": "Cost Center",
+			"is_group": 0,
+			"parent_cost_center": "_Test Company - _TC",
+			"enable_distributed_cost_center": 1,
+			"distributed_cost_center": [{
+				"cost_center": "_Test Cost Center - _TC",
+				"percentage_allocation": 40
+				}, {
+				"cost_center": "_Test Cost Center 2 - _TC",
+				"percentage_allocation": 50
+				}
+			]
+		})
+
+		self.assertRaises(frappe.ValidationError, invalid_distributed_cost_center.save)
+
 def create_cost_center(**args):
 	args = frappe._dict(args)
 	if args.cost_center_name:
diff --git a/erpnext/accounts/doctype/distributed_cost_center/__init__.py b/erpnext/accounts/doctype/distributed_cost_center/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/distributed_cost_center/__init__.py
diff --git a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json
new file mode 100644
index 0000000..45b0e2d
--- /dev/null
+++ b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json
@@ -0,0 +1,40 @@
+{
+ "actions": [],
+ "creation": "2020-03-19 12:34:01.500390",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "cost_center",
+  "percentage_allocation"
+ ],
+ "fields": [
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Cost Center",
+   "options": "Cost Center",
+   "reqd": 1
+  },
+  {
+   "fieldname": "percentage_allocation",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Percentage Allocation",
+   "reqd": 1
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-03-19 12:54:43.674655",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Distributed Cost Center",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py
new file mode 100644
index 0000000..48c589f
--- /dev/null
+++ b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class DistributedCostCenter(Document):
+	pass
diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
index b2638c7..d32a348 100644
--- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
+++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
@@ -45,7 +45,9 @@
 		shipping_amount = 0.0
 		by_value = False
 
-		self.validate_countries(doc)
+		if doc.get_shipping_address():
+			# validate country only if there is address
+			self.validate_countries(doc)
 
 		if self.calculate_based_on == 'Net Total':
 			value = doc.base_net_total
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 05dc282..9c9ada8 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -29,37 +29,60 @@
 	for dimension in dimensions:
 		dimension_items = cam_map.get(dimension)
 		if dimension_items:
-			for account, monthwise_data in iteritems(dimension_items):
-				row = [dimension, account]
-				totals = [0, 0, 0]
-				for year in get_fiscal_years(filters):
-					last_total = 0
-					for relevant_months in period_month_ranges:
-						period_data = [0, 0, 0]
-						for month in relevant_months:
-							if monthwise_data.get(year[0]):
-								month_data = monthwise_data.get(year[0]).get(month, {})
-								for i, fieldname in enumerate(["target", "actual", "variance"]):
-									value = flt(month_data.get(fieldname))
-									period_data[i] += value
-									totals[i] += value
-
-						period_data[0] += last_total
-
-						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":
-					row += totals
-				data.append(row)
+			data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, 0)
+		else:
+			DCC_allocation = frappe.db.sql('''SELECT parent, sum(percentage_allocation) as percentage_allocation
+				FROM `tabDistributed Cost Center`
+				WHERE cost_center IN %(dimension)s
+				AND parent NOT IN %(dimension)s
+				GROUP BY parent''',{'dimension':[dimension]})
+			if DCC_allocation:
+				filters['budget_against_filter'] = [DCC_allocation[0][0]]
+				cam_map = get_dimension_account_month_map(filters)
+				dimension_items = cam_map.get(DCC_allocation[0][0])
+				if dimension_items:
+					data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation[0][1])
 
 	chart = get_chart_data(filters, columns, data)
 
 	return columns, data, None, chart
 
+def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation):
+
+	for account, monthwise_data in iteritems(dimension_items):
+		row = [dimension, account]
+		totals = [0, 0, 0]
+		for year in get_fiscal_years(filters):
+			last_total = 0
+			for relevant_months in period_month_ranges:
+				period_data = [0, 0, 0]
+				for month in relevant_months:
+					if monthwise_data.get(year[0]):
+						month_data = monthwise_data.get(year[0]).get(month, {})
+						for i, fieldname in enumerate(["target", "actual", "variance"]):
+							value = flt(month_data.get(fieldname))
+							period_data[i] += value
+							totals[i] += value
+
+				period_data[0] += last_total
+
+				if DCC_allocation:
+					period_data[0] = period_data[0]*(DCC_allocation/100)
+					period_data[1] = period_data[1]*(DCC_allocation/100)
+
+				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" :
+			row += totals
+		data.append(row)
+		
+	return data
+
+
 def get_columns(filters):
 	columns = [
 		{
@@ -366,7 +389,7 @@
 			budget_values[i] += values[index]
 			actual_values[i] += values[index+1]
 			index += 3
-
+			
 	return {
 		'data': {
 			'labels': labels,
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 4a35a66..0339e49 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -387,11 +387,41 @@
 					key: value
 				})
 
+		distributed_cost_center_query = ""
+		if filters and filters.get('cost_center'):
+			distributed_cost_center_query = """
+			UNION ALL
+			SELECT posting_date,
+				account,
+				debit*(DCC_allocation.percentage_allocation/100) as debit,
+				credit*(DCC_allocation.percentage_allocation/100) as credit,
+				is_opening,
+				fiscal_year,
+				debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
+				credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency,
+				account_currency
+			FROM `tabGL Entry`,
+			(
+				SELECT parent, sum(percentage_allocation) as percentage_allocation
+				FROM `tabDistributed Cost Center`
+				WHERE cost_center IN %(cost_center)s
+				AND parent NOT IN %(cost_center)s
+				GROUP BY parent
+			) as DCC_allocation
+			WHERE company=%(company)s
+			{additional_conditions}
+			AND posting_date <= %(to_date)s
+			AND cost_center = DCC_allocation.parent
+			""".format(additional_conditions=additional_conditions.replace("and cost_center in %(cost_center)s ", ''))
+
 		gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry`
 			where company=%(company)s
 			{additional_conditions}
 			and posting_date <= %(to_date)s
-			order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True) #nosec
+			{distributed_cost_center_query}
+			order by account, posting_date""".format(
+				additional_conditions=additional_conditions,
+				distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec
 
 		if filters and filters.get('presentation_currency'):
 			convert_to_presentation_currency(gl_entries, get_currency(filters))
@@ -489,4 +519,4 @@
 				"width": 150
 			})
 
-	return columns
+	return columns
\ No newline at end of file
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index f83a259..fcd36e4 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -128,18 +128,53 @@
 		filters['company_fb'] = frappe.db.get_value("Company",
 			filters.get("company"), 'default_finance_book')
 
+	distributed_cost_center_query = ""
+	if filters and filters.get('cost_center'):
+		select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
+		credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency """
+		
+		distributed_cost_center_query = """
+		UNION ALL
+		SELECT name as gl_entry,
+			posting_date,
+			account,
+			party_type,
+			party,
+			voucher_type,
+			voucher_no,
+			cost_center, project,
+			against_voucher_type,
+			against_voucher,
+			account_currency,
+			remarks, against, 
+			is_opening, `tabGL Entry`.creation {select_fields_with_percentage}
+		FROM `tabGL Entry`,
+		(
+			SELECT parent, sum(percentage_allocation) as percentage_allocation
+			FROM `tabDistributed Cost Center`
+			WHERE cost_center IN %(cost_center)s
+			AND parent NOT IN %(cost_center)s
+			GROUP BY parent
+		) as DCC_allocation
+		WHERE company=%(company)s
+		{conditions}
+		AND posting_date <= %(to_date)s
+		AND cost_center = DCC_allocation.parent
+		""".format(select_fields_with_percentage=select_fields_with_percentage, conditions=get_conditions(filters).replace("and cost_center in %(cost_center)s ", ''))
+
 	gl_entries = frappe.db.sql(
 		"""
 		select
 			name as gl_entry, posting_date, account, party_type, party,
 			voucher_type, voucher_no, cost_center, project,
 			against_voucher_type, against_voucher, account_currency,
-			remarks, against, is_opening {select_fields}
+			remarks, against, is_opening, creation {select_fields}
 		from `tabGL Entry`
 		where company=%(company)s {conditions}
+		{distributed_cost_center_query}
 		{order_by_statement}
 		""".format(
-			select_fields=select_fields, conditions=get_conditions(filters),
+			select_fields=select_fields, conditions=get_conditions(filters), distributed_cost_center_query=distributed_cost_center_query,
 			order_by_statement=order_by_statement
 		),
 		filters, as_dict=1)
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
index 6e9b31f..60e675f 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
@@ -105,6 +105,7 @@
 
 def prepare_data(accounts, filters, total_row, parent_children_map, based_on):
 	data = []
+	new_accounts = accounts
 	company_currency = frappe.get_cached_value('Company',  filters.get("company"),  "default_currency")
 
 	for d in accounts:
@@ -118,6 +119,19 @@
 			"currency": company_currency,
 			"based_on": based_on
 		}
+		if based_on == 'cost_center':
+			cost_center_doc = frappe.get_doc("Cost Center",d.name)
+			if not cost_center_doc.enable_distributed_cost_center:
+				DCC_allocation = frappe.db.sql("""SELECT parent, sum(percentage_allocation) as percentage_allocation
+					FROM `tabDistributed Cost Center`
+					WHERE cost_center IN %(cost_center)s
+					AND parent NOT IN %(cost_center)s
+					GROUP BY parent""",{'cost_center': [d.name]})
+				if DCC_allocation:
+					for account in new_accounts:
+						if account['name'] == DCC_allocation[0][0]:
+							for value in value_fields:
+								d[value] += account[value]*(DCC_allocation[0][1]/100)
 
 		for key in value_fields:
 			row[key] = flt(d.get(key, 0.0), 3)
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
index 4ac0f65..a9fb237 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
@@ -111,7 +111,7 @@
 	# {"purchase_invoice": list of dict of all gle created for this invoice}
 	gle_map = {}
 	gle = frappe.db.get_all('GL Entry',\
-		{"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]]},
+		{"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]], 'is_cancelled': 0},
 		["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"])
 
 	for d in gle:
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index 29f8dd5..50b17ab 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -70,7 +70,7 @@
 
 		else:
 			attributes_list = attribute_values.get(attribute.lower(), [])
-			validate_item_attribute_value(attributes_list, attribute, value, item.name)
+			validate_item_attribute_value(attributes_list, attribute, value, item.name, from_variant=True)
 
 def validate_is_incremental(numeric_attribute, attribute, value, item):
 	from_range = numeric_attribute.from_range
@@ -93,13 +93,20 @@
 			.format(attribute, from_range, to_range, increment, item),
 			InvalidItemAttributeValueError, title=_('Invalid Attribute'))
 
-def validate_item_attribute_value(attributes_list, attribute, attribute_value, item):
+def validate_item_attribute_value(attributes_list, attribute, attribute_value, item, from_variant=True):
 	allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value')
 	if allow_rename_attribute_value:
 		pass
 	elif attribute_value not in attributes_list:
-		frappe.throw(_("The value {0} is already assigned to an exisiting Item {2}.").format(
-			attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Rename Not Allowed'))
+		if from_variant:
+			frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format(
+				frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value"))
+		else:
+			msg = _("The value {0} is already assigned to an exisiting Item {1}.").format(
+				frappe.bold(attribute_value), frappe.bold(item))
+			msg += "<br>" + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value"))
+
+			frappe.throw(msg, InvalidItemAttributeValueError, title=_('Edit Not Allowed'))
 
 def get_attribute_values(item):
 	if not frappe.flags.attribute_values:
diff --git a/erpnext/education/doctype/program_course/program_course.json b/erpnext/education/doctype/program_course/program_course.json
index a24e88a..940358e 100644
--- a/erpnext/education/doctype/program_course/program_course.json
+++ b/erpnext/education/doctype/program_course/program_course.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2015-09-07 14:37:01.886859",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -16,26 +17,33 @@
    "in_list_view": 1,
    "label": "Course",
    "options": "Course",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
-   {
+  {
+   "fetch_from": "course.course_name",
    "fieldname": "course_name",
    "fieldtype": "Data",
    "in_list_view": 1,
    "label": "Course Name",
-   "fetch_from": "course.course_name",
-   "read_only":1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
    "fieldname": "required",
    "fieldtype": "Check",
    "in_list_view": 1,
-   "label": "Mandatory"
+   "label": "Mandatory",
+   "show_days": 1,
+   "show_seconds": 1
   }
  ],
  "istable": 1,
- "modified": "2019-06-12 12:42:12.845972",
+ "links": [],
+ "modified": "2020-06-09 18:56:10.213241",
  "modified_by": "Administrator",
  "module": "Education",
  "name": "Program Course",
@@ -45,4 +53,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py
index 7046038..d59f909 100644
--- a/erpnext/erpnext_integrations/connectors/shopify_connection.py
+++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py
@@ -241,14 +241,17 @@
 	return taxes
 
 def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings):
+	"""Shipping lines represents the shipping details,
+		each such shipping detail consists of a list of tax_lines"""
 	for shipping_charge in shipping_lines:
-		taxes.append({
-			"charge_type": _("Actual"),
-			"account_head": get_tax_account_head(shipping_charge),
-			"description": shipping_charge["title"],
-			"tax_amount": shipping_charge["price"],
-			"cost_center": shopify_settings.cost_center
-		})
+		for tax in shipping_charge.get("tax_lines"):
+			taxes.append({
+				"charge_type": _("Actual"),
+				"account_head": get_tax_account_head(tax),
+				"description": tax["title"],
+				"tax_amount": tax["price"],
+				"cost_center": shopify_settings.cost_center
+			})
 
 	return taxes
 
diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json
index 7ac000b..1c24444 100644
--- a/erpnext/hr/desk_page/hr/hr.json
+++ b/erpnext/hr/desk_page/hr/hr.json
@@ -18,7 +18,7 @@
   {
    "hidden": 0,
    "label": "Leaves",
-   "links": "[\n    {\n        \"dependencies\": [\n            \"Employee\"\n        ],\n        \"label\": \"Leave Application\",\n        \"name\": \"Leave Application\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Employee\"\n        ],\n        \"label\": \"Leave Allocation\",\n        \"name\": \"Leave Allocation\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Leave Type\"\n        ],\n        \"label\": \"Leave Policy\",\n        \"name\": \"Leave Policy\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Employee\"\n        ],\n        \"label\": \"Leave Period\",\n        \"name\": \"Leave Period\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Leave Type\",\n        \"name\": \"Leave Type\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Holiday List\",\n        \"name\": \"Holiday List\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Employee\"\n        ],\n        \"label\": \"Compensatory Leave Request\",\n        \"name\": \"Compensatory Leave Request\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Employee\"\n        ],\n        \"label\": \"Leave Encashment\",\n        \"name\": \"Leave Encashment\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Leave Block List\",\n        \"name\": \"Leave Block List\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Leave Application\"\n        ],\n        \"doctype\": \"Leave Application\",\n        \"is_query_report\": true,\n        \"label\": \"Employee Leave Balance\",\n        \"name\": \"Employee Leave Balance\",\n        \"type\": \"report\"\n    }\n]"
+   "links": "[\n    {\n        \"dependencies\": [\n            \"Employee\"\n        ],\n        \"label\": \"Leave Application\",\n        \"name\": \"Leave Application\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Employee\"\n        ],\n        \"label\": \"Leave Allocation\",\n        \"name\": \"Leave Allocation\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Leave Type\"\n        ],\n        \"label\": \"Leave Policy\",\n        \"name\": \"Leave Policy\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Leave Period\",\n        \"name\": \"Leave Period\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Leave Type\",\n        \"name\": \"Leave Type\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Holiday List\",\n        \"name\": \"Holiday List\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Employee\"\n        ],\n        \"label\": \"Compensatory Leave Request\",\n        \"name\": \"Compensatory Leave Request\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Employee\"\n        ],\n        \"label\": \"Leave Encashment\",\n        \"name\": \"Leave Encashment\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Leave Block List\",\n        \"name\": \"Leave Block List\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Leave Application\"\n        ],\n        \"doctype\": \"Leave Application\",\n        \"is_query_report\": true,\n        \"label\": \"Employee Leave Balance\",\n        \"name\": \"Employee Leave Balance\",\n        \"type\": \"report\"\n    }\n]"
   },
   {
    "hidden": 0,
@@ -93,7 +93,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "HR",
- "modified": "2020-05-28 13:36:07.710600",
+ "modified": "2020-06-10 12:41:41.695669",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "HR",
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index fb23103..6bb9af9 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -243,7 +243,6 @@
 	},
 
 	update_employee_advance_claimed_amount: function(frm) {
-		console.log("update_employee_advance_claimed_amount")
 		let amount_to_be_allocated = frm.doc.grand_total;
 		$.each(frm.doc.advances || [], function(i, advance){
 			if (amount_to_be_allocated >= advance.unclaimed_amount){
@@ -295,6 +294,16 @@
 		frm.events.get_advances(frm);
 	},
 
+	cost_center: function(frm) {
+		frm.events.set_child_cost_center(frm);
+	},
+	set_child_cost_center: function(frm){
+		(frm.doc.expenses || []).forEach(function(d) {
+			if (!d.cost_center){
+				d.cost_center = frm.doc.cost_center;
+			}
+		});
+	},
 	get_taxes: function(frm) {
 		if(frm.doc.taxes) {
 			frappe.call({
@@ -338,8 +347,7 @@
 
 frappe.ui.form.on("Expense Claim Detail", {
 	expenses_add: function(frm, cdt, cdn) {
-		var row = frappe.get_doc(cdt, cdn);
-		frm.script_manager.copy_from_first_row("expenses", row, ["cost_center"]);
+		frm.events.set_child_cost_center(frm);
 	},
 	amount: function(frm, cdt, cdn) {
 		var child = locals[cdt][cdn];
diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js
index 473aae6..15ce468 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.js
+++ b/erpnext/hr/doctype/leave_application/leave_application.js
@@ -38,6 +38,9 @@
 	},
 
 	validate: function(frm) {
+		if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){
+			frm.doc.half_day_date = frm.doc.from_date;
+		}
 		frm.toggle_reqd("half_day_date", frm.doc.half_day == 1);
 	},
 
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index f2968bc..c2b4cdf 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -33,6 +33,7 @@
 		self.validate_block_days()
 		self.validate_salary_processed_days()
 		self.validate_attendance()
+		self.set_half_day_date()
 		if frappe.db.get_value("Leave Type", self.leave_type, 'is_optional_leave'):
 			self.validate_optional_leave()
 		self.validate_applicable_after()
@@ -131,8 +132,6 @@
 			for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
 				date = dt.strftime("%Y-%m-%d")
 				status = "Half Day" if getdate(date) == getdate(self.half_day_date) else "On Leave"
-				print("-------->>>", status)
-				# frappe.throw("Hello")
 
 				attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee,
 					attendance_date = date, docstatus = ('!=', 2)))
@@ -292,6 +291,10 @@
 				frappe.throw(_("{0} is not in Optional Holiday List").format(formatdate(day)), NotAnOptionalHoliday)
 			day = add_days(day, 1)
 
+	def set_half_day_date(self):
+		if self.from_date == self.to_date and self.half_day == 1:
+			self.half_day_date = self.from_date
+
 	def notify_employee(self):
 		employee = frappe.get_doc("Employee", self.employee)
 		if not employee.user_id:
@@ -442,6 +445,7 @@
 		total_allocated_leaves = frappe.db.get_value('Leave Allocation', {
 			'from_date': ('<=', date),
 			'to_date': ('>=', date),
+			'employee': employee,
 			'leave_type': allocation.leave_type,
 		}, 'SUM(total_leaves_allocated)') or 0
 
diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py
index cd12510..8d95924 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -73,11 +73,11 @@
 	def assign_task_to_users(self, task, users):
 		for user in users:
 			args = {
-				'assign_to' 	:	user,
-				'doctype'		:	task.doctype,
-				'name'			:	task.name,
-				'description'	:	task.description or task.subject,
-				'notify':	self.notify_users_by_email
+				'assign_to': [user],
+				'doctype': task.doctype,
+				'name': task.name,
+				'description': task.description or task.subject,
+				'notify': self.notify_users_by_email
 			}
 			assign_to.add(args)
 
diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json
index d79860a..48193b0 100644
--- a/erpnext/loan_management/desk_page/loan/loan.json
+++ b/erpnext/loan_management/desk_page/loan/loan.json
@@ -23,7 +23,7 @@
   {
    "hidden": 0,
    "label": "Reports",
-   "links": "[\n    {\n        \"dependencies\": [\n            \"Loan Repayment\"\n        ],\n        \"doctype\": \"Loan Repayment\",\n        \"incomplete_dependencies\": [\n            \"Loan Repayment\"\n        ],\n        \"is_query_report\": true,\n        \"label\": \"Loan Repayment and Closure\",\n        \"name\": \"Loan Repayment and Closure\",\n        \"route\": \"#query-report/Loan Repayment and Closure\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Loan Security Pledge\"\n        ],\n        \"doctype\": \"Loan Security Pledge\",\n        \"incomplete_dependencies\": [\n            \"Loan Security Pledge\"\n        ],\n        \"is_query_report\": true,\n        \"label\": \"Loan Security Status\",\n        \"name\": \"Loan Security Status\",\n        \"route\": \"#query-report/Loan Security Status\",\n        \"type\": \"report\"\n    }\n]"
+   "links": "[\n    {\n        \"doctype\": \"Loan Repayment\",\n        \"is_query_report\": true,\n        \"label\": \"Loan Repayment and Closure\",\n        \"name\": \"Loan Repayment and Closure\",\n        \"route\": \"#query-report/Loan Repayment and Closure\",\n        \"type\": \"report\"\n    },\n    {\n        \"doctype\": \"Loan Security Pledge\",\n        \"is_query_report\": true,\n        \"label\": \"Loan Security Status\",\n        \"name\": \"Loan Security Status\",\n        \"route\": \"#query-report/Loan Security Status\",\n        \"type\": \"report\"\n    }\n]"
   }
  ],
  "category": "Modules",
@@ -34,11 +34,10 @@
  "docstatus": 0,
  "doctype": "Desk Page",
  "extends_another_page": 0,
- "hide_custom": 0,
  "idx": 0,
  "is_standard": 1,
  "label": "Loan",
- "modified": "2020-05-28 13:37:42.017709",
+ "modified": "2020-06-07 19:42:14.947902",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan",
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index c9e36a8..d44088b 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -27,6 +27,7 @@
 
 	def on_cancel(self):
 		self.make_gl_entries(cancel=1)
+		self.ignore_linked_doctypes = ['GL Entry']
 
 	def set_missing_values(self):
 		if not self.disbursement_date:
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
index 094b9c6..e6ceb55 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
@@ -31,6 +31,7 @@
 			self.update_is_accrued()
 
 		self.make_gl_entries(cancel=1)
+		self.ignore_linked_doctypes = ['GL Entry']
 
 	def update_is_accrued(self):
 		frappe.db.set_value('Repayment Schedule', self.repayment_schedule_name, 'is_accrued', 0)
@@ -176,21 +177,23 @@
 	return term_loans
 
 def make_loan_interest_accrual_entry(args):
-		loan_interest_accrual = frappe.new_doc("Loan Interest Accrual")
-		loan_interest_accrual.loan = args.loan
-		loan_interest_accrual.applicant_type = args.applicant_type
-		loan_interest_accrual.applicant = args.applicant
-		loan_interest_accrual.interest_income_account = args.interest_income_account
-		loan_interest_accrual.loan_account = args.loan_account
-		loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, 2)
-		loan_interest_accrual.interest_amount = flt(args.interest_amount, 2)
-		loan_interest_accrual.posting_date = args.posting_date or nowdate()
-		loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest
-		loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name
-		loan_interest_accrual.payable_principal_amount = args.payable_principal
+	precision = cint(frappe.db.get_default("currency_precision")) or 2
 
-		loan_interest_accrual.save()
-		loan_interest_accrual.submit()
+	loan_interest_accrual = frappe.new_doc("Loan Interest Accrual")
+	loan_interest_accrual.loan = args.loan
+	loan_interest_accrual.applicant_type = args.applicant_type
+	loan_interest_accrual.applicant = args.applicant
+	loan_interest_accrual.interest_income_account = args.interest_income_account
+	loan_interest_accrual.loan_account = args.loan_account
+	loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, precision)
+	loan_interest_accrual.interest_amount = flt(args.interest_amount, precision)
+	loan_interest_accrual.posting_date = args.posting_date or nowdate()
+	loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest
+	loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name
+	loan_interest_accrual.payable_principal_amount = args.payable_principal
+
+	loan_interest_accrual.save()
+	loan_interest_accrual.submit()
 
 
 def get_no_of_days_for_interest_accural(loan, posting_date):
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 2ab668a..c28994e 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -6,7 +6,7 @@
 import frappe, erpnext
 import json
 from frappe import _
-from frappe.utils import flt, getdate
+from frappe.utils import flt, getdate, cint
 from six import iteritems
 from frappe.model.document import Document
 from frappe.utils import date_diff, add_days, getdate, add_months, get_first_day, get_datetime
@@ -29,8 +29,11 @@
 	def on_cancel(self):
 		self.mark_as_unpaid()
 		self.make_gl_entries(cancel=1)
+		self.ignore_linked_doctypes = ['GL Entry']
 
 	def set_missing_values(self, amounts):
+		precision = cint(frappe.db.get_default("currency_precision")) or 2
+
 		if not self.posting_date:
 			self.posting_date = get_datetime()
 
@@ -38,24 +41,26 @@
 			self.cost_center = erpnext.get_default_cost_center(self.company)
 
 		if not self.interest_payable:
-			self.interest_payable = flt(amounts['interest_amount'], 2)
+			self.interest_payable = flt(amounts['interest_amount'], precision)
 
 		if not self.penalty_amount:
-			self.penalty_amount = flt(amounts['penalty_amount'], 2)
+			self.penalty_amount = flt(amounts['penalty_amount'], precision)
 
 		if not self.pending_principal_amount:
-			self.pending_principal_amount = flt(amounts['pending_principal_amount'], 2)
+			self.pending_principal_amount = flt(amounts['pending_principal_amount'], precision)
 
 		if not self.payable_principal_amount and self.is_term_loan:
-			self.payable_principal_amount = flt(amounts['payable_principal_amount'], 2)
+			self.payable_principal_amount = flt(amounts['payable_principal_amount'], precision)
 
 		if not self.payable_amount:
-			self.payable_amount = flt(amounts['payable_amount'], 2)
+			self.payable_amount = flt(amounts['payable_amount'], precision)
 
 		if amounts.get('due_date'):
 			self.due_date = amounts.get('due_date')
 
 	def validate_amount(self):
+		precision = cint(frappe.db.get_default("currency_precision")) or 2
+
 		if not self.amount_paid:
 			frappe.throw(_("Amount paid cannot be zero"))
 
@@ -63,11 +68,13 @@
 			msg = _("Paid amount cannot be less than {0}").format(self.penalty_amount)
 			frappe.throw(msg)
 
-		if self.payment_type == "Loan Closure" and flt(self.amount_paid, 2) < flt(self.payable_amount, 2):
+		if self.payment_type == "Loan Closure" and flt(self.amount_paid, precision) < flt(self.payable_amount, precision):
 			msg = _("Amount of {0} is required for Loan closure").format(self.payable_amount)
 			frappe.throw(msg)
 
 	def update_paid_amount(self):
+		precision = cint(frappe.db.get_default("currency_precision")) or 2
+
 		loan = frappe.get_doc("Loan", self.against_loan)
 
 		for payment in self.repayment_details:
@@ -75,9 +82,9 @@
 				SET paid_principal_amount = `paid_principal_amount` + %s,
 					paid_interest_amount = `paid_interest_amount` + %s
 				WHERE name = %s""",
-				(flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual))
+				(flt(payment.paid_principal_amount, precision), flt(payment.paid_interest_amount, precision), payment.loan_interest_accrual))
 
-		if flt(loan.total_principal_paid + self.principal_amount_paid, 2) >= flt(loan.total_payment, 2):
+		if flt(loan.total_principal_paid + self.principal_amount_paid, precision) >= flt(loan.total_payment, precision):
 			if loan.is_secured_loan:
 				frappe.db.set_value("Loan", self.against_loan, "status", "Loan Closure Requested")
 			else:
@@ -253,6 +260,7 @@
 # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
 
 def get_amounts(amounts, against_loan, posting_date, payment_type):
+	precision = cint(frappe.db.get_default("currency_precision")) or 2
 
 	against_loan_doc = frappe.get_doc("Loan", against_loan)
 	loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type)
@@ -282,8 +290,8 @@
 		payable_principal_amount += entry.payable_principal_amount
 
 		pending_accrual_entries.setdefault(entry.name, {
-			'interest_amount': flt(entry.interest_amount),
-			'payable_principal_amount': flt(entry.payable_principal_amount)
+			'interest_amount': flt(entry.interest_amount, precision),
+			'payable_principal_amount': flt(entry.payable_principal_amount, precision)
 		})
 
 		if not final_due_date:
@@ -301,11 +309,11 @@
 		per_day_interest = (payable_principal_amount * (loan_type_details.rate_of_interest / 100))/365
 		total_pending_interest += (pending_days * per_day_interest)
 
-	amounts["pending_principal_amount"] = pending_principal_amount
-	amounts["payable_principal_amount"] = payable_principal_amount
-	amounts["interest_amount"] = total_pending_interest
-	amounts["penalty_amount"] = penalty_amount
-	amounts["payable_amount"] = payable_principal_amount + total_pending_interest + penalty_amount
+	amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
+	amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
+	amounts["interest_amount"] = flt(total_pending_interest, precision)
+	amounts["penalty_amount"] = flt(penalty_amount, precision)
+	amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision)
 	amounts["pending_accrual_entries"] = pending_accrual_entries
 
 	if final_due_date:
diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.json b/erpnext/loan_management/doctype/loan_type/loan_type.json
index 51c5cb9..1dd3710 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type.json
+++ b/erpnext/loan_management/doctype/loan_type/loan_type.json
@@ -41,6 +41,7 @@
    "options": "Company:company:default_currency"
   },
   {
+   "default": "0",
    "fieldname": "rate_of_interest",
    "fieldtype": "Percent",
    "label": "Rate of Interest (%) Yearly",
@@ -143,7 +144,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-04-15 00:24:43.259963",
+ "modified": "2020-06-07 18:55:59.346292",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Type",
diff --git a/erpnext/loan_management/report/loan_security_status/loan_security_status.py b/erpnext/loan_management/report/loan_security_status/loan_security_status.py
index ea6a2ee6..1951855 100644
--- a/erpnext/loan_management/report/loan_security_status/loan_security_status.py
+++ b/erpnext/loan_management/report/loan_security_status/loan_security_status.py
@@ -76,7 +76,8 @@
 			"fieldtype": "Link",
 			"fieldname": "currency",
 			"options": "Currency",
-			"width": 50
+			"width": 50,
+			"hidden": 1
 		}
 	]
 
@@ -84,17 +85,13 @@
 
 def get_data(filters):
 
-	loan_security_price_map = frappe._dict(frappe.get_all("Loan Security",
-		fields=["name", "loan_security_price"], as_list=1
-	))
-
 	data = []
 	conditions = get_conditions(filters)
 
 	loan_security_pledges = frappe.db.sql("""
 		SELECT
 			p.name, p.applicant, p.loan, p.status, p.pledge_time,
-			c.loan_security, c.qty
+			c.loan_security, c.qty, c.loan_security_price, c.amount
 		FROM
 			`tabLoan Security Pledge` p, `tabPledge` c
 		WHERE
@@ -115,8 +112,8 @@
 		row["pledge_time"] = pledge.pledge_time
 		row["loan_security"] = pledge.loan_security
 		row["qty"] = pledge.qty
-		row["loan_security_price"] = loan_security_price_map.get(pledge.loan_security)
-		row["loan_security_value"] = row["loan_security_price"] * pledge.qty
+		row["loan_security_price"] = pledge.loan_security_price
+		row["loan_security_value"] = pledge.amount
 		row["currency"] = default_currency
 
 		data.append(row)
diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
index cac8067..2ca9f16 100644
--- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
+++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
@@ -217,6 +217,8 @@
 		}
 
 	def get_summary_data(self):
+		if not self.data: return
+
 		return [
 			{
 				"value": sum(self.total_demand),
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 5862963..3570a0f 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -18,7 +18,7 @@
 		};
 	},
 	onload: function (frm) {
-		var so = frm.get_docfield("Project", "sales_order");
+		var so = frappe.meta.get_docfield("Project", "sales_order");
 		so.get_route_options_for_new_doc = function (field) {
 			if (frm.is_new()) return;
 			return {
@@ -135,4 +135,4 @@
 		frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
 	});
 
-}
+}
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 524a958..9421668 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -917,7 +917,7 @@
 
 	shipping_rule: function() {
 		var me = this;
-		if(this.frm.doc.shipping_rule && this.frm.doc.shipping_address) {
+		if(this.frm.doc.shipping_rule) {
 			return this.frm.call({
 				doc: this.frm.doc,
 				method: "apply_shipping_rule",
diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.py b/erpnext/stock/doctype/item_attribute/item_attribute.py
index 71b998f..2f75bbd 100644
--- a/erpnext/stock/doctype/item_attribute/item_attribute.py
+++ b/erpnext/stock/doctype/item_attribute/item_attribute.py
@@ -34,7 +34,7 @@
 			if self.numeric_values:
 				validate_is_incremental(self, self.name, item.value, item.name)
 			else:
-				validate_item_attribute_value(attributes_list, self.name, item.value, item.name)
+				validate_item_attribute_value(attributes_list, self.name, item.value, item.name, from_variant=False)
 
 	def validate_numeric(self):
 		if self.numeric_values:
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 3562181..3a8deb6 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -18,7 +18,7 @@
 
 		// formatter for material request item
 		frm.set_indicator_formatter('item_code',
-			function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange"; });
+			function(doc) { return (doc.stock_qty<=doc.ordered_qty) ? "green" : "orange"; });
 
 		frm.set_query("item_code", "items", function() {
 			return {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 18d6853..5fbd512 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -500,7 +500,7 @@
 					if raw_material_cost and self.purpose == "Manufacture":
 						d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate"))
 						d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount"))
-					elif self.purpose == "Repack" and total_fg_qty:
+					elif self.purpose == "Repack" and total_fg_qty and not d.set_basic_rate_manually:
 						d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty)
 						d.basic_amount = d.basic_rate * d.qty
 
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index c16a41c..7b9c129 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -23,6 +23,7 @@
   "image",
   "image_view",
   "quantity_and_rate",
+  "set_basic_rate_manually",
   "qty",
   "basic_rate",
   "basic_amount",
@@ -491,12 +492,21 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:parent.purpose===\"Repack\" && doc.t_warehouse",
+   "fieldname": "set_basic_rate_manually",
+   "fieldtype": "Check",
+   "label": "Set Basic Rate Manually",
+   "show_days": 1,
+   "show_seconds": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-04-23 19:19:28.539769",
+ "modified": "2020-06-08 12:57:03.172887",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",