Merge branch 'develop' of https://github.com/frappe/erpnext into quoted-item-report
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
index 16061c6..1b8b404 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
@@ -18,6 +18,10 @@
 	refresh: function() {
 		var me = this;
 		this._super();
+
+		if (this.frm.doc.__islocal && !this.frm.doc.valid_till) {
+			this.frm.set_value('valid_till', frappe.datetime.add_months(this.frm.doc.transaction_date, 1));
+		}
 		if (this.frm.doc.docstatus === 1) {
 			cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order,
 				__('Create'));
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 82fc628..6964e78 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -13,9 +13,10 @@
   "supplier",
   "supplier_name",
   "column_break1",
-  "transaction_date",
-  "amended_from",
   "company",
+  "transaction_date",
+  "valid_till",
+  "amended_from",
   "address_section",
   "supplier_address",
   "contact_person",
@@ -791,13 +792,19 @@
    "options": "Opportunity",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "valid_till",
+   "fieldtype": "Date",
+   "label": "Valid Till",
+   "reqd": 1
   }
  ],
  "icon": "fa fa-shopping-cart",
  "idx": 29,
  "is_submittable": 1,
  "links": [],
- "modified": "2019-12-30 19:17:28.208693",
+ "modified": "2020-04-14 22:43:32.248415",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier Quotation",
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index 5b4356a..baf2457 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import flt, nowdate, add_days
+from frappe.utils import flt, nowdate, add_days, getdate
 from frappe.model.mapper import get_mapped_doc
 
 from erpnext.controllers.buying_controller import BuyingController
@@ -28,6 +28,7 @@
 		validate_for_items(self)
 		self.validate_with_previous_doc()
 		self.validate_uom_is_integer("uom", "qty")
+		self.validate_valid_till()
 
 	def on_submit(self):
 		frappe.db.set(self, "status", "Submitted")
@@ -52,6 +53,11 @@
 				"is_child_table": True
 			}
 		})
+
+	def validate_valid_till(self):
+		if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date):
+			frappe.throw(_("Valid till Date cannot be before Transaction Date"))
+
 	def update_rfq_supplier_status(self, include_me):
 		rfq_list = set([])
 		for item in self.items:
@@ -158,3 +164,11 @@
 	}, target_doc)
 
 	return doclist
+
+def set_expired_status():
+	frappe.db.sql("""
+		UPDATE
+			`tabSupplier Quotation` SET `status` = 'Expired'
+		WHERE
+			`status` not in ('Cancelled', 'Stopped') AND `valid_till` < %s
+		""", (nowdate()))
\ No newline at end of file
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
index 3d05612..f331beb 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
+++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
@@ -5,13 +5,11 @@
 	filters: [
 		{
 			fieldtype: "Link",
-			label: __("Supplier Quotation"),
-			options: "Supplier Quotation",
-			fieldname: "supplier_quotation",
-			default: "",
-			get_query: () => {
-				return { filters: { "docstatus": ["<", 2] } }
-			}
+			label: __("Company"),
+			options: "Company",
+			fieldname: "company",
+			default: frappe.defaults.get_user_default("Company"),
+			"reqd": 1
 		},
 		{
 			reqd: 1,
@@ -37,8 +35,83 @@
 					}
 				}
 			}
+		},
+		{
+			fieldtype: "Link",
+			label: __("Supplier Quotation"),
+			options: "Supplier Quotation",
+			fieldname: "supplier_quotation",
+			default: "",
+			get_query: () => {
+				return { filters: { "docstatus": ["<", 2] } }
+			}
+		},
+		{
+			fieldtype: "Link",
+			label: __("Request for Quotation"),
+			options: "Request for Quotation",
+			fieldname: "request_for_quotation",
+			default: "",
+			get_query: () => {
+				return { filters: { "docstatus": ["<", 2] } }
+			}
 		}
 	],
+
+	prepare_chart_data: (result) => {
+		let supplier_wise_map = {}, data_points_map = {};
+		let qty_list = result.map(res=> res.qty);
+		qty_list = new Set(qty_list);
+
+		// create supplier wise map like in Report
+		for(let res of result){
+			if(!(res.supplier in supplier_wise_map)){
+				supplier_wise_map[res.supplier]= {};
+			}
+			supplier_wise_map[res.supplier][res.qty] = res.price;
+		}
+
+		// create  datapoints for each qty
+		for(let supplier of Object.keys(supplier_wise_map)) {
+			let row = supplier_wise_map[supplier];
+			for(let qty of qty_list){
+				if(!data_points_map[qty]){
+					data_points_map[qty] = []
+				}
+				if(row[qty]){
+					data_points_map[qty].push(row[qty]);
+				}
+				else{
+					data_points_map[qty].push(null);
+				}
+			}
+		}
+
+		let dataset = [];
+		qty_list.forEach((qty) => {
+			let datapoints = {
+				'name': 'Price for Qty ' + qty,
+				'values': data_points_map[qty]
+			}
+			dataset.push(datapoints);
+
+		});
+		return dataset;
+	},
+
+	get_chart_data: function (columns, result) {
+		let suppliers = result.filter(d => d.supplier_name).map(res => res.supplier_name);
+		let dataset = frappe.query_reports["Quoted Item Comparison"].prepare_chart_data(result);
+
+		return {
+			data: {
+				labels: suppliers,
+				datasets: dataset
+			},
+			type: 'bar'
+		}
+	},
+
 	onload: (report) => {
 		// Create a button for setting the default supplier
 		report.page.add_inner_button(__("Select Default Supplier"), () => {
@@ -102,6 +175,4 @@
 		});
 		dialog.show();
 	}
-}
-
-
+}
\ No newline at end of file
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
index 5aff6ba..bb1067a 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
+++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
@@ -2,103 +2,114 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
-from erpnext.setup.utils import get_exchange_rate
-from frappe.utils import flt, cint
 import frappe
+from frappe.utils import flt, cint
+from collections import defaultdict
+from erpnext.setup.utils import get_exchange_rate
 
 def execute(filters=None):
-	qty_list = get_quantity_list(filters.item)
-	data = get_quote_list(filters.item, qty_list)
-	columns = get_columns(qty_list)
+	conditions = get_conditions(filters)
+	data = get_data(filters, conditions)
+	columns = get_columns()
 	return columns, data
-	
-def get_quote_list(item, qty_list):
-	out = []
+
+def get_data(filters, conditions):
+	out, suppliers = [], []
+	item = filters.get("item")
+
 	if not item:
 		return []
 
-	suppliers = []
-	price_data = []
 	company_currency = frappe.db.get_default("currency")
-	float_precision = cint(frappe.db.get_default("float_precision")) or 2 
-	# Get the list of suppliers
-	for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item`
-		where item_code=%s and docstatus < 2""", item, as_dict=1):
-		for splr in frappe.db.sql("""select supplier from `tabSupplier Quotation`
-			where name =%s and docstatus < 2""", root.parent, as_dict=1):
-			ip = frappe._dict({
-				"supplier": splr.supplier,
-				"qty": root.qty,
-				"parent": root.parent,
-				"rate": root.rate
-			})
-			price_data.append(ip)
-			suppliers.append(splr.supplier)
+	float_precision = cint(frappe.db.get_default("float_precision")) or 2
 
-	#Add a row for each supplier
-	for root in set(suppliers):
-		supplier_currency = frappe.db.get_value("Supplier", root, "default_currency")
+	supplier_quotation_data = frappe.db.sql("""SELECT
+		sqi.parent, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
+		sq.supplier
+		FROM
+			`tabSupplier Quotation Item` sqi,
+			`tabSupplier Quotation` sq
+		WHERE
+			sqi.item_code = '{0}'
+			AND sqi.parent = sq.name
+			AND sqi.docstatus < 2
+			AND sq.company = '{1}'
+			AND sq.status != 'Expired'
+			{2}""".format(item, filters.get("company"), conditions), as_dict=1)
+
+	supplier_wise_map = defaultdict(list)
+
+	for data in supplier_quotation_data:
+		supplier_currency = frappe.db.get_value("Supplier", data.get("supplier"), "default_currency")
 		if supplier_currency:
 			exchange_rate = get_exchange_rate(supplier_currency, company_currency)
 		else:
 			exchange_rate = 1
 
-		row = frappe._dict({
-			"supplier_name": root
-		})
-		for col in qty_list:
-			# Get the quantity for this row
-			for item_price in price_data:
-				if str(item_price.qty) == col.key and item_price.supplier == root:
-					row[col.key] = flt(item_price.rate * exchange_rate, float_precision)
-					row[col.key + "QUOTE"] = item_price.parent
-					break
-				else:
-					row[col.key] = ""
-					row[col.key + "QUOTE"] = ""
-		out.append(row)
-			
-	return out
-	
-def get_quantity_list(item):
-	out = []
-	
-	if item:
-		qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item`
-			where ifnull(item_code,'')=%s and docstatus < 2 order by qty""", item, as_dict=1)
+		row = {
+			"quotation": data.get("parent"),
+			"qty": data.get("qty"),
+			"price": flt(data.get("rate") * exchange_rate, float_precision),
+			"request_for_quotation": data.get("request_for_quotation"),
+			"supplier": data.get("supplier") # used for chart generation
+		}
 
-		for qt in qty_list:
-			col = frappe._dict({
-				"key": str(qt.qty),
-				"label": "Qty: " + str(int(qt.qty))
-			})
-			out.append(col)
+		supplier_wise_map[data.supplier].append(row)
+		suppliers.append(data.supplier)
+
+	suppliers = set(suppliers)
+
+	for supplier in suppliers:
+		supplier_wise_map[supplier][0].update({"supplier_name": supplier})
+		for entry in supplier_wise_map[supplier]:
+			out.append(entry)
 
 	return out
-	
-def get_columns(qty_list):
+
+def get_conditions(filters):
+	conditions = ""
+
+	if filters.get("request_for_quotation"):
+		conditions += " AND sqi.request_for_quotation = '{0}' ".format(filters.get("request_for_quotation"))
+
+	return conditions
+
+
+def get_columns():
 	columns = [{
 		"fieldname": "supplier_name",
 		"label": "Supplier",
 		"fieldtype": "Link",
 		"options": "Supplier",
 		"width": 200
-	}]
-
-	for qty in qty_list:
-		columns.append({
-			"fieldname": qty.key,
-			"label": qty.label,
-			"fieldtype": "Currency",
-			"options": "currency",
-			"width": 80
-		})
-		columns.append({
-			"fieldname": qty.key + "QUOTE",
-			"label": "Quotation",
-			"fieldtype": "Link",
-			"options": "Supplier Quotation",
-			"width": 90
-		})
+	},
+	{
+		"fieldname": "quotation",
+		"label": "Supplier Quotation",
+		"fieldtype": "Link",
+		"options": "Supplier Quotation",
+		"width": 200
+	},
+	{
+		"fieldname": "qty",
+		"label": "Quantity",
+		"fieldtype": "Float",
+		"width": 80
+	},
+	{
+		"fieldname": "price",
+		"label": "Price",
+		"fieldtype": "Currency",
+		"options": "Company:company:default_currency",
+		"width": 110
+	},
+	{
+		"fieldname": "request_for_quotation",
+		"label": "Request for Quotation",
+		"fieldtype": "Link",
+		"options": "Request for Quotation",
+		"width": 200
+	}
+	]
 
 	return columns
\ No newline at end of file
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 6b6d1e2..e661e9c 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -306,7 +306,8 @@
 		"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts",
 		"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status",
 		"erpnext.selling.doctype.quotation.quotation.set_expired_status",
-		"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status"
+		"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status",
+		"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status"
 	],
 	"daily_long": [
 		"erpnext.setup.doctype.email_digest.email_digest.send",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 9ef0b8d..ec84f5f 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -667,3 +667,4 @@
 erpnext.patches.v12_0.set_total_batch_quantity
 erpnext.patches.v12_0.rename_mws_settings_fields
 erpnext.patches.v12_0.set_updated_purpose_in_pick_list
+erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py
new file mode 100644
index 0000000..0f24ec6
--- /dev/null
+++ b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	reload_doc("buying", "doctype", "suppplier_quotation")
+	frappe.db.sql("""UPDATE `tabSupplier Quotation`
+		SET valid_till = DATE_ADD(transaction_date , INTERVAL 1 MONTH)
+		WHERE docstatus < 2""")
\ No newline at end of file