mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-14 18:51:21 +00:00
feat: Delayed Tasks Summary (#25024)
* feat: delayed deliverables summary * fix: sider * fix: renamed to delayed tasks * fix: renamed test * fix: test * fix: sider * fix: dates, validations and chart * fix: space and column width * feat: Sort tasks by descending order of delay Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Delayed Tasks Summary"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "priority",
|
||||
"label": __("Priority"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["", "Low", "Medium", "High", "Urgent"]
|
||||
},
|
||||
{
|
||||
"fieldname": "status",
|
||||
"label": __("Status"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["", "Open", "Working","Pending Review","Overdue","Completed"]
|
||||
},
|
||||
],
|
||||
"formatter": function(value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
if (column.id == "delay") {
|
||||
if (data["delay"] > 0) {
|
||||
value = `<p style="color: red; font-weight: bold">${value}</p>`;
|
||||
} else {
|
||||
value = `<p style="color: green; font-weight: bold">${value}</p>`;
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"columns": [],
|
||||
"creation": "2021-03-25 15:03:19.857418",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2021-04-15 15:49:35.432486",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Delayed Tasks Summary",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Task",
|
||||
"report_name": "Delayed Tasks Summary",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Projects User"
|
||||
},
|
||||
{
|
||||
"role": "Projects Manager"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import date_diff, nowdate
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = [], []
|
||||
data = get_data(filters)
|
||||
columns = get_columns()
|
||||
charts = get_chart_data(data)
|
||||
return columns, data, None, charts
|
||||
|
||||
def get_data(filters):
|
||||
conditions = get_conditions(filters)
|
||||
tasks = frappe.get_all("Task",
|
||||
filters = conditions,
|
||||
fields = ["name", "subject", "exp_start_date", "exp_end_date",
|
||||
"status", "priority", "completed_on", "progress"],
|
||||
order_by="creation"
|
||||
)
|
||||
for task in tasks:
|
||||
if task.exp_end_date:
|
||||
if task.completed_on:
|
||||
task.delay = date_diff(task.completed_on, task.exp_end_date)
|
||||
elif task.status == "Completed":
|
||||
# task is completed but completed on is not set (for older tasks)
|
||||
task.delay = 0
|
||||
else:
|
||||
# task not completed
|
||||
task.delay = date_diff(nowdate(), task.exp_end_date)
|
||||
else:
|
||||
# task has no end date, hence no delay
|
||||
task.delay = 0
|
||||
|
||||
# Sort by descending order of delay
|
||||
tasks.sort(key=lambda x: x["delay"], reverse=True)
|
||||
return tasks
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = frappe._dict()
|
||||
keys = ["priority", "status"]
|
||||
for key in keys:
|
||||
if filters.get(key):
|
||||
conditions[key] = filters.get(key)
|
||||
if filters.get("from_date"):
|
||||
conditions.exp_end_date = [">=", filters.get("from_date")]
|
||||
if filters.get("to_date"):
|
||||
conditions.exp_start_date = ["<=", filters.get("to_date")]
|
||||
return conditions
|
||||
|
||||
def get_chart_data(data):
|
||||
delay, on_track = 0, 0
|
||||
for entry in data:
|
||||
if entry.get("delay") > 0:
|
||||
delay = delay + 1
|
||||
else:
|
||||
on_track = on_track + 1
|
||||
charts = {
|
||||
"data": {
|
||||
"labels": ["On Track", "Delayed"],
|
||||
"datasets": [
|
||||
{
|
||||
"name": "Delayed",
|
||||
"values": [on_track, delay]
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "percentage",
|
||||
"colors": ["#84D5BA", "#CB4B5F"]
|
||||
}
|
||||
return charts
|
||||
|
||||
def get_columns():
|
||||
columns = [
|
||||
{
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Link",
|
||||
"label": "Task",
|
||||
"options": "Task",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"label": "Subject",
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Data",
|
||||
"label": "Status",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "priority",
|
||||
"fieldtype": "Data",
|
||||
"label": "Priority",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"fieldname": "progress",
|
||||
"fieldtype": "Data",
|
||||
"label": "Progress (%)",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"fieldname": "exp_start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Expected Start Date",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"fieldname": "exp_end_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Expected End Date",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"fieldname": "completed_on",
|
||||
"fieldtype": "Date",
|
||||
"label": "Actual End Date",
|
||||
"width": 130
|
||||
},
|
||||
{
|
||||
"fieldname": "delay",
|
||||
"fieldtype": "Data",
|
||||
"label": "Delay (In Days)",
|
||||
"width": 120
|
||||
}
|
||||
]
|
||||
return columns
|
||||
@@ -0,0 +1,54 @@
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
import frappe
|
||||
from frappe.utils import nowdate, add_days, add_months
|
||||
from erpnext.projects.doctype.task.test_task import create_task
|
||||
from erpnext.projects.report.delayed_tasks_summary.delayed_tasks_summary import execute
|
||||
|
||||
class TestDelayedTasksSummary(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUp(self):
|
||||
task1 = create_task("_Test Task 98", add_days(nowdate(), -10), nowdate())
|
||||
create_task("_Test Task 99", add_days(nowdate(), -10), add_days(nowdate(), -1))
|
||||
|
||||
task1.status = "Completed"
|
||||
task1.completed_on = add_days(nowdate(), -1)
|
||||
task1.save()
|
||||
|
||||
def test_delayed_tasks_summary(self):
|
||||
filters = frappe._dict({
|
||||
"from_date": add_months(nowdate(), -1),
|
||||
"to_date": nowdate(),
|
||||
"priority": "Low",
|
||||
"status": "Open"
|
||||
})
|
||||
expected_data = [
|
||||
{
|
||||
"subject": "_Test Task 99",
|
||||
"status": "Open",
|
||||
"priority": "Low",
|
||||
"delay": 1
|
||||
},
|
||||
{
|
||||
"subject": "_Test Task 98",
|
||||
"status": "Completed",
|
||||
"priority": "Low",
|
||||
"delay": -1
|
||||
}
|
||||
]
|
||||
report = execute(filters)
|
||||
data = list(filter(lambda x: x.subject == "_Test Task 99", report[1]))[0]
|
||||
|
||||
for key in ["subject", "status", "priority", "delay"]:
|
||||
self.assertEqual(expected_data[0].get(key), data.get(key))
|
||||
|
||||
filters.status = "Completed"
|
||||
report = execute(filters)
|
||||
data = list(filter(lambda x: x.subject == "_Test Task 98", report[1]))[0]
|
||||
|
||||
for key in ["subject", "status", "priority", "delay"]:
|
||||
self.assertEqual(expected_data[1].get(key), data.get(key))
|
||||
|
||||
def tearDown(self):
|
||||
for task in ["_Test Task 98", "_Test Task 99"]:
|
||||
frappe.get_doc("Task", {"subject": task}).delete()
|
||||
Reference in New Issue
Block a user