From 508e815db007c650ef9bfd015fe4aec8e8128b60 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 16 Apr 2019 17:07:13 +0530 Subject: [PATCH 1/6] feat: Added filters and columns for inactive items report --- .../stock/report/inactive_items/__init__.py | 0 .../report/inactive_items/inactive_items.js | 39 ++++++++++++ .../report/inactive_items/inactive_items.json | 31 ++++++++++ .../report/inactive_items/inactive_items.py | 60 +++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 erpnext/stock/report/inactive_items/__init__.py create mode 100644 erpnext/stock/report/inactive_items/inactive_items.js create mode 100644 erpnext/stock/report/inactive_items/inactive_items.json create mode 100644 erpnext/stock/report/inactive_items/inactive_items.py diff --git a/erpnext/stock/report/inactive_items/__init__.py b/erpnext/stock/report/inactive_items/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/report/inactive_items/inactive_items.js b/erpnext/stock/report/inactive_items/inactive_items.js new file mode 100644 index 00000000000..73ce59b977d --- /dev/null +++ b/erpnext/stock/report/inactive_items/inactive_items.js @@ -0,0 +1,39 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Inactive Items"] = { + "filters": [ + { + fieldname: "territory", + label: __("Territory"), + fieldtype: "Link", + options: "Territory" + }, + { + fieldname: "customer", + label: __("Customer"), + fieldtype: "Link", + options: "Customer" + }, + { + fieldname: "item", + label: __("Item"), + fieldtype: "Link", + options: "Item" + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group" + }, + { + fieldname: "days", + label: __("Days Since Last order"), + fieldtype: "Select", + options: [30, 60, 90], + default: 30 + }, + ] +} diff --git a/erpnext/stock/report/inactive_items/inactive_items.json b/erpnext/stock/report/inactive_items/inactive_items.json new file mode 100644 index 00000000000..b9eb05ec050 --- /dev/null +++ b/erpnext/stock/report/inactive_items/inactive_items.json @@ -0,0 +1,31 @@ +{ + "add_total_row": 0, + "creation": "2019-04-16 16:05:00.647308", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "Test Letter Head 1", + "modified": "2019-04-16 16:06:33.630043", + "modified_by": "Administrator", + "module": "Stock", + "name": "Inactive Items", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Invoice", + "report_name": "Inactive Items", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py new file mode 100644 index 00000000000..8aeb24349a7 --- /dev/null +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -0,0 +1,60 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe + +def execute(filters=None): + columns, data = [], [] + return columns, data + +def get_columns(): + + columns = [ + { + "fieldname": "territory", + "fieldtype": "Link", + "label": _("Territory"), + "options": "Territory", + "width": 100 + }, + { + "fieldname": "item_group", + "fieldtype": "Link", + "label": _("Item Group"), + "options": "Item Group", + "width": 100 + }, + { + "fieldname": "item", + "fieldtype": "Link", + "label": _("Item"), + "options": "Item", + "width": 100 + }, + { + "fieldname": "customer", + "fieldtype": "Link", + "label": _("Customer"), + "options": "Customer", + "width": 100 + }, + { + "fieldname": "last_order_date", + "fieldtype": "Date", + "label": _("Last Order Date"), + "width": 100 + }, + { + "fieldname": "qty", + "fieldtype": "Float", + "label": _("Quantity"), + "width": 100 + }, + { + "fieldname": "days_since_last_order", + "fieldtype": "Int", + "label": _("Days Since Last Order"), + "width": 100 + }, + ] From 4d1df416e9b807cfa2aac945437f9e9d066b7c5d Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 18 Apr 2019 08:29:21 +0530 Subject: [PATCH 2/6] fix: Reordered and deleted unnecessary filters --- .../report/inactive_items/inactive_items.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/report/inactive_items/inactive_items.js b/erpnext/stock/report/inactive_items/inactive_items.js index 73ce59b977d..39dfd5c8c36 100644 --- a/erpnext/stock/report/inactive_items/inactive_items.js +++ b/erpnext/stock/report/inactive_items/inactive_items.js @@ -4,18 +4,6 @@ frappe.query_reports["Inactive Items"] = { "filters": [ - { - fieldname: "territory", - label: __("Territory"), - fieldtype: "Link", - options: "Territory" - }, - { - fieldname: "customer", - label: __("Customer"), - fieldtype: "Link", - options: "Customer" - }, { fieldname: "item", label: __("Item"), @@ -28,6 +16,13 @@ frappe.query_reports["Inactive Items"] = { fieldtype: "Link", options: "Item Group" }, + { + fieldname: "based_on", + label: __("Based On"), + fieldtype: "Select", + options: "Sales Order\nSales Invoice", + default: "Sales Order" + }, { fieldname: "days", label: __("Days Since Last order"), From 767a00fa8d3026192a019612d5bb32f7d2faa458 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 18 Apr 2019 08:45:10 +0530 Subject: [PATCH 3/6] feat: Logic for query and report creation for inactive items --- .../report/inactive_items/inactive_items.py | 95 ++++++++++++++++++- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py index 8aeb24349a7..95cec7bb348 100644 --- a/erpnext/stock/report/inactive_items/inactive_items.py +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -3,9 +3,13 @@ from __future__ import unicode_literals import frappe +from frappe.utils import getdate, add_days, today, cint +from frappe import _ def execute(filters=None): - columns, data = [], [] + + columns = get_columns() + data = get_data(filters) return columns, data def get_columns(): @@ -26,12 +30,20 @@ def get_columns(): "width": 100 }, { - "fieldname": "item", + "fieldname": "item_name", "fieldtype": "Link", - "label": _("Item"), + "options": "Item", + "label": "Item", + "width": 100 + }, + { + "fieldname": "Item Name", + "fieldtype": "Link", + "label": _("Item Name"), "options": "Item", "width": 100 }, + { "fieldname": "customer", "fieldtype": "Link", @@ -58,3 +70,80 @@ def get_columns(): "width": 100 }, ] + + return columns + + +def get_data(filters): + + data = [] + items = get_items(filters) + sales_invoice_data = get_sales_details(filters) + + for item in items: + if sales_invoice_data.get(item.name): + item_obj = sales_invoice_data[item.name] + if item_obj.days_since_last_order > cint(filters['days']): + row = { + "territory": item_obj.territory, + "item_group": item_obj.item_group, + "item": item_obj.name, + "item_name": item_obj.item_name, + "customer": item_obj.customer, + "last_order_date": item_obj.last_order_date, + "qty": item_obj.qty, + "days_since_last_order": item_obj.days_since_last_order + } + data.append(row) + else: + row = { + "item_group": item.item_group, + "item": item.name, + "item_name": item.item_name + } + data.append(row) + + return data + + +def get_sales_details(filters): + + data = [] + item_details_map = {} + + date_field = "s.transaction_date" if filters["based_on"] == "Sales Order" else "s.posting_date" + + sales_data = frappe.db.sql(""" + select s.territory, s.customer, si.item_group, si.item_name, si.qty, {date_field} as last_order_date, + DATEDIFF(CURDATE(), {date_field}) as days_since_last_order + from `tab{doctype}` s, `tab{doctype} Item` si + where s.name = si.parent and s.docstatus = 1 + group by si.name order by days_since_last_order """ + .format(date_field = date_field, doctype = filters['based_on']), as_dict=1) + + for d in sales_data: + item_details_map.setdefault(d.item_name, d) + + return item_details_map + +def get_items(filters): + + filters_dict = { + "disabled": 0, + "is_stock_item": 1 + } + + if filters.get("item_group"): + filters_dict.update({ + "item_group": filters["item_group"] + }) + + if filters.get("item"): + filters_dict.update({ + "name": filtesr["item"] + }) + + items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict) + + return items + From bf2adae97ddd186c456c5578e537945117bcaef8 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 22 Apr 2019 17:15:23 +0530 Subject: [PATCH 4/6] fix: Ordering and datatype fixes in inactive items report --- .../stock/report/inactive_items/inactive_items.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py index 95cec7bb348..3aaf1ce7090 100644 --- a/erpnext/stock/report/inactive_items/inactive_items.py +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -27,21 +27,20 @@ def get_columns(): "fieldtype": "Link", "label": _("Item Group"), "options": "Item Group", - "width": 100 + "width": 150 }, { "fieldname": "item_name", "fieldtype": "Link", "options": "Item", "label": "Item", - "width": 100 + "width": 150 }, { - "fieldname": "Item Name", - "fieldtype": "Link", + "fieldname": "item_name", + "fieldtype": "Data", "label": _("Item Name"), - "options": "Item", - "width": 100 + "width": 150 }, { @@ -143,7 +142,7 @@ def get_items(filters): "name": filtesr["item"] }) - items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict) + items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict, order_by="name") return items From a89d1df655d6894c30d56ad33996b05730110557 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 22 Apr 2019 17:54:38 +0530 Subject: [PATCH 5/6] fix: Typo fixes --- erpnext/stock/report/inactive_items/inactive_items.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py index 3aaf1ce7090..188f59bf09e 100644 --- a/erpnext/stock/report/inactive_items/inactive_items.py +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -139,7 +139,7 @@ def get_items(filters): if filters.get("item"): filters_dict.update({ - "name": filtesr["item"] + "name": filters["item"] }) items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict, order_by="name") From e3ea8063905a6c188bee036253b788665551770d Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 22 Apr 2019 21:08:29 +0530 Subject: [PATCH 6/6] fix: Ignore sql injections --- erpnext/stock/report/inactive_items/inactive_items.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py index 188f59bf09e..8d879126da6 100644 --- a/erpnext/stock/report/inactive_items/inactive_items.py +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -117,7 +117,7 @@ def get_sales_details(filters): DATEDIFF(CURDATE(), {date_field}) as days_since_last_order from `tab{doctype}` s, `tab{doctype} Item` si where s.name = si.parent and s.docstatus = 1 - group by si.name order by days_since_last_order """ + group by si.name order by days_since_last_order """ #nosec .format(date_field = date_field, doctype = filters['based_on']), as_dict=1) for d in sales_data: