From cc989b62bd6e881b797cf3f3eebd236cdcb07284 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 5 May 2020 23:58:08 +0530 Subject: [PATCH 01/11] feat: Buying Desk, Dashboard and Reports --- erpnext/buying/desk_page/buying/buying.json | 79 +++-- .../purchase_order_analysis/__init__.py | 0 .../purchase_order_analysis.js | 86 ++++++ .../purchase_order_analysis.json | 33 +++ .../purchase_order_analysis.py | 277 ++++++++++++++++++ .../requested_items_to_order/__init__.py | 0 .../requested_items_to_order.js | 64 ++++ .../requested_items_to_order.json | 34 +++ .../requested_items_to_order.py | 211 +++++++++++++ 9 files changed, 753 insertions(+), 31 deletions(-) create mode 100644 erpnext/buying/report/purchase_order_analysis/__init__.py create mode 100644 erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js create mode 100644 erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json create mode 100644 erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py create mode 100644 erpnext/buying/report/requested_items_to_order/__init__.py create mode 100644 erpnext/buying/report/requested_items_to_order/requested_items_to_order.js create mode 100644 erpnext/buying/report/requested_items_to_order/requested_items_to_order.json create mode 100644 erpnext/buying/report/requested_items_to_order/requested_items_to_order.py diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 5e764cf8bbe..432f3c557b8 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -2,24 +2,24 @@ "cards": [ { "hidden": 0, - "label": "Supplier", - "links": "[\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier Group master.\",\n \"label\": \"Supplier Group\",\n \"name\": \"Supplier Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n }\n]" + "label": "Buying", + "links": "[ \n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Request for purchase.\",\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Purchase Orders given to Suppliers.\",\n \"label\": \"Purchase Order\",\n \"name\": \"Purchase Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Request for quotation.\",\n \"label\": \"Request for Quotation\",\n \"name\": \"Request for Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Quotations received from Suppliers.\",\n \"label\": \"Supplier Quotation\",\n \"name\": \"Supplier Quotation\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, - "label": "Purchasing", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Purchase Orders given to Suppliers.\",\n \"label\": \"Purchase Order\",\n \"name\": \"Purchase Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Request for purchase.\",\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Request for quotation.\",\n \"label\": \"Request for Quotation\",\n \"name\": \"Request for Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Quotations received from Suppliers.\",\n \"label\": \"Supplier Quotation\",\n \"name\": \"Supplier Quotation\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Items and Pricing", - "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n }\n]" + "label": "Items & Pricing", + "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, "label": "Settings", "links": "[\n {\n \"description\": \"Default settings for buying transactions.\",\n \"label\": \"Buying Settings\",\n \"name\": \"Buying Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for buying transactions.\",\n \"label\": \"Purchase Taxes and Charges Template\",\n \"name\": \"Purchase Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"type\": \"doctype\"\n }\n]" }, + { + "hidden": 0, + "label": "Supplier", + "links": "[\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier Group master.\",\n \"label\": \"Supplier Group\",\n \"name\": \"Supplier Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n }\n]" + }, { "hidden": 0, "label": "Supplier Scorecard", @@ -28,32 +28,33 @@ { "hidden": 0, "label": "Key Reports", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Analytics\",\n \"name\": \"Purchase Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier-Wise Sales Analytics\",\n \"name\": \"Supplier-Wise Sales Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Stock Ledger Entry\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Trends\",\n \"name\": \"Purchase Order Trends\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Procurement Tracker\",\n \"name\": \"Procurement Tracker\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Requested Items To Be Ordered\",\n \"name\": \"Requested Items To Be Ordered\",\n \"onboard\": 1,\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Analytics\",\n \"name\": \"Purchase Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier-Wise Sales Analytics\",\n \"name\": \"Supplier-Wise Sales Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Stock Ledger Entry\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Requested Items to Order\",\n \"name\": \"Requested Items to Order\",\n \"onboard\": 1,\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Trends\",\n \"name\": \"Purchase Order Trends\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Procurement Tracker\",\n \"name\": \"Procurement Tracker\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n }\n]" }, { "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\": \"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\": \"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 \"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]" } ], + "cards_label": "Masters & Reports ", "category": "Modules", "charts": [ { - "chart_name": "Expenses", - "label": "Expenses" + "chart_name": "Purchase Analytics", + "label": "Buying Analytics" } ], + "charts_label": "Buying Dashboard", "creation": "2020-01-28 11:50:26.195467", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-04-01 11:28:51.192097", + "modified": "2020-05-05 23:48:25.788598", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -62,32 +63,48 @@ "pin_to_top": 0, "shortcuts": [ { - "format": "{} Unpaid", - "label": "Purchase Invoice", - "link_to": "Purchase Invoice", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Unpaid\"\n}", - "type": "DocType" - }, - { - "format": "{} to receive", + "color": "#ffe8cd", + "format": "{} to Receive", "label": "Purchase Order", "link_to": "Purchase Order", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Receive\"\n}", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", "type": "DocType" }, { + "color": "#ffe8cd", + "format": "{} Pending", + "label": "Material Request", + "link_to": "Material Request", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Pending\"\n}", + "type": "DocType" + }, + { + "color": "#ffe8cd", + "format": "{} to Bill ", + "label": "Purchase Receipt", + "link_to": "Purchase Receipt", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", + "type": "DocType" + }, + { + "color": "#ffe8cd", + "format": "{} Unpaid / Overdue", + "label": "Purchase Invoice", + "link_to": "Purchase Invoice", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": [\"in\", [\"Unpaid\", \"Overdue\"]]\n}", + "type": "DocType" + }, + { + "color": "#cef6d1", + "format": "{} Active", "label": "Supplier Quotation", "link_to": "Supplier Quotation", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"!=\", \"Expired\"]\n}", "type": "DocType" }, { - "label": "Accounts Payable", - "link_to": "Accounts Payable", - "type": "Report" - }, - { - "label": "Purchase Register", - "link_to": "Purchase Register", + "label": "Item-wise Purchase Register", + "link_to": "Item-wise Purchase Register", "type": "Report" } ] diff --git a/erpnext/buying/report/purchase_order_analysis/__init__.py b/erpnext/buying/report/purchase_order_analysis/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js new file mode 100644 index 00000000000..24abb6d44ab --- /dev/null +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js @@ -0,0 +1,86 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Purchase Order Analysis"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_default("company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname": "purchase_order", + "label": __("Purchase Order"), + "fieldtype": "Link", + "width": "80", + "options": "Purchase Order", + "get_query": () =>{ + return { + filters: { "docstatus": 1 } + } + } + }, + { + "fieldname": "status", + "label": __("Status"), + "fieldtype": "MultiSelectList", + "width": "80", + get_data: function(txt) { + let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"] + let options = [] + for (let option of status){ + options.push({ + "value": option, + "description": "" + }) + } + return options + } + }, + { + "fieldname": "chart_based_on", + "label": __("Chart Based On"), + "fieldtype": "Select", + "width": "80", + "options": "Quantity\nAmount", + "default": "Quantity" + }, + { + "fieldname": "group_by_po", + "label": __("Group by Purchase Order"), + "fieldtype": "Check", + "default": 0 + } + ], + + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["received_qty", "billed_amount"]; + + if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + return value; + } +}; diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json new file mode 100644 index 00000000000..196aaaed224 --- /dev/null +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json @@ -0,0 +1,33 @@ +{ + "add_total_row": 0, + "creation": "2020-05-04 18:41:28.625119", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-04 18:41:28.625119", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Order Analysis", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Purchase Order", + "report_name": "Purchase Order Analysis", + "report_type": "Script Report", + "roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock User" + }, + { + "role": "Supplier" + } + ] +} \ No newline at end of file diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py new file mode 100644 index 00000000000..78b86636e07 --- /dev/null +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -0,0 +1,277 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import copy +from frappe import _ +from frappe.utils import flt, date_diff, getdate + +def execute(filters=None): + if not filters: + return [], [] + + validate_filters(filters) + + columns = get_columns(filters) + conditions = get_conditions(filters) + + data = get_data(conditions, filters) + + if not data: + return [], [] + + data, chart_data = prepare_data(data, filters) + + return columns, data, None, chart_data + +def validate_filters(filters): + from_date, to_date = filters.get("from_date"), filters.get("to_date") + + if not from_date and to_date: + frappe.throw(_("From and To Dates are required.")) + elif date_diff(to_date, from_date) < 0: + frappe.throw(_("To Date cannot be before From Date.")) + +def get_conditions(filters): + conditions = "" + if filters.get("from_date") and filters.get("to_date"): + conditions += " and po.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date")) + + if filters.get("company"): + conditions += " and po.company = '{0}'".format(filters.get("company")) + + if filters.get("purchase_order"): + conditions += " and po.name = '{0}'".format(filters.get("purchase_order")) + + if filters.get("status"): + conditions += " and po.status in (%s)" % ', '.join(['%s']*len(filters.get("status"))) + + return conditions + +def get_data(conditions, filters): + status = filters.get("status") + # temporary fix for dashboard chart + if status is None: + status = [] + + data = frappe.db.sql(""" + SELECT + po.transaction_date as date, + poi.schedule_date as required_date, + po.name as purchase_order, + po.status, po.supplier, poi.item_code, + poi.qty, poi.received_qty, + (poi.qty - poi.received_qty) AS pending_qty, + IFNULL(pii.qty, 0) as billed_qty, + poi.base_amount as amount, + (poi.received_qty * poi.base_rate) as received_qty_amount, + (poi.billed_amt * IFNULL(po.conversion_rate, 1)) as billed_amount, + (poi.base_amount - (poi.billed_amt * IFNULL(po.conversion_rate, 1))) as pending_amount, + po.set_warehouse as warehouse, + po.company, poi.name + FROM + `tabPurchase Order` po, + `tabPurchase Order Item` poi + LEFT JOIN `tabPurchase Invoice Item` pii + ON pii.po_detail = poi.name + WHERE + poi.parent = po.name + and po.status not in ('Stopped', 'Closed') + and po.docstatus = 1 + {0} + GROUP BY poi.name + ORDER BY po.transaction_date ASC + """.format(conditions), tuple(status), as_dict=1) + + return data + +def prepare_data(data, filters): + completed, pending = 0,0 + chart_based_on = filters.get("chart_based_on") + pending_field = "pending_qty" if chart_based_on == "Quantity" else "pending_amount" + completed_field = "received_qty" if chart_based_on == "Quantity" else "billed_amount" + + if filters.get("group_by_po"): + purchase_order_map = {} + + for row in data: + # sum data for chart + completed += row[completed_field] + pending += row[pending_field] + + # prepare data for report view + row["qty_to_bill"] = flt(row["qty"]) - flt(row["billed_qty"]) + + if filters.get("group_by_po"): + po_name = row["purchase_order"] + + if not po_name in purchase_order_map: + # create an entry + row_copy = copy.deepcopy(row) + purchase_order_map[po_name] = row_copy + else: + # update existing entry + po_row = purchase_order_map[po_name] + po_row["required_date"] = min(getdate(po_row["required_date"]), getdate(row["required_date"])) + + # sum numeric columns + fields = ["qty", "received_qty", "pending_qty", "billed_qty", "qty_to_bill", "amount", + "received_qty_amount", "billed_amount", "pending_amount"] + for field in fields: + po_row[field] = flt(row[field]) + flt(po_row[field]) + + chart_data = prepare_chart_data(chart_based_on, pending, completed) + + if filters.get("group_by_po"): + data = [] + for po in purchase_order_map: + data.append(purchase_order_map[po]) + return data, chart_data + + return data, chart_data + +def prepare_chart_data(chart_based_on, pending, completed): + labels = ["Qty to Receive","Received Qty"] if chart_based_on == "Quantity" else ["Amount to Bill","Billed Amount"] + + return { + "data" : { + "labels": labels, + "datasets": [ + {"values": [pending, completed]} + ] + }, + "type": 'donut', + "height": 300 + } + +def get_columns(filters): + columns = [ + { + "label":_("Date"), + "fieldname": "date", + "fieldtype": "Date", + "width": 90 + }, + { + "label":_("Required By"), + "fieldname": "required_date", + "fieldtype": "Date", + "width": 90 + }, + { + "label": _("Purchase Order"), + "fieldname": "purchase_order", + "fieldtype": "Link", + "options": "Purchase Order", + "width": 160 + }, + { + "label":_("Status"), + "fieldname": "status", + "fieldtype": "Data", + "width": 130 + }, + { + "label": _("Supplier"), + "fieldname": "supplier", + "fieldtype": "Link", + "options": "Supplier", + "width": 130 + }] + + if not filters.get("group_by_po"): + columns.append({ + "label":_("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 100 + }) + + columns.extend([ + { + "label": _("Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Received Qty"), + "fieldname": "received_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Pending Qty"), + "fieldname": "pending_qty", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Billed Qty"), + "fieldname": "billed_qty", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Qty to Bill"), + "fieldname": "qty_to_bill", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Billed Amount"), + "fieldname": "billed_amount", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Pending Amount"), + "fieldname": "pending_amount", + "fieldtype": "Currency", + "width": 130, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Received Qty Amount"), + "fieldname": "received_qty_amount", + "fieldtype": "Currency", + "width": 130, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Warehouse"), + "fieldname": "warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": 100 + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 100 + } + ]) + + return columns + diff --git a/erpnext/buying/report/requested_items_to_order/__init__.py b/erpnext/buying/report/requested_items_to_order/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js new file mode 100644 index 00000000000..21adb135476 --- /dev/null +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js @@ -0,0 +1,64 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Requested Items to Order"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_default("company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname": "material_request", + "label": __("Material Request"), + "fieldtype": "Link", + "width": "80", + "options": "Material Request", + "get_query": () =>{ + return { + filters: { + "docstatus": 1, + "material_request_type": "Purchase", + "per_received": ["<", 100] + } + } + } + }, + { + "fieldname": "group_by_mr", + "label": __("Group by Material Request"), + "fieldtype": "Check", + "default": 0 + } + ], + + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname == "ordered_qty" && data && data.ordered_qty > 0) { + value = "" + value + ""; + } + return value; + } +}; diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.json b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.json new file mode 100644 index 00000000000..4a0578be4bf --- /dev/null +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.json @@ -0,0 +1,34 @@ +{ + "add_total_row": 1, + "creation": "2020-05-04 20:23:57.750719", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-05 13:05:51.723951", + "modified_by": "Administrator", + "module": "Buying", + "name": "Requested Items to Order", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Material Request", + "report_name": "Requested Items to Order", + "report_type": "Script Report", + "roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + }, + { + "role": "Purchase User" + } + ] +} \ No newline at end of file diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py new file mode 100644 index 00000000000..a021d3c1cab --- /dev/null +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py @@ -0,0 +1,211 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import copy +from frappe import _ +from frappe.utils import flt, date_diff, getdate + +def execute(filters=None): + if not filters: + return [],[] + + validate_filters(filters) + + columns = get_columns(filters) + conditions = get_conditions(filters) + + #get queried data + data = get_data(filters, conditions) + + #prepare data for report and chart views + data, chart_data = prepare_data(data, filters) + + return columns, data, None, chart_data + +def validate_filters(filters): + from_date, to_date = filters.get("from_date"), filters.get("to_date") + + if not from_date and to_date: + frappe.throw(_("From and To Dates are required.")) + elif date_diff(to_date, from_date) < 0: + frappe.throw(_("To Date cannot be before From Date.")) + +def get_conditions(filters): + conditions = '' + + if filters.get("from_date") and filters.get("to_date"): + conditions += " and mr.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date")) + + if filters.get("company"): + conditions += " and mr.company = '{0}'".format(filters.get("company")) + + if filters.get("material_request"): + conditions += " and mr.name = '{0}'".format(filters.get("material_request")) + + return conditions + +def get_data(filters, conditions): + data = frappe.db.sql(""" + select + mr.name as material_request, + mr.transaction_date as date, + mr_item.schedule_date as required_date, + mr_item.item_code as item_code, + sum(ifnull(mr_item.stock_qty, 0)) as qty, + ifnull(mr_item.stock_uom, '') as uom, + sum(ifnull(mr_item.ordered_qty, 0)) as ordered_qty, + (sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as qty_to_order, + mr_item.item_name as item_name, + mr.company as company + from + `tabMaterial Request` mr, `tabMaterial Request Item` mr_item + where + mr_item.parent = mr.name + and mr.material_request_type = "Purchase" + and mr.docstatus = 1 + and mr.status != "Stopped" + {conditions} + group by mr.name, mr_item.item_code + having + sum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0)) + order by mr.transaction_date, mr.schedule_date""".format(conditions=conditions), as_dict=1) + + return data + +def prepare_data(data, filters): + """Prepare consolidated Report data and Chart data""" + material_request_map = {} + + for row in data: + if not row["material_request"] in material_request_map: + # create an entry with mr as key + row_copy = copy.deepcopy(row) + material_request_map[row["material_request"]] = row_copy + else: + mr_row = material_request_map[row["material_request"]] + mr_row["required_date"] = min(getdate(mr_row["required_date"]), getdate(row["required_date"])) + + #sum numeric rows + fields = ["qty", "ordered_qty", "qty_to_order"] + for field in fields: + mr_row[field] = flt(mr_row[field]) + flt(row[field]) + + chart_data = prepare_chart_data(material_request_map) + + if filters.get("group_by_mr"): + data =[] + for mr in material_request_map: + data.append(material_request_map[mr]) + return data, chart_data + + return data, chart_data + +def prepare_chart_data(data): + labels, qty_to_order, ordered_qty = [], [], [] + + for row in data: + mr_row = data[row] + labels.append(mr_row["material_request"]) + qty_to_order.append(mr_row["qty_to_order"]) + ordered_qty.append(mr_row["ordered_qty"]) + + chart_data = { + "data" : { + "labels": labels, + "datasets": [ + { + 'name': _('Qty to Order'), + 'values': qty_to_order + }, + { + 'name': _('Ordered Qty'), + 'values': ordered_qty + } + ] + }, + "type": "bar", + "barOptions": { + "stacked": 1 + }, + } + + return chart_data + +def get_columns(filters): + columns = [ + { + "label": _("Material Request"), + "fieldname": "material_request", + "fieldtype": "Link", + "options": "Material Request", + "width": 150 + }, + { + "label":_("Date"), + "fieldname": "date", + "fieldtype": "Date", + "width": 90 + }, + { + "label":_("Required By"), + "fieldname": "required_date", + "fieldtype": "Date", + "width": 100 + } + ] + + if not filters.get("group_by_mr"): + columns.extend([{ + "label":_("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 100 + }, + { + "label":_("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 100 + }, + { + "label": _("UOM"), + "fieldname": "uom", + "fieldtype": "Data", + "width": 100, + }]) + + columns.extend([ + { + "label": _("Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Ordered Qty"), + "fieldname": "ordered_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Qty to Order"), + "fieldname": "qty_to_order", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 100 + } + ]) + + return columns From 631260b632f01b5b0b16e7fd61a7fd1b40bd562f Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 6 May 2020 18:12:26 +0530 Subject: [PATCH 02/11] chore: Added fixtures and Onboarding. --- erpnext/buying/desk_page/buying/buying.json | 6 +- erpnext/buying/onboarding/buying/buying.json | 42 +++++++++ .../buying_settings/buying_settings.json | 16 ++++ .../create_a_supplier/create_a_supplier.json | 16 ++++ .../introduction_to_buying.json | 16 ++++ .../setup_wizard/data/dashboard_charts.py | 93 +++++++++++++++++++ 6 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 erpnext/buying/onboarding/buying/buying.json create mode 100644 erpnext/buying/onboarding_step/buying_settings/buying_settings.json create mode 100644 erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json create mode 100644 erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 432f3c557b8..9749f3c97fc 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -54,10 +54,11 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-05 23:48:25.788598", + "modified": "2020-05-06 18:10:12.760321", "modified_by": "Administrator", "module": "Buying", "name": "Buying", + "onboarding": "Buying", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, @@ -107,5 +108,6 @@ "link_to": "Item-wise Purchase Register", "type": "Report" } - ] + ], + "shortcuts_label": "Quick Access" } \ No newline at end of file diff --git a/erpnext/buying/onboarding/buying/buying.json b/erpnext/buying/onboarding/buying/buying.json new file mode 100644 index 00000000000..c35309964e7 --- /dev/null +++ b/erpnext/buying/onboarding/buying/buying.json @@ -0,0 +1,42 @@ +{ + "allow_roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-06 15:56:35.049205", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-06 16:49:17.433261", + "modified_by": "Administrator", + "module": "Buying", + "name": "Buying", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Buying" + }, + { + "step": "Create a Supplier" + }, + { + "step": "Buying Settings" + } + ], + "subtitle": "Products, Purchases, Analysis and more.", + "success_message": "The Buying Module is all set up!", + "title": "Let's Setup the Buying Module.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json new file mode 100644 index 00000000000..7274c0d62d1 --- /dev/null +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -0,0 +1,16 @@ +{ + "action": "Update Settings", + "creation": "2020-05-06 15:53:44.667414", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-06 16:51:56.862206", + "modified_by": "Administrator", + "name": "Buying Settings", + "owner": "Administrator", + "reference_document": "Buying Settings", + "title": "Configure Buying Settings." +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json new file mode 100644 index 00000000000..14164c33037 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-06 15:46:09.019329", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-06 17:00:41.446942", + "modified_by": "Administrator", + "name": "Create a Supplier", + "owner": "Administrator", + "reference_document": "Supplier", + "title": "Create a Supplier" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json new file mode 100644 index 00000000000..f1a027af369 --- /dev/null +++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json @@ -0,0 +1,16 @@ +{ + "action": "Watch Video", + "creation": "2020-05-06 15:37:09.477765", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-06 16:47:47.378049", + "modified_by": "Administrator", + "name": "Introduction to Buying", + "owner": "Administrator", + "title": "Introduction to Buying", + "video_url": "https://youtu.be/efFajTTQBa8" +} \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index b182dfc103f..8d293973906 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -32,6 +32,17 @@ def get_default_dashboards(): { "chart": "Expenses" }, { "chart": "Patient Appointments" } ] + }, + { + "doctype": "Dashboard", + "dashboard_name": "Buying", + "charts": [ + { "chart": "Purchase Analytics" }, + { "chart": "Material Request Purchase Analysis" }, + { "chart": "Purchase Order Analysis" }, + { "chart": "Requested Items to Order" }, + ] + } ], "Charts": [ @@ -123,6 +134,88 @@ def get_default_dashboards(): "document_type": "Patient Appointment", "type": "Line", "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Analytics", + "filters_json": json.dumps({ + "tree_type": "Item", + "doc_type": "Purchase Receipt", + "value_quantity": "Value", + "from_date": "2020-03-01", + "to_date": "2020-07-31", + "company": company.name, + "range": "Weekly" + }), + "type": "Line", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Purchase Analytics", + "custom_options": json.dumps({ + "x_field": "entity", + "chart_type": "Line", + "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], + "y_fields": ["total"], + "lineOptions": {"regionFill": 1} + }) + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "document_type": "Material Request", + "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), + "is_custom": 0, + "type": "Donut", + "timeseries": 0, + "chart_type": "Group By", + "group_by_based_on": "status", + "chart_name": "Material Request Purchase Analysis", + "group_by_type": "Count", + "custom_options": json.dumps({"height": 300}) + + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Order Analysis", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-04", + "to_date": "2020-07-04", + "chart_based_on": "Quantity" + }), + "is_custom": 1, + "type": "Donut", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Purchase Order Analysis", + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Requested Items to Order", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-01", + "to_date": "2020-07-01", + "group_by_mr": 0 + }), + "is_custom": 1, + "type": "Bar", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Requested Items to Order", + "custom_options": json.dumps({ + "type": "bar", + "barOptions": {"stacked": 1}, + "axisOptions": {"shortenYAxisNumbers": 1} + }) } ] } From 4a73bc672b45d612fbdd3623479ac80765dfe306 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 6 May 2020 18:34:07 +0530 Subject: [PATCH 03/11] fix: Old buying config update & bar chart on desk - Line chart was inapprpriate as the values were not continuous. --- erpnext/config/buying.py | 2 +- erpnext/setup/setup_wizard/data/dashboard_charts.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 1d4054786e6..16b49a1e578 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -166,7 +166,7 @@ def get_data(): { "type": "report", "is_query_report": True, - "name": "Requested Items To Be Ordered", + "name": "Requested Items To Order", "reference_doctype": "Material Request", "onboard": 1, }, diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index 8d293973906..9c654a829d5 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -154,10 +154,9 @@ def get_default_dashboards(): "chart_name": "Purchase Analytics", "custom_options": json.dumps({ "x_field": "entity", - "chart_type": "Line", + "chart_type": "Bar", "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], - "y_fields": ["total"], - "lineOptions": {"regionFill": 1} + "y_fields": ["total"] }) }, { From d99f85bf3c514cd018dd7a0f3d851598a265026b Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 13:16:05 +0530 Subject: [PATCH 04/11] chore: Fixtures using new sync api --- erpnext/buying/dashboard_fixtures.py | 28 +++++++ .../setup_wizard/data/dashboard_charts.py | 81 ------------------- 2 files changed, 28 insertions(+), 81 deletions(-) create mode 100644 erpnext/buying/dashboard_fixtures.py diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py new file mode 100644 index 00000000000..199765bacec --- /dev/null +++ b/erpnext/buying/dashboard_fixtures.py @@ -0,0 +1,28 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json + + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + return [{ + "name": "", + "dashboard_name": "", + "charts": [ + { "chart": } + ] + }] + +def get_charts(): + return [ { ... } ] + +def get_number_cards(): + return [ { ... } ] \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index 9c654a829d5..88d42590fc9 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -134,87 +134,6 @@ def get_default_dashboards(): "document_type": "Patient Appointment", "type": "Line", "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Analytics", - "filters_json": json.dumps({ - "tree_type": "Item", - "doc_type": "Purchase Receipt", - "value_quantity": "Value", - "from_date": "2020-03-01", - "to_date": "2020-07-31", - "company": company.name, - "range": "Weekly" - }), - "type": "Line", - 'timeseries': 0, - "chart_type": "Report", - "chart_name": "Purchase Analytics", - "custom_options": json.dumps({ - "x_field": "entity", - "chart_type": "Bar", - "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], - "y_fields": ["total"] - }) - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "document_type": "Material Request", - "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), - "is_custom": 0, - "type": "Donut", - "timeseries": 0, - "chart_type": "Group By", - "group_by_based_on": "status", - "chart_name": "Material Request Purchase Analysis", - "group_by_type": "Count", - "custom_options": json.dumps({"height": 300}) - - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Order Analysis", - "filters_json": json.dumps({ - "company": company.name, - "from_date": "2020-04-04", - "to_date": "2020-07-04", - "chart_based_on": "Quantity" - }), - "is_custom": 1, - "type": "Donut", - "timeseries": 0, - "chart_type": "Report", - "chart_name": "Purchase Order Analysis", - "custom_options": json.dumps({ - "type": "donut", - "height": 300, - "axisOptions": {"shortenYAxisNumbers": 1} - }) - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Requested Items to Order", - "filters_json": json.dumps({ - "company": company.name, - "from_date": "2020-04-01", - "to_date": "2020-07-01", - "group_by_mr": 0 - }), - "is_custom": 1, - "type": "Bar", - "timeseries": 0, - "chart_type": "Report", - "chart_name": "Requested Items to Order", - "custom_options": json.dumps({ - "type": "bar", - "barOptions": {"stacked": 1}, - "axisOptions": {"shortenYAxisNumbers": 1} - }) } ] } From 354b01617effcfae1cf2cd03252f4a60a5814c99 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 17:35:17 +0530 Subject: [PATCH 05/11] chore: Dashboard Fixtures --- erpnext/buying/dashboard_fixtures.py | 112 +++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 199765bacec..0bd9a1fd488 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -12,17 +12,119 @@ def get_data(): "number_cards": get_number_cards(), }) +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +company = frappe.get_doc("Company", get_company_for_dashboards()) + def get_dashboards(): return [{ - "name": "", - "dashboard_name": "", + "name": "Buying", + "dashboard_name": "Buying", "charts": [ - { "chart": } + { "chart": "Purchase Analytics", "width": "Full"}, + { "chart": "Material Request Purchase Analysis", "width": "Half"}, + { "chart": "Purchase Order Analysis", "width": "Half"}, + { "chart": "Requested Items to Order", "width": "Full"} ] }] def get_charts(): - return [ { ... } ] + return [ + { + "name": "Purchase Analytics", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Analytics", + "filters_json": json.dumps({ + "tree_type": "Item", + "doc_type": "Purchase Receipt", + "value_quantity": "Value", + "from_date": "2020-03-01", + "to_date": "2020-07-31", + "company": company.name, + "range": "Weekly" + }), + "x_field": "entity", + "type": "Bar", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Purchase Analytics", + "custom_options": json.dumps({ + "x_field": "entity", + "chart_type": "Bar", + "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], + "y_fields": ["total"] + }) + }, + { + "name": "Material Request Purchase Analysis", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "document_type": "Material Request", + "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), + "is_custom": 0, + "type": "Donut", + "timeseries": 0, + "chart_type": "Group By", + "group_by_based_on": "status", + "chart_name": "Material Request Purchase Analysis", + "group_by_type": "Count", + "custom_options": json.dumps({"height": 300}) + + }, + { + "name": "Purchase Order Analysis", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Order Analysis", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-04", + "to_date": "2020-07-04", + "chart_based_on": "Quantity" + }), + "is_custom": 1, + "type": "Donut", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Purchase Order Analysis", + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "name": "Requested Items to Order", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Requested Items to Order", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-01", + "to_date": "2020-07-01", + "group_by_mr": 0 + }), + "is_custom": 1, + "type": "Bar", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Requested Items to Order", + "custom_options": json.dumps({ + "type": "bar", + "barOptions": {"stacked": 1}, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + } + ] def get_number_cards(): - return [ { ... } ] \ No newline at end of file + return [{}] \ No newline at end of file From 7adca986fbecb95ad4c209838dd0042092cb78ce Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 18:40:35 +0530 Subject: [PATCH 06/11] chore: Onboarding v2 --- erpnext/buying/onboarding/buying/buying.json | 11 ++++++++++- .../buying_settings/buying_settings.json | 2 +- .../create_a_product/create_a_product.json | 16 ++++++++++++++++ .../create_a_supplier/create_a_supplier.json | 4 ++-- .../create_a_warehouse/create_a_warehouse.json | 16 ++++++++++++++++ .../create_your_first_purchase_order.json | 16 ++++++++++++++++ .../introduction_to_buying.json | 2 +- 7 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 erpnext/buying/onboarding_step/create_a_product/create_a_product.json create mode 100644 erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json create mode 100644 erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json diff --git a/erpnext/buying/onboarding/buying/buying.json b/erpnext/buying/onboarding/buying/buying.json index c35309964e7..1ee98877bd8 100644 --- a/erpnext/buying/onboarding/buying/buying.json +++ b/erpnext/buying/onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-06 16:49:17.433261", + "modified": "2020-05-12 18:32:05.085967", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -31,8 +31,17 @@ { "step": "Create a Supplier" }, + { + "step": "Create a Warehouse" + }, + { + "step": "Create a Product" + }, { "step": "Buying Settings" + }, + { + "step": "Create your first Purchase Order" } ], "subtitle": "Products, Purchases, Analysis and more.", diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json index 7274c0d62d1..3b3208f5f04 100644 --- a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -7,7 +7,7 @@ "is_complete": 0, "is_mandatory": 0, "is_skipped": 0, - "modified": "2020-05-06 16:51:56.862206", + "modified": "2020-05-12 18:30:06.323797", "modified_by": "Administrator", "name": "Buying Settings", "owner": "Administrator", diff --git a/erpnext/buying/onboarding_step/create_a_product/create_a_product.json b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json new file mode 100644 index 00000000000..dce1a215053 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:16:06.624554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:30:02.489949", + "modified_by": "Administrator", + "name": "Create a Product", + "owner": "Administrator", + "reference_document": "Item", + "title": "Create a Product" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json index 14164c33037..f87a43dbc39 100644 --- a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json @@ -5,9 +5,9 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 1, + "is_mandatory": 0, "is_skipped": 0, - "modified": "2020-05-06 17:00:41.446942", + "modified": "2020-05-12 18:25:29.121647", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", diff --git a/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json new file mode 100644 index 00000000000..20744f6624b --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:00:03.027704", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:27:44.128737", + "modified_by": "Administrator", + "name": "Create a Warehouse", + "owner": "Administrator", + "reference_document": "Warehouse", + "title": "Setup your Purchase Warehouse" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json new file mode 100644 index 00000000000..0efadbd7887 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:17:49.976035", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:31:56.856112", + "modified_by": "Administrator", + "name": "Create your first Purchase Order", + "owner": "Administrator", + "reference_document": "Purchase Order", + "title": "Create your first Purchase Order" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json index f1a027af369..73f22df48c1 100644 --- a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json +++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json @@ -7,7 +7,7 @@ "is_complete": 0, "is_mandatory": 0, "is_skipped": 0, - "modified": "2020-05-06 16:47:47.378049", + "modified": "2020-05-12 18:25:08.509900", "modified_by": "Administrator", "name": "Introduction to Buying", "owner": "Administrator", From e4fc5ba83cd3360e58d779b91d15f77c14a489ca Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 18:58:54 +0530 Subject: [PATCH 07/11] fix: Report conditional query simplification. --- .../purchase_order_analysis.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 78b86636e07..96e2fc8a209 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -36,25 +36,20 @@ def validate_filters(filters): def get_conditions(filters): conditions = "" if filters.get("from_date") and filters.get("to_date"): - conditions += " and po.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date")) + conditions += " and po.transaction_date between %(from_date)s and %(to_date)s" if filters.get("company"): - conditions += " and po.company = '{0}'".format(filters.get("company")) + conditions += " and po.company = %(company)s" if filters.get("purchase_order"): - conditions += " and po.name = '{0}'".format(filters.get("purchase_order")) + conditions += " and po.name = %(purchase_order)s" if filters.get("status"): - conditions += " and po.status in (%s)" % ', '.join(['%s']*len(filters.get("status"))) + conditions += " and po.status in %(status)s" return conditions def get_data(conditions, filters): - status = filters.get("status") - # temporary fix for dashboard chart - if status is None: - status = [] - data = frappe.db.sql(""" SELECT po.transaction_date as date, @@ -82,7 +77,7 @@ def get_data(conditions, filters): {0} GROUP BY poi.name ORDER BY po.transaction_date ASC - """.format(conditions), tuple(status), as_dict=1) + """.format(conditions), filters, as_dict=1) return data From ec1f9594fa452059231f7aa87c76a5e563edd58d Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 15:17:11 +0530 Subject: [PATCH 08/11] fix: Refined Onboarding, desk cards and renamed field in Buying Settings --- erpnext/buying/desk_page/buying/buying.json | 43 ++++++--------- .../buying_settings/buying_settings.js | 23 ++++++++ .../buying_settings/buying_settings.json | 13 +++-- .../module_onboarding/buying/buying.json | 54 +++++++++++++++++++ .../buying_settings/buying_settings.json | 11 ++-- .../create_a_material_request.json | 19 +++++++ .../create_a_product/create_a_product.json | 5 +- .../create_a_supplier/create_a_supplier.json | 5 +- .../create_a_warehouse.json | 7 ++- .../create_your_first_purchase_order.json | 5 +- .../introduction_to_buying.json | 3 ++ 11 files changed, 148 insertions(+), 40 deletions(-) create mode 100644 erpnext/buying/module_onboarding/buying/buying.json create mode 100644 erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 9749f3c97fc..e00db0e86d7 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\": \"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 \"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\": \"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]" } ], "cards_label": "Masters & Reports ", @@ -54,7 +54,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-06 18:10:12.760321", + "modified": "2020-05-15 14:26:42.505702", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -64,11 +64,11 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#ffe8cd", - "format": "{} to Receive", - "label": "Purchase Order", - "link_to": "Purchase Order", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", + "color": "#cef6d1", + "format": "{} available", + "label": "Item", + "link_to": "Item", + "stats_filter": "{\n \"disabled\": 0\n}", "type": "DocType" }, { @@ -81,31 +81,20 @@ }, { "color": "#ffe8cd", - "format": "{} to Bill ", - "label": "Purchase Receipt", - "link_to": "Purchase Receipt", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", + "format": "{} to Receive", + "label": "Purchase Order", + "link_to": "Purchase Order", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", "type": "DocType" }, { - "color": "#ffe8cd", - "format": "{} Unpaid / Overdue", - "label": "Purchase Invoice", - "link_to": "Purchase Invoice", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": [\"in\", [\"Unpaid\", \"Overdue\"]]\n}", - "type": "DocType" + "label": "Purchase Analytics", + "link_to": "Purchase Analytics", + "type": "Report" }, { - "color": "#cef6d1", - "format": "{} Active", - "label": "Supplier Quotation", - "link_to": "Supplier Quotation", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"!=\", \"Expired\"]\n}", - "type": "DocType" - }, - { - "label": "Item-wise Purchase Register", - "link_to": "Item-wise Purchase Register", + "label": "Purchase Order Analysis", + "link_to": "Purchase Order Analysis", "type": "Report" } ], diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index 403b1c95e74..5a5afa41fcd 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -6,3 +6,26 @@ frappe.ui.form.on('Buying Settings', { // } }); + +frappe.tour['Buying Settings'] = [ + { + fieldname: "supp_master_name", + title: "Supplier Naming By", + description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "Naming Series" + __(" choose the 'Naming Series' option."), + }, + { + fieldname: "buying_price_list", + title: "Default Buying Price List", + description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List. You can modify the 'Price List' by using the arrow at the right-end of the field to change the currency and country.") + }, + { + fieldname: "po_required", + title: "Purchase Order Required for Purchase Invoice & Receipt Creation", + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in supplier master.") + }, + { + fieldname: "pr_required", + title: "Purchase Receipt Required for Purchase Invoice Creation", + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in supplier master.") + } +]; \ No newline at end of file diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index a492519591b..a0ab2a00f99 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -1,8 +1,10 @@ { + "actions": [], "creation": "2013-06-25 11:04:03", "description": "Settings for Buying Module", "doctype": "DocType", "document_type": "Other", + "engine": "InnoDB", "field_order": [ "supp_master_name", "supplier_group", @@ -44,13 +46,13 @@ { "fieldname": "po_required", "fieldtype": "Select", - "label": "Purchase Order Required", + "label": "Purchase Order Required for Purchase Invoice & Receipt Creation", "options": "No\nYes" }, { "fieldname": "pr_required", "fieldtype": "Select", - "label": "Purchase Receipt Required", + "label": "Purchase Receipt Required for Purchase Invoice Creation", "options": "No\nYes" }, { @@ -92,7 +94,8 @@ "icon": "fa fa-cog", "idx": 1, "issingle": 1, - "modified": "2019-08-20 13:13:09.055189", + "links": [], + "modified": "2020-05-15 14:49:32.513611", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", @@ -107,5 +110,7 @@ "share": 1, "write": 1 } - ] + ], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json new file mode 100644 index 00000000000..fc956baaa3b --- /dev/null +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-06 15:56:35.049205", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-15 14:39:16.514904", + "modified_by": "Administrator", + "module": "Buying", + "name": "Buying", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Buying" + }, + { + "step": "Create a Supplier" + }, + { + "step": "Create a Warehouse" + }, + { + "step": "Create a Product" + }, + { + "step": "Buying Settings" + }, + { + "step": "Create your first Purchase Order" + }, + { + "step": "Create a Material Request" + } + ], + "subtitle": "Products, Purchases, Analysis and more.", + "success_message": "The Buying Module is all set up!", + "title": "Let's Setup the Buying Module.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json index 3b3208f5f04..45a19fb4a18 100644 --- a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -1,16 +1,19 @@ { - "action": "Update Settings", + "action": "Show Form Tour", "creation": "2020-05-06 15:53:44.667414", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, + "is_single": 1, "is_skipped": 0, - "modified": "2020-05-12 18:30:06.323797", + "modified": "2020-05-15 14:38:01.142256", "modified_by": "Administrator", "name": "Buying Settings", "owner": "Administrator", "reference_document": "Buying Settings", - "title": "Configure Buying Settings." + "show_full_form": 0, + "title": "Explore Buying Settings.", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json new file mode 100644 index 00000000000..9dc493dd499 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-15 14:39:09.818764", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 14:39:09.818764", + "modified_by": "Administrator", + "name": "Create a Material Request", + "owner": "Administrator", + "reference_document": "Material Request", + "show_full_form": 1, + "title": "Create a Material Request", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_product/create_a_product.json b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json index dce1a215053..d2068e167b7 100644 --- a/erpnext/buying/onboarding_step/create_a_product/create_a_product.json +++ b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:30:02.489949", "modified_by": "Administrator", "name": "Create a Product", "owner": "Administrator", "reference_document": "Item", - "title": "Create a Product" + "show_full_form": 0, + "title": "Create a Product", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json index f87a43dbc39..7bbbc928418 100644 --- a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:25:29.121647", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", "reference_document": "Supplier", - "title": "Create a Supplier" + "show_full_form": 0, + "title": "Create a Supplier", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json index 20744f6624b..8aac6d4cf48 100644 --- a/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json +++ b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 18:27:44.128737", + "modified": "2020-05-15 14:32:17.072731", "modified_by": "Administrator", "name": "Create a Warehouse", "owner": "Administrator", "reference_document": "Warehouse", - "title": "Setup your Purchase Warehouse" + "show_full_form": 0, + "title": "Setup your Warehouse", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json index 0efadbd7887..9dbed239789 100644 --- a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json +++ b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:31:56.856112", "modified_by": "Administrator", "name": "Create your first Purchase Order", "owner": "Administrator", "reference_document": "Purchase Order", - "title": "Create your first Purchase Order" + "show_full_form": 0, + "title": "Create your first Purchase Order", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json index 73f22df48c1..fd98fddafae 100644 --- a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json +++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:25:08.509900", "modified_by": "Administrator", "name": "Introduction to Buying", "owner": "Administrator", + "show_full_form": 0, "title": "Introduction to Buying", + "validate_action": 1, "video_url": "https://youtu.be/efFajTTQBa8" } \ No newline at end of file From a74d433cd81661120b4bcb5f9099ce2d3b25fbde Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 23:37:48 +0530 Subject: [PATCH 09/11] fix: Fixed Dashboard Charts, modified fixtures and minor changes --- erpnext/buying/dashboard_fixtures.py | 217 ++++++++++++------ .../buying_settings/buying_settings.js | 2 +- .../supplier_quotation.json | 4 +- .../purchase_order_analysis.js | 8 - .../purchase_order_analysis.json | 4 +- .../purchase_order_analysis.py | 13 +- .../purchase_order_trends.py | 43 +++- erpnext/public/js/purchase_trends_filters.js | 5 +- 8 files changed, 198 insertions(+), 98 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 0bd9a1fd488..291abb87b04 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -3,7 +3,8 @@ import frappe import json - +from frappe.utils import nowdate +from erpnext.accounts.utils import get_fiscal_year def get_data(): return frappe._dict({ @@ -23,108 +24,172 @@ def get_company_for_dashboards(): return None company = frappe.get_doc("Company", get_company_for_dashboards()) +fiscal_year = get_fiscal_year(nowdate(), as_dict=1) +fiscal_year_name = fiscal_year.get("name") +start_date = str(fiscal_year.get("year_start_date")) +end_date = str(fiscal_year.get("year_end_date")) def get_dashboards(): return [{ "name": "Buying", "dashboard_name": "Buying", "charts": [ - { "chart": "Purchase Analytics", "width": "Full"}, - { "chart": "Material Request Purchase Analysis", "width": "Half"}, + { "chart": "Top Suppliers", "width": "Full"}, + { "chart": "Material Request Analysis", "width": "Half"}, { "chart": "Purchase Order Analysis", "width": "Half"}, - { "chart": "Requested Items to Order", "width": "Full"} + { "chart": "Purchase Order Trends", "width": "Full"} + ], + "cards": [ + { "card": "Purchase Orders to Receive"}, + { "card": "Purchase Order Expenses"}, + { "card": "Active Suppliers"}, + { "card": "Active Supplier Quotations"} ] }] def get_charts(): return [ - { - "name": "Purchase Analytics", - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Analytics", - "filters_json": json.dumps({ - "tree_type": "Item", - "doc_type": "Purchase Receipt", - "value_quantity": "Value", - "from_date": "2020-03-01", - "to_date": "2020-07-31", - "company": company.name, - "range": "Weekly" - }), - "x_field": "entity", - "type": "Bar", - 'timeseries': 0, - "chart_type": "Report", - "chart_name": "Purchase Analytics", - "custom_options": json.dumps({ - "x_field": "entity", - "chart_type": "Bar", - "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], - "y_fields": ["total"] - }) - }, - { - "name": "Material Request Purchase Analysis", - "doctype": "Dashboard Chart", - "owner": "Administrator", - "document_type": "Material Request", - "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), - "is_custom": 0, - "type": "Donut", - "timeseries": 0, - "chart_type": "Group By", - "group_by_based_on": "status", - "chart_name": "Material Request Purchase Analysis", - "group_by_type": "Count", - "custom_options": json.dumps({"height": 300}) - - }, { "name": "Purchase Order Analysis", - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Order Analysis", - "filters_json": json.dumps({ - "company": company.name, - "from_date": "2020-04-04", - "to_date": "2020-07-04", - "chart_based_on": "Quantity" - }), - "is_custom": 1, - "type": "Donut", - "timeseries": 0, - "chart_type": "Report", "chart_name": "Purchase Order Analysis", + "chart_type": "Report", "custom_options": json.dumps({ "type": "donut", "height": 300, "axisOptions": {"shortenYAxisNumbers": 1} - }) - }, - { - "name": "Requested Items to Order", + }), "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Requested Items to Order", "filters_json": json.dumps({ "company": company.name, - "from_date": "2020-04-01", - "to_date": "2020-07-01", - "group_by_mr": 0 + "from_date": start_date, + "to_date": end_date }), "is_custom": 1, - "type": "Bar", - "timeseries": 0, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Order Analysis", + "type": "Donut" + }, + { + "name": "Material Request Analysis", + "chart_name": "Material Request Analysis", + "chart_type": "Group By", + "custom_options": json.dumps({"height": 300}), + "doctype": "Dashboard Chart", + "document_type": "Material Request", + "filters_json": json.dumps( + [["Material Request", "status", "not in", ["Draft", "Cancelled", "Stopped", None], False], + ["Material Request", "material_request_type", "=", "Purchase", False], + ["Material Request", "company", "=", company.name, False]] + ), + "group_by_based_on": "status", + "group_by_type": "Count", + "is_custom": 0, + "is_public": 1, + "number_of_groups": 0, + "owner": "Administrator", + "type": "Donut" + }, + { + "name": "Purchase Order Trends", + "chart_name": "Purchase Order Trends", "chart_type": "Report", - "chart_name": "Requested Items to Order", "custom_options": json.dumps({ - "type": "bar", - "barOptions": {"stacked": 1}, - "axisOptions": {"shortenYAxisNumbers": 1} - }) + "type": "line", + "regionFill": 1, + "axisOptions": {"shortenYAxisNumbers": 1}, + "tooltipOptions": {} + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "period": "Monthly", + "fiscal_year": fiscal_year_name, + "period_based_on": "posting_date", + "based_on": "Item" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Order Trends", + "type": "Line" + }, + { + "name": "Top Suppliers", + "chart_name": "Top Suppliers", + "chart_type": "Report", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "period": "Monthly", + "fiscal_year": fiscal_year_name, + "period_based_on": "posting_date", + "based_on": "Supplier" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Receipt Trends", + "type": "Bar" } ] def get_number_cards(): - return [{}] \ No newline at end of file + return [ + { + "name": "Purchase Order Expenses", + "aggregate_function_based_on": "base_grand_total", + "doctype": "Number Card", + "document_type": "Purchase Order", + "filters_json": json.dumps( + [["Purchase Order", "transaction_date", "Between", [start_date,end_date], False], + ["Purchase Order", "status", "not in", ["Draft","On Hold","Cancelled","Closed", None], False], + ["Purchase Order", "company", "=", company.name, False]] + ), + "function": "Sum", + "is_public": 1, + "label": "Purchase Order Expenses", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "name": "Purchase Orders to Receive", + "doctype": "Number Card", + "document_type": "Purchase Order", + "filters_json": json.dumps( + [["Purchase Order", "status", "in", ["To Receive and Bill", "To Receive", None], False], + ["Purchase Order", "company", "=", company.name, False]] + ), + "function": "Count", + "is_public": 1, + "label": "Purchase Orders to Receive", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Weekly" + }, + { + "name": "Active Suppliers", + "doctype": "Number Card", + "document_type": "Supplier", + "filters_json": json.dumps([["Supplier", "disabled", "=", "0"]]), + "function": "Count", + "is_public": 1, + "label": "Active Suppliers", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "name": "Active Supplier Quotations", + "doctype": "Number Card", + "document_type": "Supplier Quotation", + "filters_json": json.dumps([["Supplier Quotation", "status", "=", "Submitted", False]]), + "function": "Count", + "is_public": 1, + "label": "Active Supplier Quotations", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + } + ] \ No newline at end of file diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index 5a5afa41fcd..a27950a9414 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -16,7 +16,7 @@ frappe.tour['Buying Settings'] = [ { fieldname: "buying_price_list", title: "Default Buying Price List", - description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List. You can modify the 'Price List' by using the arrow at the right-end of the field to change the currency and country.") + description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List.") }, { fieldname: "po_required", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 3bc441af6d1..7db1516ce1b 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -761,7 +761,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nDraft\nSubmitted\nStopped\nCancelled", + "options": "\nDraft\nSubmitted\nStopped\nCancelled\nExpired", "print_hide": 1, "read_only": 1, "reqd": 1, @@ -803,7 +803,7 @@ "idx": 29, "is_submittable": 1, "links": [], - "modified": "2020-04-15 11:44:52.958022", + "modified": "2020-05-15 21:24:12.639482", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js index 24abb6d44ab..701da4380aa 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js @@ -58,14 +58,6 @@ frappe.query_reports["Purchase Order Analysis"] = { return options } }, - { - "fieldname": "chart_based_on", - "label": __("Chart Based On"), - "fieldtype": "Select", - "width": "80", - "options": "Quantity\nAmount", - "default": "Quantity" - }, { "fieldname": "group_by_po", "label": __("Group by Purchase Order"), diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json index 196aaaed224..5ba3101ec51 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "creation": "2020-05-04 18:41:28.625119", "disable_prepared_report": 0, "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2020-05-04 18:41:28.625119", + "modified": "2020-05-15 20:57:52.623455", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Analysis", diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 96e2fc8a209..497ce684fd6 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -82,10 +82,9 @@ def get_data(conditions, filters): return data def prepare_data(data, filters): - completed, pending = 0,0 - chart_based_on = filters.get("chart_based_on") - pending_field = "pending_qty" if chart_based_on == "Quantity" else "pending_amount" - completed_field = "received_qty" if chart_based_on == "Quantity" else "billed_amount" + completed, pending = 0, 0 + pending_field = "pending_amount" + completed_field = "billed_amount" if filters.get("group_by_po"): purchase_order_map = {} @@ -116,7 +115,7 @@ def prepare_data(data, filters): for field in fields: po_row[field] = flt(row[field]) + flt(po_row[field]) - chart_data = prepare_chart_data(chart_based_on, pending, completed) + chart_data = prepare_chart_data(pending, completed) if filters.get("group_by_po"): data = [] @@ -126,8 +125,8 @@ def prepare_data(data, filters): return data, chart_data -def prepare_chart_data(chart_based_on, pending, completed): - labels = ["Qty to Receive","Received Qty"] if chart_based_on == "Quantity" else ["Amount to Bill","Billed Amount"] +def prepare_chart_data(pending, completed): + labels = ["Amount to Bill", "Billed Amount"] return { "data" : { diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py index 888676cf64f..011760dacc3 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns,get_data def execute(filters=None): @@ -10,5 +11,45 @@ def execute(filters=None): data = [] conditions = get_columns(filters, "Purchase Order") data = get_data(filters, conditions) + chart_data = get_chart_data(data, conditions, filters) - return conditions["columns"], data \ No newline at end of file + return conditions["columns"], data, None, chart_data + +def get_chart_data(data, conditions, filters): + if not (data and conditions): + return [] + + datapoints = [] + + start = 2 if filters.get("based_on") in ["Item", "Supplier"] else 1 + if filters.get("group_by"): + start += 1 + + # fetch only periodic columns as labels + columns = conditions.get("columns")[start:-2][1::2] + labels = [column.split(':')[0] for column in columns] + datapoints = [0] * len(labels) + + for row in data: + # If group by filter, don't add first row of group (it's already summed) + if not row[start-1]: + continue + # Remove None values and compute only periodic data + row = [x if x else 0 for x in row[start:-2]] + row = row[1::2] + + for i in range(len(row)): + datapoints[i] += row[i] + + return { + "data" : { + "labels" : labels, + "datasets" : [ + { + "name" : _("{0}").format(filters.get("period")) + _(" Revenue"), + "values" : datapoints + } + ] + }, + "type" : "line" + } \ No newline at end of file diff --git a/erpnext/public/js/purchase_trends_filters.js b/erpnext/public/js/purchase_trends_filters.js index cd767f5d167..c786a8674e6 100644 --- a/erpnext/public/js/purchase_trends_filters.js +++ b/erpnext/public/js/purchase_trends_filters.js @@ -51,7 +51,10 @@ erpnext.get_purchase_trends_filters = function() { { "value": "Supplier Group", "label": __("Supplier Group") }, { "value": "Project", "label": __("Project") } ], - "default": "Item" + "default": "Item", + "dashboard_config": { + "read_only": 1 + } }, { "fieldname":"group_by", From 75c65fd1b1eb6ee79858ad75f3bd5a4aa8eec6bb Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 23:47:51 +0530 Subject: [PATCH 10/11] fix: Handle empty chart state and change desk dashboard --- erpnext/buying/desk_page/buying/buying.json | 6 +++--- erpnext/buying/module_onboarding/buying/buying.json | 2 +- .../purchase_order_analysis/purchase_order_analysis.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index e00db0e86d7..1a69f422695 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -40,8 +40,8 @@ "category": "Modules", "charts": [ { - "chart_name": "Purchase Analytics", - "label": "Buying Analytics" + "chart_name": "Purchase Order Trends", + "label": "Purchase Order Trends" } ], "charts_label": "Buying Dashboard", @@ -54,7 +54,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-15 14:26:42.505702", + "modified": "2020-05-15 23:41:09.307288", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index fc956baaa3b..70d6cbb77a4 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-15 14:39:16.514904", + "modified": "2020-05-15 23:42:35.625736", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 497ce684fd6..89be62231b9 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -19,7 +19,7 @@ def execute(filters=None): data = get_data(conditions, filters) if not data: - return [], [] + return [], [], None, [] data, chart_data = prepare_data(data, filters) From 50d4bf578b584475399029f42c075e330a4e6dd3 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 19 May 2020 17:02:47 +0530 Subject: [PATCH 11/11] fix: Date filters added to dashboards, number cards and minor fixes. --- erpnext/buying/dashboard_fixtures.py | 19 ++++++++++++------- .../module_onboarding/buying/buying.json | 2 +- .../purchase_order_trends.py | 7 +++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 291abb87b04..abb858f1f66 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -34,10 +34,10 @@ def get_dashboards(): "name": "Buying", "dashboard_name": "Buying", "charts": [ - { "chart": "Top Suppliers", "width": "Full"}, + { "chart": "Purchase Order Trends", "width": "Full"}, { "chart": "Material Request Analysis", "width": "Half"}, { "chart": "Purchase Order Analysis", "width": "Half"}, - { "chart": "Purchase Order Trends", "width": "Full"} + { "chart": "Top Suppliers", "width": "Full"} ], "cards": [ { "card": "Purchase Orders to Receive"}, @@ -80,7 +80,8 @@ def get_charts(): "filters_json": json.dumps( [["Material Request", "status", "not in", ["Draft", "Cancelled", "Stopped", None], False], ["Material Request", "material_request_type", "=", "Purchase", False], - ["Material Request", "company", "=", company.name, False]] + ["Material Request", "company", "=", company.name, False], + ["Material Request", "transaction_date", "Between", [start_date,end_date], False]] ), "group_by_based_on": "status", "group_by_type": "Count", @@ -96,9 +97,11 @@ def get_charts(): "chart_type": "Report", "custom_options": json.dumps({ "type": "line", - "regionFill": 1, "axisOptions": {"shortenYAxisNumbers": 1}, - "tooltipOptions": {} + "tooltipOptions": {}, + "lineOptions": { + "regionFill": 1 + } }), "doctype": "Dashboard Chart", "filters_json": json.dumps({ @@ -144,7 +147,8 @@ def get_number_cards(): "filters_json": json.dumps( [["Purchase Order", "transaction_date", "Between", [start_date,end_date], False], ["Purchase Order", "status", "not in", ["Draft","On Hold","Cancelled","Closed", None], False], - ["Purchase Order", "company", "=", company.name, False]] + ["Purchase Order", "company", "=", company.name, False], + ["Purchase Order", "transaction_date", "Between", [start_date,end_date], False]] ), "function": "Sum", "is_public": 1, @@ -159,7 +163,8 @@ def get_number_cards(): "document_type": "Purchase Order", "filters_json": json.dumps( [["Purchase Order", "status", "in", ["To Receive and Bill", "To Receive", None], False], - ["Purchase Order", "company", "=", company.name, False]] + ["Purchase Order", "company", "=", company.name, False], + ["Purchase Order", "transaction_date", "Between", [start_date,end_date], False]] ), "function": "Count", "is_public": 1, diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index 70d6cbb77a4..7de44f087fc 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-15 23:42:35.625736", + "modified": "2020-05-19 15:37:50.111851", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py index 011760dacc3..abe9af979ce 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py @@ -46,10 +46,13 @@ def get_chart_data(data, conditions, filters): "labels" : labels, "datasets" : [ { - "name" : _("{0}").format(filters.get("period")) + _(" Revenue"), + "name" : _("{0}").format(filters.get("period")) + _(" Expenditure"), "values" : datapoints } ] }, - "type" : "line" + "type" : "line", + "lineOptions": { + "regionFill": 1 + } } \ No newline at end of file