zammad/app/models/checklist.rb

126 lines
3.1 KiB
Ruby

# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
class Checklist < ApplicationModel
include HasDefaultModelUserRelations
include ChecksClientNotification
include HasHistory
include Checklist::SearchIndex
include Checklist::TriggersSubscriptions
include Checklist::Assets
include CanChecklistSortedItems
has_one :ticket, dependent: :nullify
has_many :items, inverse_of: :checklist, dependent: :destroy
validates :name, length: { maximum: 250 }
history_attributes_ignored :sorted_item_ids
# Those callbacks are necessary to trigger updates in legacy UI.
# First checklist item is created right after the checklist itself
# and it triggers update on the freshly created ticket.
# Thus no need for after_create callback.
after_update :update_ticket
after_destroy :update_ticket
def history_log_attributes
{
related_o_id: ticket.id,
related_history_object: 'Ticket',
}
end
def history_create
history_log('created', created_by_id, { value_to: name })
end
def history_destroy
history_log('removed', updated_by_id, { value_to: name })
end
def notify_clients_data_attributes
{
id: id,
ticket_id: ticket.id,
updated_at: updated_at,
updated_by_id: updated_by_id,
}
end
def completed?
incomplete.zero?
end
def incomplete
items.incomplete.count
end
def total
items.count
end
def complete
total - incomplete
end
# Returns scope to tickets tracking the given target ticket in their checklists.
# If a user is given, it returns tickets acccessible to that user only.
#
# @param target_ticket [Ticket, Integer] target ticket or it's id
# @param user [User] to optionally filter accessible tickets
def self.tickets_referencing(target_ticket, user = nil)
source_checklist_ids = joins(:items)
.where(items: { ticket: target_ticket })
.pluck(:id)
scope = Ticket.where(checklist_id: source_checklist_ids)
return scope if !user
TicketPolicy::ReadScope
.new(user, scope)
.resolve
end
def self.ticket_closed?(ticket)
state = Ticket::State.lookup id: ticket.state_id
state_type = Ticket::StateType.lookup id: state.state_type_id
%w[closed merged].include? state_type.name
end
def self.create_fresh!(ticket)
ActiveRecord::Base.transaction do
Checklist
.create!(ticket:)
.tap { |checklist| checklist.items.create! }
end
end
def self.create_from_template!(ticket, template)
if !template.active
raise Exceptions::UnprocessableContent, __('Checklist template must be active to use as a checklist starting point.')
end
ActiveRecord::Base.transaction do
Checklist.create!(name: template.name, ticket:)
.tap do |checklist|
sorted_item_ids = template
.items
.map { |elem| checklist.items.create!(text: elem.text, initial_clone: true) }
.pluck(:id)
checklist.update! sorted_item_ids:
end
end
end
private
def update_ticket
return if ticket.destroyed?
ticket.updated_at = Time.current
ticket.save!
end
end