Merge pull request #3971 from anandpdoshi/fix-item-variant-attributes

Attributes in the variant should be in the same order as in the Template
diff --git a/erpnext/__version__.py b/erpnext/__version__.py
index 889fbef..7a61e5f 100644
--- a/erpnext/__version__.py
+++ b/erpnext/__version__.py
@@ -1,2 +1,2 @@
 from __future__ import unicode_literals
-__version__ = '5.8.2'
+__version__ = '6.0.0'
diff --git a/erpnext/change_log/current/collapsible_sections.md b/erpnext/change_log/current/collapsible_sections.md
deleted file mode 100644
index 9c7fb30..0000000
--- a/erpnext/change_log/current/collapsible_sections.md
+++ /dev/null
@@ -1,2 +0,0 @@
-- Most transaction forms will now have collapsibe sections so that forms appear to be more organized and you can easily locate parts to be edited.
-- The document title on most transactions can be edited so that you can set meaningful titles to all transactions like Sales Invoice and also edit it to denote status.
diff --git a/erpnext/change_log/v6/v6_0_0.md b/erpnext/change_log/v6/v6_0_0.md
new file mode 100644
index 0000000..bb8cd5d
--- /dev/null
+++ b/erpnext/change_log/v6/v6_0_0.md
@@ -0,0 +1,4 @@
+- Added Calendar and Gantt Views for Sales Order based on Delivery Date
+- Most transaction forms will now have collapsibe sections so that forms appear to be more organized and you can easily locate parts to be edited.
+- The document title on most transactions can be edited so that you can set meaningful titles to all transactions like Sales Invoice and also edit it to denote status.
+- Allow user to disable warnings for "Multiple Items" and "Multiple Sales Order against a Customer's Purchase Order" via Sales and Purchase Settings. Sponsored by: **[McLean Images](http://www.mcleanimages.com.au/)**
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 04c12f7..6c98202 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -268,7 +268,7 @@
 			{0}
 			{match_conditions}
 			order by expiry_date, name desc
-			limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args, debug=1)
+			limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args)
 
 def get_account_list(doctype, txt, searchfield, start, page_len, filters):
 	filter_list = []
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 7a6a535..f7f9197 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -27,7 +27,7 @@
 """
 app_icon = "icon-th"
 app_color = "#e74c3c"
-app_version = "5.8.2"
+app_version = "6.0.0"
 github_link = "https://github.com/frappe/erpnext"
 
 error_report_email = "support@erpnext.com"
@@ -51,7 +51,7 @@
 
 email_append_to = ["Job Applicant", "Opportunity", "Issue"]
 
-calendars = ["Task", "Production Order", "Time Log", "Leave Application"]
+calendars = ["Task", "Production Order", "Time Log", "Leave Application", "Sales Order"]
 
 website_generators = ["Item Group", "Item", "Sales Partner"]
 
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py
index 6299736..19746fc 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/production_order.py
@@ -329,15 +329,15 @@
 			frappe.throw(_("Production Order cannot be raised against a Item Template"), ItemHasVariantError)
 
 		validate_end_of_life(self.production_item)
-		
+
 	def validate_qty(self):
 		if not self.qty > 0:
 			frappe.throw(_("Quantity to Manufacture must be greater than 0."))
-			
+
 	def validate_operation_time(self):
 		for d in self.operations:
 			if not d.time_in_mins > 0:
-				frappe.throw(_("Operation Time must be greater than 0 for Operation {0}".format(d.operation))) 
+				frappe.throw(_("Operation Time must be greater than 0 for Operation {0}".format(d.operation)))
 
 @frappe.whitelist()
 def get_item_details(item):
@@ -381,19 +381,17 @@
 
 @frappe.whitelist()
 def get_events(start, end, filters=None):
-	from frappe.desk.reportview import build_match_conditions
-	if not frappe.has_permission("Production Order"):
-		frappe.msgprint(_("No Permission"), raise_exception=1)
+	"""Returns events for Gantt / Calendar view rendering.
 
-	conditions = build_match_conditions("Production Order")
-	conditions = conditions and (" and " + conditions) or ""
-	if filters:
-		filters = json.loads(filters)
-		for key in filters:
-			if filters[key]:
-				conditions += " and " + key + ' = "' + filters[key].replace('"', '\"') + '"'
+	:param start: Start date-time.
+	:param end: End date-time.
+	:param filters: Filters (JSON).
+	"""
+	from frappe.desk.calendar import get_event_conditions
+	conditions = get_event_conditions("Production Order", filters)
 
-	data = frappe.db.sql("""select name, production_item, planned_start_date, planned_end_date
+	data = frappe.db.sql("""select name, production_item, planned_start_date,
+		planned_end_date, status
 		from `tabProduction Order`
 		where ((ifnull(planned_start_date, '0000-00-00')!= '0000-00-00') \
 				and (planned_start_date between %(start)s and %(end)s) \
@@ -427,4 +425,4 @@
 def get_default_warehouse():
 	wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
 	fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse")
-	return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse}
\ No newline at end of file
+	return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse}
diff --git a/erpnext/manufacturing/doctype/production_order/production_order_calendar.js b/erpnext/manufacturing/doctype/production_order/production_order_calendar.js
index a43b25e..2832494 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order_calendar.js
+++ b/erpnext/manufacturing/doctype/production_order/production_order_calendar.js
@@ -10,6 +10,15 @@
 		"allDay": "allDay"
 	},
 	gantt: true,
+	get_css_class: function(data) {
+		if(data.status==="Completed") {
+			return "success";
+		} else if(data.status==="In Process") {
+			return "warning";
+		} else {
+			return "danger";
+		}
+	},
 	filters: [
 		{
 			"fieldtype": "Link",
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index fcd756b..28cfcd3 100644
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -42,7 +42,7 @@
 			for d in self.depends_on:
 				if frappe.db.get_value("Task", d.task, "status") != "Closed":
 					frappe.throw(_("Cannot close task as its dependant task {0} is not closed.").format(d.task))
-			
+
 			from frappe.desk.form.assign_to import clear
 			clear(self.doctype, self.name)
 
@@ -107,18 +107,14 @@
 
 @frappe.whitelist()
 def get_events(start, end, filters=None):
-	from frappe.desk.reportview import build_match_conditions
-	if not frappe.has_permission("Task"):
-		frappe.msgprint(_("No Permission"), raise_exception=1)
+	"""Returns events for Gantt / Calendar view rendering.
 
-	conditions = build_match_conditions("Task")
-	conditions = conditions and (" and " + conditions) or ""
-
-	if filters:
-		filters = json.loads(filters)
-		for key in filters:
-			if filters[key]:
-				conditions += " and " + key + ' = "' + filters[key].replace('"', '\"') + '"'
+	:param start: Start date-time.
+	:param end: End date-time.
+	:param filters: Filters (JSON).
+	"""
+	from frappe.desk.calendar import get_event_conditions
+	conditions = get_event_conditions("Task", filters)
 
 	data = frappe.db.sql("""select name, exp_start_date, exp_end_date,
 		subject, status, project from `tabTask`
diff --git a/erpnext/projects/doctype/time_log/time_log.py b/erpnext/projects/doctype/time_log/time_log.py
index 6e937c0..ed89307 100644
--- a/erpnext/projects/doctype/time_log/time_log.py
+++ b/erpnext/projects/doctype/time_log/time_log.py
@@ -2,7 +2,7 @@
 # License: GNU General Public License v3. See license.txt
 
 from __future__ import unicode_literals
-import frappe, json
+import frappe
 from frappe import _
 from frappe.utils import cstr, flt, get_datetime, get_time, getdate
 from dateutil.relativedelta import relativedelta
@@ -248,17 +248,8 @@
 	:param end: End date-time.
 	:param filters: Filters like workstation, project etc.
 	"""
-	from frappe.desk.reportview import build_match_conditions
-	if not frappe.has_permission("Time Log"):
-		frappe.msgprint(_("No Permission"), raise_exception=1)
-
-	conditions = build_match_conditions("Time Log")
-	conditions = conditions and (" and " + conditions) or ""
-	if filters:
-		filters = json.loads(filters)
-		for key in filters:
-			if filters[key]:
-				conditions += " and " + key + ' = "' + filters[key].replace('"', '\"') + '"'
+	from frappe.desk.calendar import get_event_conditions
+	conditions = get_event_conditions("Time Log", filters)
 
 	data = frappe.db.sql("""select name, from_time, to_time,
 		activity_type, task, project, production_order, workstation from `tabTime Log`
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 0b5eb37..065d329 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -471,3 +471,24 @@
 		}, target_doc)
 
 		return doclist
+
+@frappe.whitelist()
+def get_events(start, end, filters=None):
+	"""Returns events for Gantt / Calendar view rendering.
+
+	:param start: Start date-time.
+	:param end: End date-time.
+	:param filters: Filters (JSON).
+	"""
+	from frappe.desk.calendar import get_event_conditions
+	conditions = get_event_conditions("Sales Order", filters)
+
+	data = frappe.db.sql("""select name, customer_name, delivery_status, billing_status, delivery_date
+		from `tabSales Order`
+		where (ifnull(delivery_date, '0000-00-00')!= '0000-00-00') \
+				and (delivery_date between %(start)s and %(end)s) {conditions}
+		""".format(conditions=conditions), {
+			"start": start,
+			"end": end
+		}, as_dict=True, update={"allDay": 0})
+	return data
diff --git a/erpnext/selling/doctype/sales_order/sales_order_calendar.js b/erpnext/selling/doctype/sales_order/sales_order_calendar.js
new file mode 100644
index 0000000..8724daa
--- /dev/null
+++ b/erpnext/selling/doctype/sales_order/sales_order_calendar.js
@@ -0,0 +1,45 @@
+// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+frappe.views.calendar["Sales Order"] = {
+	field_map: {
+		"start": "delivery_date",
+		"end": "delivery_date",
+		"id": "name",
+		"title": "customer_name",
+		"allDay": "allDay"
+	},
+	gantt: true,
+	filters: [
+		{
+			"fieldtype": "Link",
+			"fieldname": "customer",
+			"options": "Customer",
+			"label": __("Customer")
+		},
+		{
+			"fieldtype": "Select",
+			"fieldname": "delivery_status",
+			"options": "Not Delivered\nFully Delivered\nPartly Delivered\nClosed\nNot Applicable",
+			"label": __("Delivery Status")
+		},
+		{
+			"fieldtype": "Select",
+			"fieldname": "billing_status",
+			"options": "Not Billed\nFully Billed\nPartly Billed\nClosed",
+			"label": __("Billing Status")
+		},
+	],
+	get_events_method: "erpnext.selling.doctype.sales_order.sales_order.get_events",
+	get_css_class: function(data) {
+		if(data.status=="Stopped") {
+			return "";
+		} if(data.delivery_status=="Not Delivered") {
+			return "danger";
+		} else if(data.delivery_status=="Partly Delivered") {
+			return "warning";
+		} else if(data.delivery_status=="Fully Delivered") {
+			return "success";
+		}
+	}
+}
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index 87c1375..f7334b1 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -6,6 +6,7 @@
 
 from frappe.utils import cint
 from frappe import _
+from frappe.desk.notifications import clear_notifications
 
 @frappe.whitelist()
 def delete_company_transactions(company_name):
@@ -16,11 +17,16 @@
 		frappe.throw(_("Transactions can only be deleted by the creator of the Company"), frappe.PermissionError)
 
 	delete_bins(company_name)
+	
+	delete_time_logs(company_name)
 
 	for doctype in frappe.db.sql_list("""select parent from
 		tabDocField where fieldtype='Link' and options='Company'"""):
-		if doctype not in ("Account", "Cost Center", "Warehouse", "Budget Detail", "Party Account"):
+		if doctype not in ("Account", "Cost Center", "Warehouse", "Budget Detail", "Party Account", "Employee"):
 			delete_for_doctype(doctype, company_name)
+			
+	# Clear notification counts
+	clear_notifications()
 
 def delete_for_doctype(doctype, company_name):
 	meta = frappe.get_meta(doctype)
@@ -60,3 +66,20 @@
 def delete_bins(company_name):
 	frappe.db.sql("""delete from tabBin where warehouse in
 			(select name from tabWarehouse where company=%s)""", company_name)
+
+def delete_time_logs(company_name):
+	# Delete Time Logs as it is linked to Production Order / Project / Task, which are linked to company
+	frappe.db.sql("""
+		delete from `tabTime Log`
+		where 
+			(ifnull(project, '') != '' 
+				and exists(select name from `tabProject` where name=`tabTime Log`.project and company=%(company)s))
+			or (ifnull(task, '') != '' 
+				and exists(select name from `tabTask` where name=`tabTime Log`.task and company=%(company)s))
+			or (ifnull(production_order, '') != '' 
+				and exists(select name from `tabProduction Order` 
+					where name=`tabTime Log`.production_order and company=%(company)s))
+			or (ifnull(sales_invoice, '') != '' 
+				and exists(select name from `tabSales Invoice` 
+					where name=`tabTime Log`.sales_invoice and company=%(company)s))
+	""", {"company": company_name})
\ No newline at end of file
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 0cec77b..40c2729 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -212,7 +212,7 @@
 		price_list_rate = get_price_list_rate_for(args, item_doc.name)
 		if not price_list_rate and item_doc.variant_of:
 			price_list_rate = get_price_list_rate_for(args, item_doc.variant_of)
-
+			
 		if not price_list_rate:
 			if args.price_list and args.rate:
 				insert_item_price(args)
@@ -231,12 +231,16 @@
 	if frappe.db.get_value("Price List", args.price_list, "currency") == args.currency \
 		and cint(frappe.db.get_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing")):
 		if frappe.has_permission("Item Price", "write"):
+			
+			price_list_rate = args.rate / args.conversion_factor \
+				if args.get("conversion_factor") else args.rate
+			
 			item_price = frappe.get_doc({
 				"doctype": "Item Price",
 				"price_list": args.price_list,
 				"item_code": args.item_code,
 				"currency": args.currency,
-				"price_list_rate": args.rate
+				"price_list_rate": price_list_rate
 			})
 			item_price.insert()
 			frappe.msgprint("Item Price added for {0} in Price List {1}".format(args.item_code,
diff --git a/setup.py b/setup.py
index 6a420c3..bfa2fa7 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
 from setuptools import setup, find_packages
 
-version = "5.8.2"
+version = "6.0.0"
 
 with open("requirements.txt", "r") as f:
 	install_requires = f.readlines()