# == Schema Information # # Table name: applied_slas # # id :bigint not null, primary key # sla_status :integer default("active") # created_at :datetime not null # updated_at :datetime not null # account_id :bigint not null # conversation_id :bigint not null # sla_policy_id :bigint not null # # Indexes # # index_applied_slas_on_account_id (account_id) # index_applied_slas_on_account_sla_policy_conversation (account_id,sla_policy_id,conversation_id) UNIQUE # index_applied_slas_on_conversation_id (conversation_id) # index_applied_slas_on_sla_policy_id (sla_policy_id) # class AppliedSla < ApplicationRecord belongs_to :account belongs_to :sla_policy belongs_to :conversation has_many :sla_events, dependent: :destroy_async validates :account_id, uniqueness: { scope: %i[sla_policy_id conversation_id] } before_validation :ensure_account_id enum sla_status: { active: 0, hit: 1, missed: 2, active_with_misses: 3 } scope :filter_by_date_range, ->(range) { where(created_at: range) if range.present? } scope :filter_by_inbox_id, ->(inbox_id) { joins(:conversation).where(conversations: { inbox_id: inbox_id }) if inbox_id.present? } scope :filter_by_team_id, ->(team_id) { joins(:conversation).where(conversations: { team_id: team_id }) if team_id.present? } scope :filter_by_sla_policy_id, ->(sla_policy_id) { where(sla_policy_id: sla_policy_id) if sla_policy_id.present? } scope :filter_by_label_list, lambda { |label_list| joins(:conversation).where('conversations.cached_label_list LIKE ?', "%#{label_list}%") if label_list.present? } scope :filter_by_assigned_agent_id, lambda { |assigned_agent_id| joins(:conversation).where(conversations: { assignee_id: assigned_agent_id }) if assigned_agent_id.present? } scope :missed, -> { where(sla_status: %i[missed active_with_misses]) } scope :with_sla_applicable_conversation, -> { where(conversation_id: Conversation.with_sla_applicable_contact.select(:id)) } after_update_commit :push_conversation_event def push_event_data sla_due_at_values = due_at_values { id: id, sla_id: sla_policy_id, sla_status: sla_status, created_at: created_at.to_i, updated_at: updated_at.to_i, sla_description: sla_policy.description, sla_name: sla_policy.name, sla_first_response_time_threshold: sla_policy.first_response_time_threshold, sla_next_response_time_threshold: sla_policy.next_response_time_threshold, sla_only_during_business_hours: sla_policy.only_during_business_hours, sla_resolution_time_threshold: sla_policy.resolution_time_threshold, sla_frt_due_at: sla_due_at_values[:frt], sla_nrt_due_at: sla_due_at_values[:nrt], sla_rt_due_at: sla_due_at_values[:rt] } end def due_at_values working_hours_by_day_cache = conversation.inbox.working_hours.index_by(&:day_of_week) if sla_policy.only_during_business_hours? { frt: frt_due_at(working_hours_by_day_cache: working_hours_by_day_cache), nrt: nrt_due_at(working_hours_by_day_cache: working_hours_by_day_cache), rt: rt_due_at(working_hours_by_day_cache: working_hours_by_day_cache) } end def frt_due_at(working_hours_by_day_cache: nil) return nil if sla_policy.first_response_time_threshold.blank? calculate_due_at( conversation.created_at, sla_policy.first_response_time_threshold, working_hours_by_day_cache: working_hours_by_day_cache ) end def nrt_due_at(working_hours_by_day_cache: nil) return nil if sla_policy.next_response_time_threshold.blank? return nil if conversation.waiting_since.blank? calculate_due_at( conversation.waiting_since, sla_policy.next_response_time_threshold, working_hours_by_day_cache: working_hours_by_day_cache ) end def rt_due_at(working_hours_by_day_cache: nil) return nil if sla_policy.resolution_time_threshold.blank? calculate_due_at( conversation.created_at, sla_policy.resolution_time_threshold, working_hours_by_day_cache: working_hours_by_day_cache ) end def calculate_due_at(start_time, threshold_seconds, working_hours_by_day_cache: nil) return (start_time + threshold_seconds.to_i.seconds).to_i unless sla_policy.only_during_business_hours? Sla::BusinessHoursService.new( inbox: conversation.inbox, start_time: start_time, threshold_seconds: threshold_seconds, working_hours_by_day_cache: working_hours_by_day_cache ).deadline.to_i end private def push_conversation_event # right now we simply use `CONVERSATION_UPDATED` event to notify the frontend # we can eventually start using `CONVERSATION_SLA_UPDATED` event as required later # for now the updated event should suffice return unless saved_change_to_sla_status? conversation.dispatch_conversation_updated_event end def ensure_account_id self.account_id ||= sla_policy&.account_id end end