|
|
@ -26,9 +26,6 @@ class Issue(Document): |
|
|
|
|
|
|
|
self.set_lead_contact(self.raised_by) |
|
|
|
|
|
|
|
if not self.service_level_agreement: |
|
|
|
self.reset_sla_fields() |
|
|
|
|
|
|
|
def on_update(self): |
|
|
|
# Add a communication in the issue timeline |
|
|
|
if self.flags.create_communication and self.via_customer_portal: |
|
|
@ -54,106 +51,6 @@ class Issue(Document): |
|
|
|
self.company = frappe.db.get_value("Lead", self.lead, "company") or \ |
|
|
|
frappe.db.get_default("Company") |
|
|
|
|
|
|
|
def reset_sla_fields(self): |
|
|
|
self.agreement_status = "" |
|
|
|
self.response_by = "" |
|
|
|
self.resolution_by = "" |
|
|
|
self.response_by_variance = 0 |
|
|
|
self.resolution_by_variance = 0 |
|
|
|
|
|
|
|
def update_status(self): |
|
|
|
status = frappe.db.get_value("Issue", self.name, "status") |
|
|
|
if self.status != "Open" and status == "Open" and not self.first_responded_on: |
|
|
|
self.first_responded_on = frappe.flags.current_time or now_datetime() |
|
|
|
|
|
|
|
if self.status in ["Closed", "Resolved"] and status not in ["Resolved", "Closed"]: |
|
|
|
self.resolution_date = frappe.flags.current_time or now_datetime() |
|
|
|
if frappe.db.get_value("Issue", self.name, "agreement_status") == "Ongoing": |
|
|
|
set_service_level_agreement_variance(issue=self.name) |
|
|
|
self.update_agreement_status() |
|
|
|
set_resolution_time(issue=self) |
|
|
|
set_user_resolution_time(issue=self) |
|
|
|
|
|
|
|
if self.status == "Open" and status != "Open": |
|
|
|
# if no date, it should be set as None and not a blank string "", as per mysql strict config |
|
|
|
self.resolution_date = None |
|
|
|
self.reset_issue_metrics() |
|
|
|
# enable SLA and variance on Reopen |
|
|
|
self.agreement_status = "Ongoing" |
|
|
|
set_service_level_agreement_variance(issue=self.name) |
|
|
|
|
|
|
|
self.handle_hold_time(status) |
|
|
|
|
|
|
|
def handle_hold_time(self, status): |
|
|
|
if self.service_level_agreement: |
|
|
|
# set response and resolution variance as None as the issue is on Hold |
|
|
|
pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"], |
|
|
|
filters={"parent": self.service_level_agreement}) |
|
|
|
hold_statuses = [entry.status for entry in pause_sla_on] |
|
|
|
update_values = {} |
|
|
|
|
|
|
|
if hold_statuses: |
|
|
|
if self.status in hold_statuses and status not in hold_statuses: |
|
|
|
update_values['on_hold_since'] = frappe.flags.current_time or now_datetime() |
|
|
|
if not self.first_responded_on: |
|
|
|
update_values['response_by'] = None |
|
|
|
update_values['response_by_variance'] = 0 |
|
|
|
update_values['resolution_by'] = None |
|
|
|
update_values['resolution_by_variance'] = 0 |
|
|
|
|
|
|
|
# calculate hold time when status is changed from any hold status to any non-hold status |
|
|
|
if self.status not in hold_statuses and status in hold_statuses: |
|
|
|
hold_time = self.total_hold_time if self.total_hold_time else 0 |
|
|
|
now_time = frappe.flags.current_time or now_datetime() |
|
|
|
last_hold_time = 0 |
|
|
|
if self.on_hold_since: |
|
|
|
# last_hold_time will be added to the sla variables |
|
|
|
last_hold_time = time_diff_in_seconds(now_time, self.on_hold_since) |
|
|
|
update_values['total_hold_time'] = hold_time + last_hold_time |
|
|
|
|
|
|
|
# re-calculate SLA variables after issue changes from any hold status to any non-hold status |
|
|
|
# add hold time to SLA variables |
|
|
|
start_date_time = get_datetime(self.service_level_agreement_creation) |
|
|
|
priority = get_priority(self) |
|
|
|
now_time = frappe.flags.current_time or now_datetime() |
|
|
|
|
|
|
|
if not self.first_responded_on: |
|
|
|
response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) |
|
|
|
response_by = add_to_date(response_by, seconds=round(last_hold_time)) |
|
|
|
response_by_variance = round(time_diff_in_seconds(response_by, now_time)) |
|
|
|
update_values['response_by'] = response_by |
|
|
|
update_values['response_by_variance'] = response_by_variance + last_hold_time |
|
|
|
|
|
|
|
resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) |
|
|
|
resolution_by = add_to_date(resolution_by, seconds=round(last_hold_time)) |
|
|
|
resolution_by_variance = round(time_diff_in_seconds(resolution_by, now_time)) |
|
|
|
update_values['resolution_by'] = resolution_by |
|
|
|
update_values['resolution_by_variance'] = resolution_by_variance + last_hold_time |
|
|
|
update_values['on_hold_since'] = None |
|
|
|
|
|
|
|
self.db_set(update_values) |
|
|
|
|
|
|
|
def update_agreement_status(self): |
|
|
|
if self.service_level_agreement and self.agreement_status == "Ongoing": |
|
|
|
if cint(frappe.db.get_value("Issue", self.name, "response_by_variance")) < 0 or \ |
|
|
|
cint(frappe.db.get_value("Issue", self.name, "resolution_by_variance")) < 0: |
|
|
|
|
|
|
|
self.agreement_status = "Failed" |
|
|
|
else: |
|
|
|
self.agreement_status = "Fulfilled" |
|
|
|
|
|
|
|
def update_agreement_status_on_custom_status(self): |
|
|
|
""" |
|
|
|
Update Agreement Fulfilled status using Custom Scripts for Custom Issue Status |
|
|
|
""" |
|
|
|
if not self.first_responded_on: # first_responded_on set when first reply is sent to customer |
|
|
|
self.response_by_variance = round(time_diff_in_seconds(self.response_by, now_datetime()), 2) |
|
|
|
|
|
|
|
if not self.resolution_date: # resolution_date set when issue has been closed |
|
|
|
self.resolution_by_variance = round(time_diff_in_seconds(self.resolution_by, now_datetime()), 2) |
|
|
|
|
|
|
|
self.agreement_status = "Fulfilled" if self.response_by_variance > 0 and self.resolution_by_variance > 0 else "Failed" |
|
|
|
|
|
|
|
def create_communication(self): |
|
|
|
communication = frappe.new_doc("Communication") |
|
|
|
communication.update({ |
|
|
@ -318,4 +215,4 @@ def make_issue_from_communication(communication, ignore_communication_links=Fals |
|
|
|
def get_holidays(holiday_list_name): |
|
|
|
holiday_list = frappe.get_cached_doc("Holiday List", holiday_list_name) |
|
|
|
holidays = [holiday.holiday_date for holiday in holiday_list.holidays] |
|
|
|
return holidays |
|
|
|
return holidays |