From 59239172a1ed352848fe06c705a68281c41e3106 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 14 Sep 2020 17:34:21 +0530 Subject: [PATCH 1/2] feat: Supplier Quotation Comparison - v3 --- .../quoted_item_comparison.json | 32 ------- .../__init__.py | 0 .../supplier_quotation_comparison.html} | 0 .../supplier_quotation_comparison.js} | 12 ++- .../supplier_quotation_comparison.json | 32 +++++++ .../supplier_quotation_comparison.py} | 83 ++++++++++++++----- 6 files changed, 106 insertions(+), 53 deletions(-) delete mode 100644 erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.json rename erpnext/buying/report/{quoted_item_comparison => supplier_quotation_comparison}/__init__.py (100%) rename erpnext/buying/report/{quoted_item_comparison/quoted_item_comparison.html => supplier_quotation_comparison/supplier_quotation_comparison.html} (100%) rename erpnext/buying/report/{quoted_item_comparison/quoted_item_comparison.js => supplier_quotation_comparison/supplier_quotation_comparison.js} (91%) create mode 100644 erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.json rename erpnext/buying/report/{quoted_item_comparison/quoted_item_comparison.py => supplier_quotation_comparison/supplier_quotation_comparison.py} (70%) diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.json b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.json deleted file mode 100644 index 23b3ace49c..0000000000 --- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2016-07-21 08:31:05.890362", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 2, - "is_standard": "Yes", - "modified": "2017-02-24 20:04:58.784351", - "modified_by": "Administrator", - "module": "Buying", - "name": "Quoted Item Comparison", - "owner": "Administrator", - "ref_doctype": "Supplier Quotation", - "report_name": "Quoted Item Comparison", - "report_type": "Script Report", - "roles": [ - { - "role": "Manufacturing Manager" - }, - { - "role": "Purchase Manager" - }, - { - "role": "Purchase User" - }, - { - "role": "Stock User" - } - ] -} \ No newline at end of file diff --git a/erpnext/buying/report/quoted_item_comparison/__init__.py b/erpnext/buying/report/supplier_quotation_comparison/__init__.py similarity index 100% rename from erpnext/buying/report/quoted_item_comparison/__init__.py rename to erpnext/buying/report/supplier_quotation_comparison/__init__.py diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.html b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html similarity index 100% rename from erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.html rename to erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js similarity index 91% rename from erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js rename to erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js index 518d665e7e..80e521a8bf 100644 --- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js @@ -1,7 +1,7 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.query_reports["Quoted Item Comparison"] = { +frappe.query_reports["Supplier Quotation Comparison"] = { filters: [ { fieldtype: "Link", @@ -78,6 +78,13 @@ frappe.query_reports["Quoted Item Comparison"] = { return { filters: { "docstatus": ["<", 2] } } } }, + { + "fieldname":"group_by", + "label": __("Group by"), + "fieldtype": "Select", + "options": [__("Group by Supplier"), __("Group by Item")], + "default": __("Group by Supplier") + }, { fieldtype: "Check", label: __("Include Expired"), @@ -98,6 +105,9 @@ frappe.query_reports["Quoted Item Comparison"] = { } } + if(column.fieldname === "price_per_unit" && data.price_per_unit && data.min && data.min === 1){ + value = `
${value}
`; + } return value; }, diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.json b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.json new file mode 100644 index 0000000000..886e5b8757 --- /dev/null +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.json @@ -0,0 +1,32 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2016-07-21 08:31:05.890362", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 2, + "is_standard": "Yes", + "modified": "2017-02-24 20:04:58.784351", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Quotation Comparison", + "owner": "Administrator", + "ref_doctype": "Supplier Quotation", + "report_name": "Supplier Quotation Comparison", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing Manager" + }, + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock User" + } + ] +} \ No newline at end of file diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py similarity index 70% rename from erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py rename to erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py index 4426560c16..d2399b1ec9 100644 --- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py @@ -12,9 +12,9 @@ def execute(filters=None): if not filters: return [], [] + columns = get_columns(filters) conditions = get_conditions(filters) supplier_quotation_data = get_data(filters, conditions) - columns = get_columns() data, chart_data = prepare_data(supplier_quotation_data, filters) message = get_message() @@ -41,9 +41,13 @@ def get_conditions(filters): return conditions def get_data(filters, conditions): - supplier_quotation_data = frappe.db.sql("""SELECT - sqi.parent, sqi.item_code, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation, - sqi.lead_time_days, sq.supplier, sq.valid_till + supplier_quotation_data = frappe.db.sql(""" + SELECT + sqi.parent, sqi.item_code, + sqi.qty, sqi.stock_qty, sqi.rate, + sqi.uom, sqi.stock_uom, + sqi.request_for_quotation, + sqi.lead_time_days, sq.supplier as supplier_name, sq.valid_till FROM `tabSupplier Quotation Item` sqi, `tabSupplier Quotation` sq @@ -58,16 +62,18 @@ def get_data(filters, conditions): return supplier_quotation_data def prepare_data(supplier_quotation_data, filters): - out, suppliers, qty_list, chart_data = [], [], [], [] - supplier_wise_map = defaultdict(list) + out, groups, qty_list, suppliers, chart_data = [], [], [], [], [] + group_wise_map = defaultdict(list) supplier_qty_price_map = {} + group_by_field = "supplier_name" if filters.get("group_by") == "Group by Supplier" else "item_code" 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") + group = data.get(group_by_field) # get item or supplier value for this row + + supplier_currency = frappe.db.get_value("Supplier", data.get("supplier_name"), "default_currency") if supplier_currency: exchange_rate = get_exchange_rate(supplier_currency, company_currency) @@ -75,38 +81,55 @@ def prepare_data(supplier_quotation_data, filters): exchange_rate = 1 row = { - "item_code": data.get('item_code'), + "item_code": "" if group_by_field=="item_code" else data.get("item_code"), # leave blank if group by field + "supplier_name": "" if group_by_field=="supplier_name" else data.get("supplier_name"), "quotation": data.get("parent"), "qty": data.get("qty"), "price": flt(data.get("rate") * exchange_rate, float_precision), "uom": data.get("uom"), + "stock_uom": data.get('stock_uom'), "request_for_quotation": data.get("request_for_quotation"), "valid_till": data.get('valid_till'), "lead_time_days": data.get('lead_time_days') } + row["price_per_unit"] = flt(row["price"]) / (flt(data.get("stock_qty")) or 1) - # map for report view of form {'supplier1':[{},{},...]} - supplier_wise_map[supplier].append(row) + # map for report view of form {'supplier1'/'item1':[{},{},...]} + group_wise_map[group].append(row) # map for chart preparation of the form {'supplier1': {'qty': 'price'}} + supplier = data.get("supplier_name") if filters.get("item_code"): if not supplier in supplier_qty_price_map: supplier_qty_price_map[supplier] = {} supplier_qty_price_map[supplier][row["qty"]] = row["price"] + groups.append(group) suppliers.append(supplier) qty_list.append(data.get("qty")) + groups = list(set(groups)) suppliers = list(set(suppliers)) qty_list = list(set(qty_list)) + highlight_min_price = group_by_field == "item_code" + # 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]: + for group in groups: + group_entries = group_wise_map[group] # all entries pertaining to item/supplier + group_entries[0].update({group_by_field : group}) + + if highlight_min_price: + prices = [group_entry["price_per_unit"] for group_entry in group_entries] + min_price = min(prices) + + for entry in group_entries: + if highlight_min_price and entry["price_per_unit"] == min_price: + entry["min"] = 1 out.append(entry) if filters.get("item_code"): + # render chart only for one item comparison chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map) return out, chart_data @@ -145,8 +168,9 @@ def prepare_chart_data(suppliers, qty_list, supplier_qty_price_map): return chart_data -def get_columns(): - columns = [{ +def get_columns(filters): + group_by_columns = [ + { "fieldname": "supplier_name", "label": _("Supplier"), "fieldtype": "Link", @@ -158,8 +182,10 @@ def get_columns(): "label": _("Item"), "fieldtype": "Link", "options": "Item", - "width": 200 - }, + "width": 150 + }] + + columns = [ { "fieldname": "uom", "label": _("UOM"), @@ -180,6 +206,20 @@ def get_columns(): "options": "Company:company:default_currency", "width": 110 }, + { + "fieldname": "stock_uom", + "label": _("Stock UOM"), + "fieldtype": "Link", + "options": "UOM", + "width": 90 + }, + { + "fieldname": "price_per_unit", + "label": _("Price per Unit (Stock UOM)"), + "fieldtype": "Currency", + "options": "Company:company:default_currency", + "width": 120 + }, { "fieldname": "quotation", "label": _("Supplier Quotation"), @@ -205,9 +245,12 @@ def get_columns(): "fieldtype": "Link", "options": "Request for Quotation", "width": 150 - } - ] + }] + if filters.get("group_by") == "Group by Item": + group_by_columns.reverse() + + columns[0:0] = group_by_columns # add positioned group by columns to the report return columns def get_message(): From f90c3602a73786e101ae7a2fcf1bfb1e4551b743 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 1 Oct 2020 14:38:17 +0530 Subject: [PATCH 2/2] fix: Price should be total amount, modified deskpage --- erpnext/buying/desk_page/buying/buying.json | 4 ++-- .../supplier_quotation_comparison.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 565d39c3c8..2e870fea82 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -33,7 +33,7 @@ { "hidden": 0, "label": "Other Reports", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"reference_doctype\": \"Purchase Receipt\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"reference_doctype\": \"Purchase Invoice\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"reference_doctype\": \"Purchase Receipt\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"reference_doctype\": \"Purchase Invoice\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Quotation Comparison\",\n \"name\": \"Supplier Quotation Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -60,7 +60,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-06-29 19:30:24.983050", + "modified": "2020-09-30 14:40:55.638458", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py index d2399b1ec9..2b371915f3 100644 --- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py @@ -44,7 +44,7 @@ def get_data(filters, conditions): supplier_quotation_data = frappe.db.sql(""" SELECT sqi.parent, sqi.item_code, - sqi.qty, sqi.stock_qty, sqi.rate, + sqi.qty, sqi.stock_qty, sqi.amount, sqi.uom, sqi.stock_uom, sqi.request_for_quotation, sqi.lead_time_days, sq.supplier as supplier_name, sq.valid_till @@ -85,7 +85,7 @@ def prepare_data(supplier_quotation_data, filters): "supplier_name": "" if group_by_field=="supplier_name" else data.get("supplier_name"), "quotation": data.get("parent"), "qty": data.get("qty"), - "price": flt(data.get("rate") * exchange_rate, float_precision), + "price": flt(data.get("amount") * exchange_rate, float_precision), "uom": data.get("uom"), "stock_uom": data.get('stock_uom'), "request_for_quotation": data.get("request_for_quotation"), @@ -112,12 +112,12 @@ def prepare_data(supplier_quotation_data, filters): suppliers = list(set(suppliers)) qty_list = list(set(qty_list)) - highlight_min_price = group_by_field == "item_code" + highlight_min_price = group_by_field == "item_code" or filters.get("item_code") # final data format for report view for group in groups: group_entries = group_wise_map[group] # all entries pertaining to item/supplier - group_entries[0].update({group_by_field : group}) + group_entries[0].update({group_by_field : group}) # Add item/supplier name in first group row if highlight_min_price: prices = [group_entry["price_per_unit"] for group_entry in group_entries]