mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-31 10:49:09 +00:00
feat: record assignment on first response failure
This commit is contained in:
@@ -211,6 +211,43 @@ class TestIssue(TestSetUp):
|
|||||||
self.assertEquals(issue.agreement_status, 'Fulfilled')
|
self.assertEquals(issue.agreement_status, 'Fulfilled')
|
||||||
self.assertEquals(issue.resolution_date, frappe.flags.current_time)
|
self.assertEquals(issue.resolution_date, frappe.flags.current_time)
|
||||||
|
|
||||||
|
def test_recording_of_assignment_on_first_reponse_failure(self):
|
||||||
|
from frappe.desk.form.assign_to import add as add_assignment
|
||||||
|
|
||||||
|
frappe.flags.current_time = get_datetime("2021-11-01 19:00")
|
||||||
|
|
||||||
|
issue = make_issue(frappe.flags.current_time, index=1)
|
||||||
|
create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
|
||||||
|
add_assignment({
|
||||||
|
'doctype': issue.doctype,
|
||||||
|
'name': issue.name,
|
||||||
|
'assign_to': ['test@admin.com']
|
||||||
|
})
|
||||||
|
issue.reload()
|
||||||
|
|
||||||
|
# send a reply failing response SLA
|
||||||
|
frappe.flags.current_time = get_datetime("2021-11-02 15:00")
|
||||||
|
create_communication(issue.name, "test@admin.com", "Sent", frappe.flags.current_time)
|
||||||
|
|
||||||
|
# assert if a new timeline item has been added
|
||||||
|
# to record the assignment
|
||||||
|
comment = frappe.get_last_doc('Comment')
|
||||||
|
self.assertTrue('First Response SLA Failed' in comment.content)
|
||||||
|
|
||||||
|
def test_agreement_status_on_response(self):
|
||||||
|
frappe.flags.current_time = get_datetime("2021-11-01 19:00")
|
||||||
|
|
||||||
|
issue = make_issue(frappe.flags.current_time, index=1)
|
||||||
|
create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
|
||||||
|
self.assertTrue(issue.status == 'Open')
|
||||||
|
|
||||||
|
# send a reply within response SLA
|
||||||
|
frappe.flags.current_time = get_datetime("2021-11-02 11:00")
|
||||||
|
create_communication(issue.name, "test@admin.com", "Sent", frappe.flags.current_time)
|
||||||
|
|
||||||
|
issue.reload()
|
||||||
|
self.assertEquals(issue.first_responded_on, frappe.flags.current_time)
|
||||||
|
self.assertEquals(issue.agreement_status, 'Resolution Due')
|
||||||
|
|
||||||
class TestFirstResponseTime(TestSetUp):
|
class TestFirstResponseTime(TestSetUp):
|
||||||
# working hours used in all cases: Mon-Fri, 10am to 6pm
|
# working hours used in all cases: Mon-Fri, 10am to 6pm
|
||||||
@@ -425,6 +462,7 @@ class TestFirstResponseTime(TestSetUp):
|
|||||||
def create_issue_and_communication(issue_creation, first_responded_on):
|
def create_issue_and_communication(issue_creation, first_responded_on):
|
||||||
issue = make_issue(issue_creation, index=1)
|
issue = make_issue(issue_creation, index=1)
|
||||||
sender = create_user("test@admin.com")
|
sender = create_user("test@admin.com")
|
||||||
|
frappe.flags.current_time = first_responded_on
|
||||||
create_communication(issue.name, sender.email, "Sent", first_responded_on)
|
create_communication(issue.name, sender.email, "Sent", first_responded_on)
|
||||||
issue.reload()
|
issue.reload()
|
||||||
|
|
||||||
|
|||||||
@@ -397,6 +397,12 @@ def handle_status_change(doc, apply_sla_for_resolution):
|
|||||||
def is_open_status(status):
|
def is_open_status(status):
|
||||||
return status not in hold_statuses and status not in fulfillment_statuses
|
return status not in hold_statuses and status not in fulfillment_statuses
|
||||||
|
|
||||||
|
def set_first_response():
|
||||||
|
if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
|
||||||
|
doc.first_responded_on = now_time
|
||||||
|
if get_datetime(doc.get('first_responded_on')) > get_datetime(doc.get('response_by')):
|
||||||
|
record_assigned_users_on_failure(doc)
|
||||||
|
|
||||||
def calculate_hold_hours():
|
def calculate_hold_hours():
|
||||||
# In case issue was closed and after few days it has been opened
|
# In case issue was closed and after few days it has been opened
|
||||||
# The hold time should be calculated from resolution_date
|
# The hold time should be calculated from resolution_date
|
||||||
@@ -408,9 +414,7 @@ def handle_status_change(doc, apply_sla_for_resolution):
|
|||||||
doc.on_hold_since = None
|
doc.on_hold_since = None
|
||||||
|
|
||||||
if ((is_open_status(prev_status) and not is_open_status(doc.status)) or doc.flags.on_first_reply):
|
if ((is_open_status(prev_status) and not is_open_status(doc.status)) or doc.flags.on_first_reply):
|
||||||
# status changed from Open to something else
|
set_first_response()
|
||||||
if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
|
|
||||||
doc.first_responded_on = now_time
|
|
||||||
|
|
||||||
# Open to Replied
|
# Open to Replied
|
||||||
if is_open_status(prev_status) and is_hold_status(doc.status):
|
if is_open_status(prev_status) and is_hold_status(doc.status):
|
||||||
@@ -688,6 +692,18 @@ def set_resolution_by(doc, start_date_time, priority):
|
|||||||
doc.resolution_by = add_to_date(doc.resolution_by, seconds=round(doc.get('total_hold_time')))
|
doc.resolution_by = add_to_date(doc.resolution_by, seconds=round(doc.get('total_hold_time')))
|
||||||
|
|
||||||
|
|
||||||
|
def record_assigned_users_on_failure(doc):
|
||||||
|
assigned_users = doc.get_assigned_users()
|
||||||
|
if assigned_users:
|
||||||
|
from frappe.utils import get_fullname
|
||||||
|
assigned_users = ', '.join((get_fullname(user) for user in assigned_users))
|
||||||
|
message = _(f'First Response SLA Failed by {assigned_users}')
|
||||||
|
doc.add_comment(
|
||||||
|
comment_type='Assigned',
|
||||||
|
text=message
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_service_level_agreement_fields():
|
def get_service_level_agreement_fields():
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user