2017-01-16 13:34:44 +00:00
|
|
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
|
|
|
|
|
|
class TimeAccountingsController < ApplicationController
|
2017-02-15 12:29:25 +00:00
|
|
|
prepend_before_action { authentication_check(permission: 'admin.time_accounting') }
|
2017-01-16 13:34:44 +00:00
|
|
|
|
|
|
|
def by_ticket
|
|
|
|
|
|
|
|
year = params[:year] || Time.zone.now.year
|
|
|
|
month = params[:month] || Time.zone.now.month
|
|
|
|
|
2017-09-05 19:24:43 +00:00
|
|
|
start_periode = Time.zone.parse("#{year}-#{month}-01")
|
2017-01-16 13:34:44 +00:00
|
|
|
end_periode = start_periode.end_of_month
|
|
|
|
|
|
|
|
time_unit = {}
|
2017-10-01 12:25:52 +00:00
|
|
|
Ticket::TimeAccounting.where('created_at >= ? AND created_at <= ?', start_periode, end_periode).pluck(:ticket_id, :time_unit, :created_by_id).each do |record|
|
2017-01-16 13:34:44 +00:00
|
|
|
if !time_unit[record[0]]
|
|
|
|
time_unit[record[0]] = {
|
|
|
|
time_unit: 0,
|
|
|
|
agent_id: record[2],
|
|
|
|
}
|
|
|
|
end
|
|
|
|
time_unit[record[0]][:time_unit] += record[1]
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-01-16 13:34:44 +00:00
|
|
|
customers = {}
|
|
|
|
organizations = {}
|
|
|
|
agents = {}
|
|
|
|
results = []
|
2017-10-01 12:25:52 +00:00
|
|
|
time_unit.each do |ticket_id, local_time_unit|
|
2017-01-16 13:34:44 +00:00
|
|
|
ticket = Ticket.lookup(id: ticket_id)
|
|
|
|
next if !ticket
|
|
|
|
if !customers[ticket.customer_id]
|
|
|
|
customers[ticket.customer_id] = '-'
|
|
|
|
if ticket.customer_id
|
|
|
|
customer_user = User.lookup(id: ticket.customer_id)
|
|
|
|
if customer_user
|
|
|
|
customers[ticket.customer_id] = customer_user.fullname
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if !organizations[ticket.organization_id]
|
|
|
|
organizations[ticket.organization_id] = '-'
|
|
|
|
if ticket.organization_id
|
|
|
|
organization = Organization.lookup(id: ticket.organization_id)
|
|
|
|
if organization
|
|
|
|
organizations[ticket.organization_id] = organization.name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-09-28 06:49:06 +00:00
|
|
|
if !agents[local_time_unit[:agent_id]]
|
2017-01-16 13:34:44 +00:00
|
|
|
agent_user = User.lookup(id: local_time_unit[:agent_id])
|
|
|
|
agent = '-'
|
|
|
|
if agent_user
|
|
|
|
agents[local_time_unit[:agent_id]] = agent_user.fullname
|
|
|
|
end
|
|
|
|
end
|
|
|
|
result = {
|
|
|
|
ticket: ticket.attributes,
|
|
|
|
time_unit: local_time_unit[:time_unit],
|
|
|
|
customer: customers[ticket.customer_id],
|
|
|
|
organization: organizations[ticket.organization_id],
|
|
|
|
agent: agents[local_time_unit[:agent_id]],
|
|
|
|
}
|
|
|
|
results.push result
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-04-21 06:47:28 +00:00
|
|
|
|
|
|
|
if params[:download]
|
|
|
|
header = [
|
|
|
|
{
|
|
|
|
name: 'Ticket#',
|
|
|
|
width: 15,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Title',
|
|
|
|
width: 30,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Customer',
|
|
|
|
width: 20,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Organization',
|
|
|
|
width: 20,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Agent',
|
|
|
|
width: 20,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Time Units',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Time Units Total',
|
|
|
|
width: 10,
|
|
|
|
},
|
2017-07-06 09:05:28 +00:00
|
|
|
{
|
|
|
|
name: 'Created at',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Closed at',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Close Escalation At',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Close In Min',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Close Diff In Min',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'First Response At',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'First Response Escalation At',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'First Response In Min',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'First Response Diff In Min',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Update Escalation At',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Update In Min',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Update Diff In Min',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Last Contact At',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Last Contact Agent At',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Last Contact Customer At',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Article Count',
|
|
|
|
width: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Escalation At',
|
|
|
|
width: 10,
|
|
|
|
},
|
2017-04-21 06:47:28 +00:00
|
|
|
]
|
2018-04-27 13:35:20 +00:00
|
|
|
objects = ObjectManager::Attribute.where(editable: true,
|
|
|
|
active: true,
|
|
|
|
object_lookup_id: ObjectLookup.lookup(name: 'Ticket').id)
|
|
|
|
.pluck(:name, :display, :data_type, :data_option)
|
|
|
|
.map { |name, display, data_type, data_option| { name: name, display: display, data_type: data_type, data_option: data_option } }
|
|
|
|
objects.each do |object|
|
|
|
|
header.push({ name: object[:display], width: 10 })
|
|
|
|
end
|
|
|
|
|
2017-04-21 06:47:28 +00:00
|
|
|
result = []
|
2017-10-01 12:25:52 +00:00
|
|
|
results.each do |row|
|
2017-11-23 08:09:44 +00:00
|
|
|
row[:ticket].each_key do |field|
|
2017-07-06 09:05:28 +00:00
|
|
|
next if row[:ticket][field].blank?
|
2017-07-06 11:47:33 +00:00
|
|
|
next if !row[:ticket][field].is_a?(ActiveSupport::TimeWithZone)
|
2017-07-06 09:05:28 +00:00
|
|
|
|
|
|
|
row[:ticket][field] = row[:ticket][field].iso8601
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-07-06 09:05:28 +00:00
|
|
|
|
2017-04-21 06:47:28 +00:00
|
|
|
result_row = [
|
|
|
|
row[:ticket]['number'],
|
|
|
|
row[:ticket]['title'],
|
|
|
|
row[:customer],
|
|
|
|
row[:organization],
|
|
|
|
row[:agent],
|
|
|
|
row[:time_unit],
|
|
|
|
row[:ticket]['time_unit'],
|
2017-07-06 09:05:28 +00:00
|
|
|
row[:ticket]['created_at'],
|
|
|
|
row[:ticket]['close_at'],
|
|
|
|
row[:ticket]['close_escalation_at'],
|
|
|
|
row[:ticket]['close_in_min'],
|
|
|
|
row[:ticket]['close_diff_in_min'],
|
|
|
|
row[:ticket]['first_response_at'],
|
|
|
|
row[:ticket]['first_response_escalation_at'],
|
|
|
|
row[:ticket]['first_response_in_min'],
|
|
|
|
row[:ticket]['first_response_diff_in_min'],
|
|
|
|
row[:ticket]['update_escalation_at'],
|
|
|
|
row[:ticket]['update_in_min'],
|
|
|
|
row[:ticket]['update_diff_in_min'],
|
|
|
|
row[:ticket]['last_contact_at'],
|
|
|
|
row[:ticket]['last_contact_agent_at'],
|
|
|
|
row[:ticket]['last_contact_customer_at'],
|
|
|
|
row[:ticket]['article_count'],
|
|
|
|
row[:ticket]['escalation_at'],
|
2017-04-21 06:47:28 +00:00
|
|
|
]
|
2018-04-27 13:35:20 +00:00
|
|
|
|
|
|
|
# needed to get human values for boolean/select rather than true/false values
|
|
|
|
ticket = Ticket.lookup(id: row[:ticket]['id'])
|
|
|
|
|
|
|
|
# Object Manager attributes
|
|
|
|
# We already queried ObjectManager::Attributes, so we just use objects
|
|
|
|
objects.each do |object|
|
|
|
|
key = object[:name]
|
|
|
|
case object[:data_type]
|
|
|
|
when 'boolean', 'select'
|
|
|
|
value = object[:data_option]['options'][ticket.send(key.to_sym)]
|
|
|
|
value.present? ? result_row.push(value) : result_row.push('')
|
|
|
|
when 'datetime', 'date'
|
|
|
|
row[:ticket][key].present? ? result_row.push(row[:ticket][key].to_time.iso8601) : result_row.push('')
|
|
|
|
else
|
|
|
|
# for text, integer and tree select
|
|
|
|
row[:ticket][key].present? ? result_row.push(row[:ticket][key]) : result_row.push('')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-04-21 06:47:28 +00:00
|
|
|
result.push result_row
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-04-21 06:47:28 +00:00
|
|
|
content = sheet("By Ticket #{year}-#{month}", header, result)
|
|
|
|
send_data(
|
|
|
|
content,
|
|
|
|
filename: "by_ticket-#{year}-#{month}.xls",
|
|
|
|
type: 'application/vnd.ms-excel',
|
|
|
|
disposition: 'attachment'
|
|
|
|
)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2017-01-16 13:34:44 +00:00
|
|
|
render json: results
|
|
|
|
end
|
|
|
|
|
|
|
|
def by_customer
|
|
|
|
|
|
|
|
year = params[:year] || Time.zone.now.year
|
|
|
|
month = params[:month] || Time.zone.now.month
|
|
|
|
|
2017-09-05 19:24:43 +00:00
|
|
|
start_periode = Time.zone.parse("#{year}-#{month}-01")
|
2017-01-16 13:34:44 +00:00
|
|
|
end_periode = start_periode.end_of_month
|
|
|
|
|
|
|
|
time_unit = {}
|
2017-10-01 12:25:52 +00:00
|
|
|
Ticket::TimeAccounting.where('created_at >= ? AND created_at <= ?', start_periode, end_periode).pluck(:ticket_id, :time_unit, :created_by_id).each do |record|
|
2017-01-16 13:34:44 +00:00
|
|
|
if !time_unit[record[0]]
|
|
|
|
time_unit[record[0]] = {
|
|
|
|
time_unit: 0,
|
|
|
|
agent_id: record[2],
|
|
|
|
}
|
|
|
|
end
|
|
|
|
time_unit[record[0]][:time_unit] += record[1]
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-01-16 13:34:44 +00:00
|
|
|
|
|
|
|
customers = {}
|
2017-10-01 12:25:52 +00:00
|
|
|
time_unit.each do |ticket_id, local_time_unit|
|
2017-01-16 13:34:44 +00:00
|
|
|
ticket = Ticket.lookup(id: ticket_id)
|
|
|
|
next if !ticket
|
|
|
|
if !customers[ticket.customer_id]
|
|
|
|
organization = nil
|
|
|
|
if ticket.organization_id
|
|
|
|
organization = Organization.lookup(id: ticket.organization_id).attributes
|
|
|
|
end
|
|
|
|
customers[ticket.customer_id] = {
|
|
|
|
customer: User.lookup(id: ticket.customer_id).attributes,
|
|
|
|
organization: organization,
|
|
|
|
time_unit: local_time_unit[:time_unit],
|
|
|
|
}
|
|
|
|
next
|
|
|
|
end
|
|
|
|
customers[ticket.customer_id][:time_unit] += local_time_unit[:time_unit]
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-01-16 13:34:44 +00:00
|
|
|
results = []
|
2017-11-23 08:09:44 +00:00
|
|
|
customers.each_value do |content|
|
2017-01-16 13:34:44 +00:00
|
|
|
results.push content
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-04-21 06:47:28 +00:00
|
|
|
|
|
|
|
if params[:download]
|
|
|
|
header = [
|
|
|
|
{
|
|
|
|
name: 'Customer',
|
|
|
|
width: 30,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Organization',
|
|
|
|
width: 30,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Time Units',
|
|
|
|
width: 10,
|
|
|
|
}
|
|
|
|
]
|
|
|
|
result = []
|
2017-10-01 12:25:52 +00:00
|
|
|
results.each do |row|
|
2017-04-21 06:47:28 +00:00
|
|
|
customer_name = User.find(row[:customer]['id']).fullname
|
|
|
|
organization_name = ''
|
|
|
|
if row[:organization].present?
|
|
|
|
organization_name = row[:organization]['name']
|
|
|
|
end
|
|
|
|
result_row = [customer_name, organization_name, row[:time_unit]]
|
|
|
|
result.push result_row
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-04-21 06:47:28 +00:00
|
|
|
content = sheet("By Customer #{year}-#{month}", header, result)
|
|
|
|
send_data(
|
|
|
|
content,
|
|
|
|
filename: "by_customer-#{year}-#{month}.xls",
|
|
|
|
type: 'application/vnd.ms-excel',
|
|
|
|
disposition: 'attachment'
|
|
|
|
)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2017-01-16 13:34:44 +00:00
|
|
|
render json: results
|
|
|
|
end
|
|
|
|
|
|
|
|
def by_organization
|
|
|
|
|
|
|
|
year = params[:year] || Time.zone.now.year
|
|
|
|
month = params[:month] || Time.zone.now.month
|
|
|
|
|
2017-09-05 19:24:43 +00:00
|
|
|
start_periode = Time.zone.parse("#{year}-#{month}-01")
|
2017-01-16 13:34:44 +00:00
|
|
|
end_periode = start_periode.end_of_month
|
|
|
|
|
|
|
|
time_unit = {}
|
2017-10-01 12:25:52 +00:00
|
|
|
Ticket::TimeAccounting.where('created_at >= ? AND created_at <= ?', start_periode, end_periode).pluck(:ticket_id, :time_unit, :created_by_id).each do |record|
|
2017-01-16 13:34:44 +00:00
|
|
|
if !time_unit[record[0]]
|
|
|
|
time_unit[record[0]] = {
|
|
|
|
time_unit: 0,
|
|
|
|
agent_id: record[2],
|
|
|
|
}
|
|
|
|
end
|
|
|
|
time_unit[record[0]][:time_unit] += record[1]
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-01-16 13:34:44 +00:00
|
|
|
|
|
|
|
organizations = {}
|
2017-10-01 12:25:52 +00:00
|
|
|
time_unit.each do |ticket_id, local_time_unit|
|
2017-01-16 13:34:44 +00:00
|
|
|
ticket = Ticket.lookup(id: ticket_id)
|
|
|
|
next if !ticket
|
|
|
|
next if !ticket.organization_id
|
|
|
|
if !organizations[ticket.organization_id]
|
|
|
|
organizations[ticket.organization_id] = {
|
|
|
|
organization: Organization.lookup(id: ticket.organization_id).attributes,
|
|
|
|
time_unit: local_time_unit[:time_unit],
|
|
|
|
}
|
|
|
|
next
|
|
|
|
end
|
|
|
|
organizations[ticket.organization_id][:time_unit] += local_time_unit[:time_unit]
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-01-16 13:34:44 +00:00
|
|
|
results = []
|
2017-11-23 08:09:44 +00:00
|
|
|
organizations.each_value do |content|
|
2017-01-16 13:34:44 +00:00
|
|
|
results.push content
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-04-21 06:47:28 +00:00
|
|
|
|
|
|
|
if params[:download]
|
|
|
|
header = [
|
|
|
|
{
|
|
|
|
name: 'Organization',
|
|
|
|
width: 40,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Time Units',
|
|
|
|
width: 20,
|
|
|
|
}
|
|
|
|
]
|
|
|
|
result = []
|
2017-10-01 12:25:52 +00:00
|
|
|
results.each do |row|
|
2017-04-21 06:47:28 +00:00
|
|
|
organization_name = ''
|
|
|
|
if row[:organization].present?
|
|
|
|
organization_name = row[:organization]['name']
|
|
|
|
end
|
|
|
|
result_row = [organization_name, row[:time_unit]]
|
|
|
|
result.push result_row
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-04-21 06:47:28 +00:00
|
|
|
content = sheet("By Organization #{year}-#{month}", header, result)
|
|
|
|
send_data(
|
|
|
|
content,
|
|
|
|
filename: "by_organization-#{year}-#{month}.xls",
|
|
|
|
type: 'application/vnd.ms-excel',
|
|
|
|
disposition: 'attachment'
|
|
|
|
)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2017-01-16 13:34:44 +00:00
|
|
|
render json: results
|
|
|
|
end
|
|
|
|
|
2017-04-21 06:47:28 +00:00
|
|
|
private
|
|
|
|
|
|
|
|
def sheet(title, header, result)
|
|
|
|
|
|
|
|
# Create a new Excel workbook
|
|
|
|
temp_file = Tempfile.new('time_tracking.xls')
|
|
|
|
workbook = WriteExcel.new(temp_file)
|
|
|
|
|
|
|
|
# Add a worksheet
|
|
|
|
worksheet = workbook.add_worksheet
|
|
|
|
|
|
|
|
# Add and define a format
|
|
|
|
format = workbook.add_format # Add a format
|
|
|
|
format.set_bold
|
|
|
|
format.set_size(14)
|
|
|
|
format.set_color('black')
|
|
|
|
worksheet.set_row(0, 0, header.count)
|
|
|
|
|
|
|
|
# Write a formatted and unformatted string, row and column notation.
|
2018-01-22 14:37:42 +00:00
|
|
|
worksheet.write_string(0, 0, title, format)
|
2017-04-21 06:47:28 +00:00
|
|
|
|
|
|
|
format_header = workbook.add_format # Add a format
|
|
|
|
format_header.set_italic
|
|
|
|
format_header.set_bg_color('gray')
|
|
|
|
format_header.set_color('white')
|
|
|
|
count = 0
|
2017-10-01 12:25:52 +00:00
|
|
|
header.each do |item|
|
2017-04-21 06:47:28 +00:00
|
|
|
if item[:width]
|
|
|
|
worksheet.set_column(count, count, item[:width])
|
|
|
|
end
|
2018-01-22 14:37:42 +00:00
|
|
|
worksheet.write_string(2, count, item[:name], format_header)
|
2017-04-21 06:47:28 +00:00
|
|
|
count += 1
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-04-21 06:47:28 +00:00
|
|
|
|
|
|
|
row_count = 2
|
2017-10-01 12:25:52 +00:00
|
|
|
result.each do |row|
|
2017-04-21 06:47:28 +00:00
|
|
|
row_count += 1
|
|
|
|
row_item_count = 0
|
2017-10-01 12:25:52 +00:00
|
|
|
row.each do |item|
|
2018-01-22 14:37:42 +00:00
|
|
|
if item.acts_like?(:date)
|
|
|
|
worksheet.write_date_time(row_count, row_item_count, item.to_time.iso8601)
|
|
|
|
else
|
|
|
|
worksheet.write_string(row_count, row_item_count, item)
|
|
|
|
end
|
2017-04-21 06:47:28 +00:00
|
|
|
row_item_count += 1
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
|
|
|
end
|
2017-04-21 06:47:28 +00:00
|
|
|
|
|
|
|
workbook.close
|
|
|
|
|
|
|
|
# read file again
|
|
|
|
file = File.new(temp_file, 'r')
|
|
|
|
contents = file.read
|
|
|
|
file.close
|
|
|
|
contents
|
|
|
|
end
|
|
|
|
|
2017-01-16 13:34:44 +00:00
|
|
|
end
|