diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index b7da29649d0..31797dff42a 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -76,9 +76,9 @@ class Issue(Document): self.agreement_fulfilled = "Ongoing" set_service_level_agreement_variance(issue=self.name) - self.handle_hold_time() + self.handle_hold_time(status) - def handle_hold_time(self): + def handle_hold_time(self, status): # set response and resolution variance as None as the issue is on Hold for status as Replied if self.status == "Replied" and status != "Replied": self.on_hold_since = frappe.flags.current_time or now_datetime() @@ -91,24 +91,26 @@ class Issue(Document): # calculate hold time when status is changed from Replied to any other status if self.status != "Replied" and status == "Replied": hold_time = self.total_hold_time if self.total_hold_time else 0 - self.total_hold_time = hold_time + time_diff_in_seconds(now_datetime(), self.on_hold_since) + now_time = frappe.flags.current_time or now_datetime() + self.total_hold_time = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) # re-calculate SLA variables after issue changes from Replied to Open # add hold time to SLA variables if self.status == "Open" and status == "Replied": start_date_time = get_datetime(self.service_level_agreement_creation) priority = get_priority(self) + now_time = frappe.flags.current_time or now_datetime() hold_time = time_diff_in_seconds(now_datetime(), self.on_hold_since) if not self.first_responded_on: response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) self.response_by = add_to_date(response_by, seconds=round(hold_time)) - response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime())) + response_by_variance = round(time_diff_in_hours(self.response_by, now_time)) self.response_by_variance = response_by_variance + (hold_time // 3600) resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) - resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) + resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time)) self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) self.on_hold_since = None @@ -377,7 +379,7 @@ def set_average_response_time(issue): def set_resolution_time(issue): # total time taken from issue creation to closing - resolution_time = time_diff_in_seconds(now_datetime(), issue.creation) + resolution_time = time_diff_in_seconds(issue.resolution_date, issue.creation) issue.db_set("resolution_time", resolution_time) @@ -399,7 +401,7 @@ def set_user_resolution_time(issue): pending_time.append(wait_time) total_pending_time = sum(pending_time) - resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) + resolution_time_in_secs = time_diff_in_seconds(issue.resolution_date, issue.creation) user_resolution_time = resolution_time_in_secs - total_pending_time issue.db_set("user_resolution_time", user_resolution_time) diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 7a5e3e300db..2818b1b8eb3 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -10,10 +10,13 @@ import datetime from datetime import timedelta class TestIssue(unittest.TestCase): - def test_response_time_and_resolution_time_based_on_different_sla(self): + def setUp(self): + frappe.db.sql("delete from `tabService Level Agreement`") + frappe.db.sql("delete from `tabEmployee`") frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) create_service_level_agreements_for_issues() + def test_response_time_and_resolution_time_based_on_different_sla(self): creation = datetime.datetime(2019, 3, 4, 12, 0) # make issue with customer specific SLA @@ -72,6 +75,33 @@ class TestIssue(unittest.TestCase): self.assertEqual(issue.agreement_fulfilled, 'Fulfilled') + def test_issue_metrics(self): + creation = datetime.datetime(2020, 3, 4, 4, 0) + + # make issue with customer specific SLA + customer = create_customer("_Test Customer", "__Test SLA Customer Group", "__Test SLA Territory") + issue = make_issue(creation, "_Test Customer", 1) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 4, 15) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + creation = datetime.datetime(2020, 3, 4, 5, 0) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 5, 5) + create_communication(issue.name, "test@admin.com", "Sent", creation) + issue = frappe.get_doc('Issue', issue.name) + + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5) + issue.status = 'Closed' + issue.save() + + self.assertEqual(issue.avg_response_time, 600) + self.assertEqual(issue.resolution_time, 3900) + self.assertEqual(issue.user_resolution_time, 1200) + + def make_issue(creation=None, customer=None, index=0): issue = frappe.get_doc({ @@ -86,6 +116,7 @@ def make_issue(creation=None, customer=None, index=0): return issue + def create_customer(name, customer_group, territory): create_customer_group(customer_group) @@ -99,6 +130,7 @@ def create_customer(name, customer_group, territory): "territory": territory }).insert(ignore_permissions=True) + def create_customer_group(customer_group): if not frappe.db.exists("Customer Group", {"customer_group_name": customer_group}): @@ -107,6 +139,7 @@ def create_customer_group(customer_group): "customer_group_name": customer_group }).insert(ignore_permissions=True) + def create_territory(territory): if not frappe.db.exists("Territory", {"territory_name": territory}): @@ -114,3 +147,20 @@ def create_territory(territory): "doctype": "Territory", "territory_name": territory, }).insert(ignore_permissions=True) + + +def create_communication(reference_name, sender, sent_or_received, creation): + issue = frappe.get_doc({ + "doctype": "Communication", + "communication_type": "Communication", + "communication_medium": "Email", + "sent_or_received": sent_or_received, + "email_status": "Open", + "subject": "Test Issue", + "sender": sender, + "content": "Test", + "status": "Linked", + "reference_doctype": "Issue", + "creation": creation, + "reference_name": reference_name + }).insert(ignore_permissions=True) diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 94e0b582f97..57d4747e5c9 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -16,7 +16,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # Default Service Level Agreement create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type=None, entity=None, response_time=4, resolution_time=6) + entity_type=None, entity=None, response_time=14400, resolution_time=21600) get_default_service_level_agreement = get_service_level_agreement(default_service_level_agreement=1) @@ -29,7 +29,7 @@ class TestServiceLevelAgreement(unittest.TestCase): customer = create_customer() create_customer_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer", entity=customer, response_time=2, resolution_time=3) + entity_type="Customer", entity=customer, response_time=7200, resolution_time=10800) get_customer_service_level_agreement = get_service_level_agreement(entity_type="Customer", entity=customer) self.assertEqual(create_customer_service_level_agreement.name, get_customer_service_level_agreement.name) @@ -41,7 +41,7 @@ class TestServiceLevelAgreement(unittest.TestCase): customer_group = create_customer_group() create_customer_group_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer Group", entity=customer_group, response_time=2, resolution_time=3) + entity_type="Customer Group", entity=customer_group, response_time=7200, resolution_time=10800) get_customer_group_service_level_agreement = get_service_level_agreement(entity_type="Customer Group", entity=customer_group) self.assertEqual(create_customer_group_service_level_agreement.name, get_customer_group_service_level_agreement.name) @@ -53,7 +53,7 @@ class TestServiceLevelAgreement(unittest.TestCase): territory = create_territory() create_territory_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Territory", entity=territory, response_time=2, resolution_time=3) + entity_type="Territory", entity=territory, response_time=7200, resolution_time=10800) get_territory_service_level_agreement = get_service_level_agreement(entity_type="Territory", entity=territory) self.assertEqual(create_territory_service_level_agreement.name, get_territory_service_level_agreement.name) @@ -83,6 +83,7 @@ def create_service_level_agreement(default_service_level_agreement, holiday_list "enable": 1, "service_level": "__Test Service Level", "default_service_level_agreement": default_service_level_agreement, + "default_priority": "Medium", "holiday_list": holiday_list, "employee_group": employee_group, "entity_type": entity_type, @@ -212,19 +213,19 @@ def create_territory(): def create_service_level_agreements_for_issues(): create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=4, resolution_time=6) + employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=14400, resolution_time=21600) create_customer() create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type="Customer", entity="_Test Customer", response_time=2, resolution_time=3) + employee_group="_Test Employee Group", entity_type="Customer", entity="_Test Customer", response_time=7200, resolution_time=10800) create_customer_group() create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=2, resolution_time=3) + employee_group="_Test Employee Group", entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=7200, resolution_time=10800) create_territory() create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type="Territory", entity="_Test SLA Territory", response_time=2, resolution_time=3) + employee_group="_Test Employee Group", entity_type="Territory", entity="_Test SLA Territory", response_time=7200, resolution_time=10800) def make_holiday_list(): holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List")