# Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/ # Adds close time (if missing) when tickets are closed. class Ticket::Stats attr_accessor :user_id, :organization_ids, :current_user, :limit def initialize(current_user:, assets:, user_id: nil, organization_id: nil, limit: 100) @user_id = user_id @organization_ids = Array.wrap(organization_id) @current_user = current_user @limit = limit @assets = assets end def list_stats_user user = User.lookup(id: user_id) return if !user result = {} conditions = { closed_ids: { 'ticket.state_id' => { operator: 'is', value: Ticket::State.by_category_ids(:closed), }, 'ticket.customer_id' => { operator: 'is', value: user.id, }, }, open_ids: { 'ticket.state_id' => { operator: 'is', value: Ticket::State.by_category_ids(:open), }, 'ticket.customer_id' => { operator: 'is', value: user.id, }, }, } conditions.each do |key, local_condition| result[key] = search_stats(local_condition) end # generate stats by user condition = { 'tickets.customer_id' => user.id, } result[:volume_by_year] = search_stats_year(condition) result end def list_stats_organization result = {} organization_ids.select do |organization_id| Organization.lookup(id: organization_id).present? end return if organization_ids.blank? conditions = { closed_ids: { 'ticket.state_id' => { operator: 'is', value: Ticket::State.by_category_ids(:closed), }, 'ticket.organization_id' => { operator: 'is', value: organization_ids, }, }, open_ids: { 'ticket.state_id' => { operator: 'is', value: Ticket::State.by_category_ids(:open), }, 'ticket.organization_id' => { operator: 'is', value: organization_ids, }, }, } conditions.each do |key, local_condition| result[key] = search_stats(local_condition) end # generate stats by org condition = { 'tickets.organization_id' => organization_ids, } result[:volume_by_year] = search_stats_year(condition) result end def list_stats { user: list_stats_user || {}, organization: list_stats_organization || {}, assets: @assets, } end def search_stats(condition) tickets = Ticket.search( limit: limit, condition: condition, current_user: current_user, sort_by: 'created_at', order_by: 'desc', ) assets_of_tickets(tickets) end def search_stats_year(condition) volume_by_year = [] now = Time.zone.now (0..11).each do |month_back| date_to_check = now - month_back.month date_start = "#{date_to_check.year}-#{date_to_check.month}-01 00:00:00" date_end = "#{date_to_check.year}-#{date_to_check.month}-#{date_to_check.end_of_month.day} 00:00:00" # created created = TicketPolicy::ReadScope.new(current_user).resolve .where(created_at: (date_start..date_end)) .where(condition) .count # closed closed = TicketPolicy::ReadScope.new(current_user).resolve .where(close_at: (date_start..date_end)) .where(condition) .count data = { month: date_to_check.month, year: date_to_check.year, text: Date::MONTHNAMES[date_to_check.month], created: created, closed: closed, } volume_by_year.push data end volume_by_year end private def assets_of_tickets(tickets) ticket_ids = [] tickets.each do |ticket| ticket_ids.push ticket.id @assets = ticket.assets(@assets) end ticket_ids end end