From 898ab61dd4f9f1f60a7dc10275471ed7799a79cf Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Sat, 4 May 2019 23:16:02 +0530 Subject: [PATCH] fix: Child table for priority --- erpnext/support/doctype/issue/issue.json | 10 +-- erpnext/support/doctype/issue/issue.py | 35 ++++---- .../doctype/service_level/service_level.json | 45 +++-------- .../doctype/service_level/service_level.py | 79 ++++++++++++++----- .../service_level_agreement.js | 30 +++++-- .../service_level_agreement.json | 48 ++--------- .../service_level_agreement.py | 43 ++++++---- .../service_level_priority/__init__.py | 0 .../service_level_priority.json | 74 +++++++++++++++++ .../service_level_priority.py | 10 +++ 10 files changed, 233 insertions(+), 141 deletions(-) create mode 100644 erpnext/support/doctype/service_level_priority/__init__.py create mode 100644 erpnext/support/doctype/service_level_priority/service_level_priority.json create mode 100644 erpnext/support/doctype/service_level_priority/service_level_priority.py diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 2e23b09a40..9639702e24 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -14,12 +14,12 @@ "raised_by", "cb00", "status", - "service_level_agreement", "priority", "issue_type", "sb_details", "description", "service_level_section", + "service_level_agreement", "response_by", "cb", "agreement_status", @@ -115,8 +115,7 @@ "fieldtype": "Select", "in_standard_filter": 1, "label": "Priority", - "options": "Low\nMedium\nHigh", - "reqd": 1 + "options": "Low\nMedium\nHigh" }, { "fieldname": "issue_type", @@ -151,7 +150,8 @@ "fieldname": "service_level_agreement", "fieldtype": "Link", "label": "Service Level Agreement", - "options": "Service Level Agreement" + "options": "Service Level Agreement", + "read_only": 1 }, { "fieldname": "response_by", @@ -321,7 +321,7 @@ ], "icon": "fa fa-ticket", "idx": 7, - "modified": "2019-05-03 11:11:50.495619", + "modified": "2019-05-03 14:36:19.117560", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index d626def665..0ff37d7e31 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -126,25 +126,29 @@ class Issue(Document): return replicated_issue.name def before_insert(self): - self.set_response_and_resolution_time() + self.set_response_and_resolution_time(priority=self.priority) - def set_response_and_resolution_time(self): - service_level_agreement = get_active_service_level_agreement_for(self.customer) + def set_response_and_resolution_time(self, priority=None): + service_level_agreement = get_active_service_level_agreement_for(self.customer, priority) if service_level_agreement: self.service_level_agreement = service_level_agreement.name - self.priority = service_level_agreement.priority - - if not self.service_level_agreement: return + else: + return service_level = frappe.get_doc("Service Level", service_level_agreement.service_level) + priority = service_level.get_service_level_priority(priority) + priority.update({ + "support_and_resolution": service_level.support_and_resolution, + "holiday_list": service_level.holiday_list + }) if not self.creation: self.creation = now_datetime() start_date_time = get_datetime(self.creation) - self.response_by = get_expected_time_for('response', service_level, start_date_time) - self.resolution_by = get_expected_time_for('resolution', service_level, start_date_time) + self.response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) + self.resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time @@ -154,11 +158,11 @@ def get_expected_time_for(parameter, service_level, start_date_time): # lets assume response time is in days by default if parameter == 'response': - allotted_days = service_level.response_time - time_period = service_level.response_time_period + allotted_days = service_level.get("response_time") + time_period = service_level.get("response_time_period") elif parameter == 'resolution': - allotted_days = service_level.resolution_time - time_period = service_level.resolution_time_period + allotted_days = service_level.get("resolution_time") + time_period = service_level.get("resolution_time_period") else: frappe.throw(_("{0} parameter is invalid".format(parameter))) @@ -172,13 +176,13 @@ def get_expected_time_for(parameter, service_level, start_date_time): expected_time_is_set = 1 if allotted_days == 0 and time_period in ['Day', 'Week'] else 0 support_days = {} - for service in service_level.support_and_resolution: + for service in service_level.get("support_and_resolution"): support_days[service.workday] = frappe._dict({ 'start_time': service.start_time, 'end_time': service.end_time, }) - holidays = get_holidays(service_level.holiday_list) + holidays = get_holidays(service_level.get("holiday_list")) weekdays = get_weekdays() while not expected_time_is_set: @@ -248,14 +252,12 @@ def set_multiple_status(names, status): for name in names: set_status(name, status) - @frappe.whitelist() def set_status(name, status): st = frappe.get_doc("Issue", name) st.status = status st.save() - def auto_close_tickets(): """Auto-close replied support tickets after 7 days""" auto_close_after_days = frappe.db.get_value("Support Settings", "Support Settings", "close_issue_after_days") or 7 @@ -295,6 +297,7 @@ def make_task(source_name, target_doc=None): "doctype": "Task" } }, target_doc) + @frappe.whitelist() def make_issue_from_communication(communication, ignore_communication_links=False): """ raise a issue from email """ diff --git a/erpnext/support/doctype/service_level/service_level.json b/erpnext/support/doctype/service_level/service_level.json index 9dc1a8d373..fb4e74780d 100644 --- a/erpnext/support/doctype/service_level/service_level.json +++ b/erpnext/support/doctype/service_level/service_level.json @@ -10,11 +10,7 @@ "column_break_2", "holiday_list", "response_and_resoution_time", - "response_time", - "resolution_time", - "column_break_9", - "response_time_period", - "resolution_time_period", + "priority", "section_break_01", "support_and_resolution" ], @@ -51,36 +47,6 @@ "fieldtype": "Section Break", "label": "Response and Resoution Time" }, - { - "fieldname": "response_time", - "fieldtype": "Int", - "label": "Response Time", - "reqd": 1 - }, - { - "fieldname": "resolution_time", - "fieldtype": "Int", - "label": "Resolution Time", - "reqd": 1 - }, - { - "fieldname": "column_break_9", - "fieldtype": "Column Break" - }, - { - "fieldname": "response_time_period", - "fieldtype": "Select", - "label": "Response Time Period", - "options": "Hour\nDay\nWeek", - "reqd": 1 - }, - { - "fieldname": "resolution_time_period", - "fieldtype": "Select", - "label": "Resolution Time Period", - "options": "Hour\nDay\nWeek", - "reqd": 1 - }, { "fieldname": "section_break_01", "fieldtype": "Section Break", @@ -92,9 +58,16 @@ "label": "Support and Resolution", "options": "Service Day", "reqd": 1 + }, + { + "fieldname": "priority", + "fieldtype": "Table", + "label": "Priority", + "options": "Service Level Priority", + "reqd": 1 } ], - "modified": "2019-05-03 01:09:43.571945", + "modified": "2019-05-04 13:08:33.381734", "modified_by": "Administrator", "module": "Support", "name": "Service Level", diff --git a/erpnext/support/doctype/service_level/service_level.py b/erpnext/support/doctype/service_level/service_level.py index 4b41e53560..5389afb3e6 100644 --- a/erpnext/support/doctype/service_level/service_level.py +++ b/erpnext/support/doctype/service_level/service_level.py @@ -12,35 +12,78 @@ from frappe.utils import get_weekdays class ServiceLevel(Document): def validate(self): + self.check_priorities() + self.check_support_and_resolution() + + def check_priorities(self): + priorities = [] + for priority in self.priority: + priorities.append(priority.priority) + + if not (priority.response_time or priority.resolution_time): + frappe.throw(_("Set Response Time and Resolution for Priority {0} at index {1}.".format(priority.priority, priority.idx))) + + if priority.response_time_period == "Hour": + response = priority.response_time * 0.0416667 + elif priority.response_time_period == "Day": + response = priority.response_time + elif priority.response_time_period == "Week": + response = priority.response_time * 7 + + if priority.resolution_time_period == "Hour": + resolution = priority.resolution_time * 0.0416667 + elif priority.resolution_time_period == "Day": + resolution = priority.resolution_time + elif priority.resolution_time_period == "Week": + resolution = priority.resolution_time * 7 + + if response > resolution: + frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.".format(priority.priority, priority.idx))) + + if not len(set(priorities)) == len(priorities): + repeated_priority = get_repeated(priorities) + frappe.throw(_("Priority {0} has been repeated twice.".format(repeated_priority))) + + # Check if values for all the priority options is set + priority_count = ([field.options for field in frappe.get_meta("Service Level Priority").fields if field.fieldname=='priority'][0]).split("\n") + if not len(set(priorities)) == len(priority_count): + frappe.throw(_("Set values for all the Priorities.")) + + def check_support_and_resolution(self): week = get_weekdays() indexes = [] - - self.check_response_and_resolution_time() + support_days = [] for support_and_resolution in self.support_and_resolution: indexes.append(week.index(support_and_resolution.workday)) + support_days.append(support_and_resolution.workday) support_and_resolution.idx = week.index(support_and_resolution.workday) + 1 start_time, end_time = (datetime.strptime(support_and_resolution.start_time, '%H:%M:%S').time(), datetime.strptime(support_and_resolution.end_time, '%H:%M:%S').time()) if start_time > end_time: frappe.throw(_("Start Time can't be greater than End Time for {0}.".format(support_and_resolution.workday))) + if not len(set(indexes)) == len(indexes): - frappe.throw(_("Workday has been repeated twice")) + repeated_days = get_repeated(support_days) + frappe.throw(_("Workday {0} has been repeated twice".format(repeated_days))) - def check_response_and_resolution_time(self): - if self.response_time_period == "Hour": - response = self.response_time * 0.0416667 - elif self.response_time_period == "Day": - response = self.response_time - elif self.response_time_period == "Week": - response = self.response_time * 7 + def get_service_level_priority(self, priority): + priority = frappe.get_doc("Service Level Priority", {"priority": priority, "parent": self.name}) - if self.resolution_time_period == "Hour": - resolution = self.resolution_time * 0.0416667 - elif self.resolution_time_period == "Day": - resolution = self.resolution_time - elif self.resolution_time_period == "Week": - resolution = self.resolution_time * 7 + return frappe._dict({ + "priority": priority.priority, + "response_time": priority.response_time, + "response_time_period": priority.response_time_period, + "resolution_time": priority.resolution_time, + "response_time_period": priority.resolution_time_period + }) - if response > resolution: - frappe.throw(_("Response Time can't be greater than Resolution Time")) \ No newline at end of file +def get_repeated(values): + unique_list = [] + diff = [] + for value in values: + if value not in unique_list: + unique_list.append(value) + else: + diff.append(value) + return " ".join(diff) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js index 66ec2f3ef2..92d7abdc63 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js @@ -11,7 +11,11 @@ frappe.ui.form.on('Service Level Agreement', { name: frm.doc.service_level }, callback: function(data){ - for (var i = 0; i < data.message.support_and_resolution.length; i++){ + console.log(data); + for (var i in data.message.priority){ + frm.add_child("priority", data.message.priority[i]); + } + for (var i in data.message.support_and_resolution){ frm.add_child("support_and_resolution", data.message.support_and_resolution[i]); } frm.refresh(); @@ -19,14 +23,24 @@ frappe.ui.form.on('Service Level Agreement', { }); }, - customer: function(frm) { + validate: function(frm) { frm.doc.service_level_agreement_name = null; - frm.doc.service_level_agreement_name = frm.doc.priority + ': ' + frm.doc.customer; - }, - - default_service_level_agreement: function(frm) { - frm.doc.service_level_agreement_name = null; - frm.doc.service_level_agreement_name = frm.doc.priority + ': Default Service Level Agreement'; + var sla_name = 'Default Service Level Agreement'; + if (frm.doc.customer){ + sla_name = frm.doc.customer; + } + frm.doc.service_level_agreement_name = sla_name; }, + priority: function(frm) { + if (!frm.doc.__is_local) { + frappe.call({ + "method": "erpnext.support.service_level_agreement.service_level_agreement.get_active_service_level_agreement_for", + "args": { + "customer": frm.doc.customer, + "priority": frm.doc.priority + } + }) + } + } }); diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index 67400a1288..30a8bfdbba 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -11,7 +11,6 @@ "holiday_list", "column_break_2", "service_level", - "priority", "employee_group", "agreement_details_section", "start_date", @@ -19,11 +18,7 @@ "column_break_7", "end_date", "response_and_resolution_time_section", - "response_time", - "resolution_time", - "column_break_16", - "response_time_period", - "resolution_time_period", + "priority", "support_and_resolution_section_break", "support_and_resolution" ], @@ -66,11 +61,9 @@ }, { "fieldname": "priority", - "fieldtype": "Select", + "fieldtype": "Table", "label": "Priority", - "options": "Low\nMedium\nHigh", - "reqd": 1, - "set_only_once": 1 + "options": "Service Level Priority" }, { "fetch_from": "service_level.employee_group", @@ -113,42 +106,11 @@ "label": "End Date" }, { + "collapsible": 1, "fieldname": "response_and_resolution_time_section", "fieldtype": "Section Break", "label": "Response and Resolution Time" }, - { - "fetch_from": "service_level.response_time", - "fieldname": "response_time", - "fieldtype": "Int", - "label": "Response Time", - "read_only": 1 - }, - { - "fetch_from": "service_level.resolution_time", - "fieldname": "resolution_time", - "fieldtype": "Int", - "label": "Resolution Time", - "read_only": 1 - }, - { - "fieldname": "column_break_16", - "fieldtype": "Column Break" - }, - { - "fetch_from": "service_level.response_time_period", - "fieldname": "response_time_period", - "fieldtype": "Data", - "label": "Response Time Period", - "read_only": 1 - }, - { - "fetch_from": "service_level.resolution_time_period", - "fieldname": "resolution_time_period", - "fieldtype": "Data", - "label": "Resolution Time Period", - "read_only": 1 - }, { "collapsible": 1, "fieldname": "support_and_resolution_section_break", @@ -170,7 +132,7 @@ "unique": 1 } ], - "modified": "2019-05-03 01:50:47.135540", + "modified": "2019-05-04 13:11:21.373147", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index a4db83563a..e879b4ae68 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -9,21 +9,20 @@ from frappe import _ class ServiceLevelAgreement(Document): - def before_insert(self): - if self.default_service_level_agreement: - doc = frappe.get_list("Service Level Agreement", filters=[{"default_service_level_agreement": "1"}]) - if doc: - frappe.throw(_("A Default Service Level Agreement already exists.")) - def validate(self): - if not frm.doc.customer and not frm.doc.default_service_level_agreement: + if not (self.customer or self.default_service_level_agreement): frappe.throw(_("Select a Customer or set as Default Service Level Agreement.")) - if not self.default_service_level_agreement: + if self.default_service_level_agreement: + if frappe.db.exists("Service Level Agreement", {"default_service_level_agreement": "1"}): + frappe.throw(_("A Default Service Level Agreement already exists.")) + else: if not (self.start_date and self.end_date): frappe.throw(_("Enter Start and End Date for the Agreement.")) if self.start_date >= self.end_date: frappe.throw(_("Start Date of Agreement can't be greater than or equal to End Date.")) + if self.end_date < frappe.utils.getdate(): + frappe.throw(_("End Date of Agreement can't be less than today.")) def check_agreement_status(): service_level_agreements = frappe.get_list("Service Level Agreement", filters=[ @@ -37,12 +36,26 @@ def check_agreement_status(): service_level_agreement.agreement_status = "Expired" service_level_agreement.save() -def get_active_service_level_agreement_for(customer): - agreement = frappe.get_list("Service Level Agreement", - filters=[{"agreement_status": "Active"}], - or_filters=[{'customer': customer},{"default_service_level_agreement": "1"}], - fields=["name", "service_level", "holiday_list", "priority"], - order_by='customer DESC', - limit=1) +@frappe.whitelist() +def get_active_service_level_agreement_for(customer, priority): + + agreement = frappe.db.sql(""" + select `tabService Level Agreement`.name, `tabService Level Agreement`.service_level, + `tabService Level Agreement`.holiday_list + from `tabService Level Agreement` + inner join `tabService Level Priority` + on `tabService Level Agreement`.name=`tabService Level Priority`.parent where + ( + `tabService Level Agreement`.customer='{0}' and + `tabService Level Agreement`.agreement_status='Active' and + `tabService Level Priority`.priority='{1}' + ) or + ( + `tabService Level Agreement`.default_service_level_agreement='1' + ) + limit 1 + """.format(customer, priority), as_dict=True, debug=True) + + print(agreement) return agreement[0] if agreement else None \ No newline at end of file diff --git a/erpnext/support/doctype/service_level_priority/__init__.py b/erpnext/support/doctype/service_level_priority/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json new file mode 100644 index 0000000000..6693649c25 --- /dev/null +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -0,0 +1,74 @@ +{ + "creation": "2019-05-04 05:54:03.658991", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "priority", + "sb_00", + "response_time", + "response_time_period", + "cb_00", + "resolution_time", + "resolution_time_period" + ], + "fields": [ + { + "columns": 2, + "fieldname": "priority", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Priority", + "options": "Low\nMedium\nHigh" + }, + { + "fieldname": "sb_00", + "fieldtype": "Section Break" + }, + { + "columns": 2, + "fieldname": "response_time", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Response Time" + }, + { + "columns": 2, + "fieldname": "resolution_time", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Resolution Time" + }, + { + "fieldname": "cb_00", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "response_time_period", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Response Time Period", + "options": "Hour\nDay\nWeek" + }, + { + "columns": 2, + "fieldname": "resolution_time_period", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Resolution Time Period", + "options": "Hour\nDay\nWeek" + } + ], + "istable": 1, + "modified": "2019-05-04 06:04:32.956731", + "modified_by": "Administrator", + "module": "Support", + "name": "Service Level Priority", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.py b/erpnext/support/doctype/service_level_priority/service_level_priority.py new file mode 100644 index 0000000000..0c0fe4a066 --- /dev/null +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class ServiceLevelPriority(Document): + pass