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 5e764cf8bb..432f3c557b 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 0000000000..e69de29bb2 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 0000000000..24abb6d44a --- /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 0000000000..196aaaed22 --- /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 0000000000..78b86636e0 --- /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 0000000000..e69de29bb2 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 0000000000..21adb13547 --- /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 0000000000..4a0578be4b --- /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 0000000000..a021d3c1ca --- /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 432f3c557b..9749f3c97f 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 0000000000..c35309964e --- /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 0000000000..7274c0d62d --- /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 0000000000..14164c3303 --- /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 0000000000..f1a027af36 --- /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 b182dfc103..8d29397390 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 1d4054786e..16b49a1e57 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 8d29397390..9c654a829d 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 0000000000..199765bace --- /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 9c654a829d..88d42590fc 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 199765bace..0bd9a1fd48 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 c35309964e..1ee98877bd 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 7274c0d62d..3b3208f5f0 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 0000000000..dce1a21505 --- /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 14164c3303..f87a43dbc3 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 0000000000..20744f6624 --- /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 0000000000..0efadbd788 --- /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 f1a027af36..73f22df48c 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 78b86636e0..96e2fc8a20 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 9749f3c97f..e00db0e86d 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 403b1c95e7..5a5afa41fc 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 a492519591..a0ab2a00f9 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 0000000000..fc956baaa3 --- /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 3b3208f5f0..45a19fb4a1 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 0000000000..9dc493dd49 --- /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 dce1a21505..d2068e167b 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 f87a43dbc3..7bbbc92841 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 20744f6624..8aac6d4cf4 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 0efadbd788..9dbed23978 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 73f22df48c..fd98fddafa 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 0bd9a1fd48..291abb87b0 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 5a5afa41fc..a27950a941 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 3bc441af6d..7db1516ce1 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 24abb6d44a..701da4380a 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 196aaaed22..5ba3101ec5 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 96e2fc8a20..497ce684fd 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 888676cf64..011760dacc 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 cd767f5d16..c786a8674e 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 e00db0e86d..1a69f42269 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 fc956baaa3..70d6cbb77a 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 497ce684fd..89be62231b 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 291abb87b0..abb858f1f6 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 70d6cbb77a..7de44f087f 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 011760dacc..abe9af979c 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