Merge pull request #21273 from marination/quoted-item-report
refactor: Quoted Item Comparison Report
This commit is contained in:
		
						commit
						2ee400989b
					
				| @ -18,6 +18,10 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext | ||||
| 	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')); | ||||
|  | ||||
| @ -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,18 @@ | ||||
|    "options": "Opportunity", | ||||
|    "print_hide": 1, | ||||
|    "read_only": 1 | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "valid_till", | ||||
|    "fieldtype": "Date", | ||||
|    "label": "Valid Till" | ||||
|   } | ||||
|  ], | ||||
|  "icon": "fa fa-shopping-cart", | ||||
|  "idx": 29, | ||||
|  "is_submittable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2019-12-30 19:17:28.208693", | ||||
|  "modified": "2020-04-15 11:44:52.958022", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Buying", | ||||
|  "name": "Supplier Quotation", | ||||
|  | ||||
| @ -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 @@ class SupplierQuotation(BuyingController): | ||||
| 		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 @@ class SupplierQuotation(BuyingController): | ||||
| 				"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 @@ def make_quotation(source_name, target_doc=None): | ||||
| 	}, 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())) | ||||
| @ -5,6 +5,8 @@ frappe.listview_settings['Supplier Quotation'] = { | ||||
| 			return [__("Ordered"), "green", "status,=,Ordered"]; | ||||
| 		} else if(doc.status==="Rejected") { | ||||
| 			return [__("Lost"), "darkgrey", "status,=,Lost"]; | ||||
| 		} else if(doc.status==="Expired") { | ||||
| 			return [__("Expired"), "darkgrey", "status,=,Expired"]; | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| @ -5,20 +5,18 @@ frappe.query_reports["Quoted Item Comparison"] = { | ||||
| 	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, | ||||
| 			default: "", | ||||
| 			options: "Item", | ||||
| 			label: __("Item"), | ||||
| 			fieldname: "item", | ||||
| 			fieldname: "item_code", | ||||
| 			fieldtype: "Link", | ||||
| 			get_query: () => { | ||||
| 				let quote = frappe.query_report.get_filter_value('supplier_quotation'); | ||||
| @ -37,8 +35,37 @@ frappe.query_reports["Quoted Item Comparison"] = { | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			fieldname: "supplier", | ||||
| 			label: __("Supplier"), | ||||
| 			fieldtype: "MultiSelectList", | ||||
| 			get_data: function(txt) { | ||||
| 				return frappe.db.get_link_options('Supplier', txt); | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			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] } } | ||||
| 			} | ||||
| 		} | ||||
| 	], | ||||
| 
 | ||||
| 	onload: (report) => { | ||||
| 		// Create a button for setting the default supplier
 | ||||
| 		report.page.add_inner_button(__("Select Default Supplier"), () => { | ||||
| @ -102,6 +129,4 @@ frappe.query_reports["Quoted Item Comparison"] = { | ||||
| 		}); | ||||
| 		dialog.show(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -2,103 +2,180 @@ | ||||
| # 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 frappe import _ | ||||
| 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) | ||||
| 	return columns, data | ||||
| 	 | ||||
| def get_quote_list(item, qty_list): | ||||
| 	out = [] | ||||
| 	if not item: | ||||
| 	if not filters: | ||||
| 		return [], [] | ||||
| 
 | ||||
| 	conditions = get_conditions(filters) | ||||
| 	supplier_quotation_data = get_data(filters, conditions) | ||||
| 	columns = get_columns() | ||||
| 
 | ||||
| 	data, chart_data = prepare_data(supplier_quotation_data) | ||||
| 
 | ||||
| 	return columns, data, None, chart_data | ||||
| 
 | ||||
| def get_conditions(filters): | ||||
| 	conditions = "" | ||||
| 	if filters.get("supplier_quotation"): | ||||
| 		conditions += " AND sqi.parent = %(supplier_quotation)s" | ||||
| 
 | ||||
| 	if filters.get("request_for_quotation"): | ||||
| 		conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s" | ||||
| 
 | ||||
| 	if filters.get("supplier"): | ||||
| 		conditions += " AND sq.supplier in %(supplier)s" | ||||
| 	return conditions | ||||
| 
 | ||||
| def get_data(filters, conditions): | ||||
| 	if not filters.get("item_code"): | ||||
| 		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) | ||||
| 	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 = %(item_code)s | ||||
| 			AND sqi.parent = sq.name | ||||
| 			AND sqi.docstatus < 2 | ||||
| 			AND sq.company = %(company)s | ||||
| 			AND sq.status != 'Expired' | ||||
| 			{0}""".format(conditions), filters, as_dict=1) | ||||
| 
 | ||||
| 	return supplier_quotation_data | ||||
| 
 | ||||
| def prepare_data(supplier_quotation_data): | ||||
| 	out, suppliers, qty_list = [], [], [] | ||||
| 	supplier_wise_map = defaultdict(list) | ||||
| 	supplier_qty_price_map = {} | ||||
| 
 | ||||
| 	company_currency = frappe.db.get_default("currency") | ||||
| 	float_precision = cint(frappe.db.get_default("float_precision")) or 2 | ||||
| 
 | ||||
| 	for data in supplier_quotation_data: | ||||
| 		supplier = data.get("supplier") | ||||
| 		supplier_currency = frappe.db.get_value("Supplier", data.get("supplier"), "default_currency") | ||||
| 
 | ||||
| 	#Add a row for each supplier | ||||
| 	for root in set(suppliers): | ||||
| 		supplier_currency = frappe.db.get_value("Supplier", root, "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), | ||||
| 			"uom": data.get("uom"), | ||||
| 			"request_for_quotation": data.get("request_for_quotation"), | ||||
| 		} | ||||
| 
 | ||||
| 		for qt in qty_list: | ||||
| 			col = frappe._dict({ | ||||
| 				"key": str(qt.qty), | ||||
| 				"label": "Qty: " + str(int(qt.qty)) | ||||
| 			}) | ||||
| 			out.append(col) | ||||
| 		# map for report view of form {'supplier1':[{},{},...]} | ||||
| 		supplier_wise_map[supplier].append(row) | ||||
| 
 | ||||
| 	return out | ||||
| 	 | ||||
| def get_columns(qty_list): | ||||
| 		# map for chart preparation of the form {'supplier1': {'qty': 'price'}} | ||||
| 		if not supplier in supplier_qty_price_map: | ||||
| 			supplier_qty_price_map[supplier] = {} | ||||
| 		supplier_qty_price_map[supplier][row["qty"]] = row["price"] | ||||
| 
 | ||||
| 		suppliers.append(supplier) | ||||
| 		qty_list.append(data.get("qty")) | ||||
| 
 | ||||
| 	suppliers = list(set(suppliers)) | ||||
| 	qty_list = list(set(qty_list)) | ||||
| 
 | ||||
| 	# final data format for report view | ||||
| 	for supplier in suppliers: | ||||
| 		supplier_wise_map[supplier][0].update({"supplier_name": supplier}) | ||||
| 		for entry in supplier_wise_map[supplier]: | ||||
| 			out.append(entry) | ||||
| 
 | ||||
| 	chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map) | ||||
| 
 | ||||
| 	return out, chart_data | ||||
| 
 | ||||
| def prepare_chart_data(suppliers, qty_list, supplier_qty_price_map): | ||||
| 	data_points_map = {} | ||||
| 	qty_list.sort() | ||||
| 
 | ||||
| 	# create qty wise values map of the form {'qty1':[value1, value2]} | ||||
| 	for supplier in suppliers: | ||||
| 		entry = supplier_qty_price_map[supplier] | ||||
| 		for qty in qty_list: | ||||
| 			if not qty in data_points_map: | ||||
| 				data_points_map[qty] = [] | ||||
| 			if qty in entry: | ||||
| 				data_points_map[qty].append(entry[qty]) | ||||
| 			else: | ||||
| 				data_points_map[qty].append(None) | ||||
| 
 | ||||
| 	dataset = [] | ||||
| 	for qty in qty_list: | ||||
| 		datapoints = { | ||||
| 			"name": _("Price for Qty ") + str(qty), | ||||
| 			"values": data_points_map[qty] | ||||
| 		} | ||||
| 		dataset.append(datapoints) | ||||
| 
 | ||||
| 	chart_data = { | ||||
| 		"data": { | ||||
| 			"labels": suppliers, | ||||
| 			"datasets": dataset | ||||
| 		}, | ||||
| 		"type": "bar" | ||||
| 	} | ||||
| 
 | ||||
| 	return chart_data | ||||
| 
 | ||||
| def get_columns(): | ||||
| 	columns = [{ | ||||
| 		"fieldname": "supplier_name", | ||||
| 		"label": "Supplier", | ||||
| 		"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": "uom", | ||||
| 		"label": _("UOM"), | ||||
| 		"fieldtype": "Link", | ||||
| 		"options": "UOM", | ||||
| 		"width": 90 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fieldname": "request_for_quotation", | ||||
| 		"label": _("Request for Quotation"), | ||||
| 		"fieldtype": "Link", | ||||
| 		"options": "Request for Quotation", | ||||
| 		"width": 200 | ||||
| 	} | ||||
| 	] | ||||
| 
 | ||||
| 	return columns | ||||
| @ -308,7 +308,8 @@ scheduler_events = { | ||||
| 		"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", | ||||
|  | ||||
| @ -683,3 +683,4 @@ erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashme | ||||
| 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 | ||||
| erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation | ||||
|  | ||||
| @ -0,0 +1,8 @@ | ||||
| from __future__ import unicode_literals | ||||
| import frappe | ||||
| 
 | ||||
| def execute(): | ||||
| 	frappe.reload_doc("buying", "doctype", "supplier_quotation") | ||||
| 	frappe.db.sql("""UPDATE `tabSupplier Quotation` | ||||
| 		SET valid_till = DATE_ADD(transaction_date , INTERVAL 1 MONTH) | ||||
| 		WHERE docstatus < 2""") | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user