Some checks failed
Lock Threads / action (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
- Add Logistics component with progress tracking - Add OrderDetail component for order information - Support data-driven steps and actions - Add blue color scale to widget SCSS - Fix node overflow and progress bar rendering issues - Add English translations for dashboard components Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
108 lines
3.9 KiB
Ruby
108 lines
3.9 KiB
Ruby
class Sla::EvaluateAppliedSlaService
|
|
pattr_initialize [:applied_sla!]
|
|
|
|
def perform
|
|
check_sla_thresholds
|
|
|
|
# We will calculate again in the next iteration
|
|
return unless applied_sla.conversation.resolved?
|
|
|
|
# after conversation is resolved, we will check if the SLA was hit or missed
|
|
handle_hit_sla(applied_sla)
|
|
end
|
|
|
|
private
|
|
|
|
def check_sla_thresholds
|
|
[:first_response_time_threshold, :next_response_time_threshold, :resolution_time_threshold].each do |threshold|
|
|
next if applied_sla.sla_policy.send(threshold).blank?
|
|
|
|
send("check_#{threshold}", applied_sla, applied_sla.conversation, applied_sla.sla_policy)
|
|
end
|
|
end
|
|
|
|
def still_within_threshold?(threshold)
|
|
Time.zone.now.to_i < threshold
|
|
end
|
|
|
|
def check_first_response_time_threshold(applied_sla, conversation, sla_policy)
|
|
threshold = conversation.created_at.to_i + sla_policy.first_response_time_threshold.to_i
|
|
return if first_reply_was_within_threshold?(conversation, threshold)
|
|
return if still_within_threshold?(threshold)
|
|
|
|
handle_missed_sla(applied_sla, 'frt')
|
|
end
|
|
|
|
def first_reply_was_within_threshold?(conversation, threshold)
|
|
conversation.first_reply_created_at.present? && conversation.first_reply_created_at.to_i <= threshold
|
|
end
|
|
|
|
def check_next_response_time_threshold(applied_sla, conversation, sla_policy)
|
|
# still waiting for first reply, so covered under first response time threshold
|
|
return if conversation.first_reply_created_at.blank?
|
|
# Waiting on customer response, no need to check next response time threshold
|
|
return if conversation.waiting_since.blank?
|
|
|
|
threshold = conversation.waiting_since.to_i + sla_policy.next_response_time_threshold.to_i
|
|
return if still_within_threshold?(threshold)
|
|
|
|
handle_missed_sla(applied_sla, 'nrt')
|
|
end
|
|
|
|
def get_last_message_id(conversation)
|
|
# TODO: refactor the method to fetch last message without reply
|
|
conversation.messages.where(message_type: :incoming).last&.id
|
|
end
|
|
|
|
def already_missed?(applied_sla, type, meta = {})
|
|
SlaEvent.exists?(applied_sla: applied_sla, event_type: type, meta: meta)
|
|
end
|
|
|
|
def check_resolution_time_threshold(applied_sla, conversation, sla_policy)
|
|
return if conversation.resolved?
|
|
|
|
threshold = conversation.created_at.to_i + sla_policy.resolution_time_threshold.to_i
|
|
return if still_within_threshold?(threshold)
|
|
|
|
handle_missed_sla(applied_sla, 'rt')
|
|
end
|
|
|
|
def handle_missed_sla(applied_sla, type, meta = {})
|
|
meta = { message_id: get_last_message_id(applied_sla.conversation) } if type == 'nrt'
|
|
return if already_missed?(applied_sla, type, meta)
|
|
|
|
create_sla_event(applied_sla, type, meta)
|
|
Rails.logger.warn "SLA #{type} missed for conversation #{applied_sla.conversation.id} " \
|
|
"in account #{applied_sla.account_id} " \
|
|
"for sla_policy #{applied_sla.sla_policy.id}"
|
|
|
|
applied_sla.update!(sla_status: 'active_with_misses') if applied_sla.sla_status != 'active_with_misses'
|
|
end
|
|
|
|
def handle_hit_sla(applied_sla)
|
|
if applied_sla.active?
|
|
applied_sla.update!(sla_status: 'hit')
|
|
Rails.logger.info "SLA hit for conversation #{applied_sla.conversation.id} " \
|
|
"in account #{applied_sla.account_id} " \
|
|
"for sla_policy #{applied_sla.sla_policy.id}"
|
|
else
|
|
applied_sla.update!(sla_status: 'missed')
|
|
Rails.logger.info "SLA missed for conversation #{applied_sla.conversation.id} " \
|
|
"in account #{applied_sla.account_id} " \
|
|
"for sla_policy #{applied_sla.sla_policy.id}"
|
|
end
|
|
end
|
|
|
|
def create_sla_event(applied_sla, event_type, meta = {})
|
|
SlaEvent.create!(
|
|
applied_sla: applied_sla,
|
|
conversation: applied_sla.conversation,
|
|
event_type: event_type,
|
|
meta: meta,
|
|
account: applied_sla.account,
|
|
inbox: applied_sla.conversation.inbox,
|
|
sla_policy: applied_sla.sla_policy
|
|
)
|
|
end
|
|
end
|