Merge branch 'feature/suspend_escalation' into develop
This commit is contained in:
commit
7a2ab735a9
4 changed files with 320 additions and 12 deletions
|
@ -611,15 +611,27 @@ class Ticket < ApplicationModel
|
|||
|
||||
# first response
|
||||
if sla_selected.first_response_time
|
||||
self.first_response_escal_date = TimeCalculation.dest_time( created_at, sla_selected.first_response_time, sla_selected.data, sla_selected.timezone )
|
||||
|
||||
# get escalation date without pending time
|
||||
self.first_response_escal_date = TimeCalculation.dest_time( self.created_at, sla_selected.first_response_time, sla_selected.data, sla_selected.timezone )
|
||||
|
||||
# get pending time between created and first response escal. time
|
||||
time_in_pending = escalation_suspend( self.created_at, self.first_response_escal_date, 'relative', sla_selected )
|
||||
|
||||
# get new escalation time (original escal_date + time_in_pending)
|
||||
self.first_response_escal_date = TimeCalculation.dest_time( self.first_response_escal_date, time_in_pending.to_i, sla_selected.data, sla_selected.timezone )
|
||||
|
||||
# set ticket escalation
|
||||
self.escalation_time = self._escalation_calculation_higher_time( self.escalation_time, self.first_response_escal_date, self.first_response )
|
||||
end
|
||||
if self.first_response# && !self.first_response_in_min
|
||||
self.first_response_in_min = TimeCalculation.business_time_diff( self.created_at, self.first_response, sla_selected.data, sla_selected.timezone )
|
||||
|
||||
# get response time in min between created and first response
|
||||
self.first_response_in_min = escalation_suspend( self.created_at, self.first_response, 'real', sla_selected )
|
||||
|
||||
end
|
||||
# set sla time
|
||||
|
||||
# set time to show if sla is raised ot in
|
||||
if sla_selected.first_response_time && self.first_response_in_min
|
||||
self.first_response_diff_in_min = sla_selected.first_response_time - self.first_response_in_min
|
||||
end
|
||||
|
@ -648,13 +660,21 @@ class Ticket < ApplicationModel
|
|||
|
||||
# close time
|
||||
if sla_selected.close_time
|
||||
|
||||
# get escalation date without pending time
|
||||
self.close_time_escal_date = TimeCalculation.dest_time( self.created_at, sla_selected.close_time, sla_selected.data, sla_selected.timezone )
|
||||
|
||||
# get pending time between created and close escal. time
|
||||
extended_escalation = escalation_suspend( self.created_at, self.close_time_escal_date, 'relative', sla_selected )
|
||||
|
||||
# get new escalation time (original escal_date + time_in_pending)
|
||||
self.close_time_escal_date = TimeCalculation.dest_time( self.close_time_escal_date, extended_escalation.to_i, sla_selected.data, sla_selected.timezone )
|
||||
|
||||
# set ticket escalation
|
||||
self.escalation_time = self._escalation_calculation_higher_time( self.escalation_time, self.close_time_escal_date, self.close_time )
|
||||
end
|
||||
if self.close_time # && !self.close_time_in_min
|
||||
self.close_time_in_min = TimeCalculation.business_time_diff( self.created_at, self.close_time, sla_selected.data, sla_selected.timezone )
|
||||
self.close_time_in_min = escalation_suspend( self.created_at, self.close_time, 'real', sla_selected )
|
||||
end
|
||||
# set sla time
|
||||
if sla_selected.close_time && self.close_time_in_min
|
||||
|
@ -703,6 +723,106 @@ class Ticket < ApplicationModel
|
|||
self.articles.destroy_all
|
||||
end
|
||||
|
||||
#type could be:
|
||||
# real - time without supsend state
|
||||
# relative - only suspend time
|
||||
|
||||
def escalation_suspend (start_time, end_time, type, sla_selected)
|
||||
total_time_without_pending = 0
|
||||
total_time = 0
|
||||
#get history for ticket
|
||||
history_list = History.history_list( 'Ticket', self.id, 'Ticket' )
|
||||
|
||||
#loop through hist. changes and get time
|
||||
last_state = nil
|
||||
last_state_change = nil
|
||||
last_state_is_pending = false
|
||||
history_list.each { |history_item|
|
||||
|
||||
# ignore if it isn't a state change
|
||||
next if history_item['history_attribute'] != 'ticket_state'
|
||||
|
||||
# ignore all older state changes after end_time
|
||||
next if last_state_change && last_state_change > end_time
|
||||
|
||||
# if created_at is later then end_time, use end_time as last time
|
||||
if history_item['created_at'] > end_time
|
||||
history_item['created_at'] = end_time
|
||||
end
|
||||
|
||||
# get initial state and time
|
||||
if !last_state
|
||||
last_state = history_item['value_from']
|
||||
last_state_change = start_time
|
||||
end
|
||||
|
||||
# use time if ticket got from e. g. open to pending
|
||||
if history_item['value_from'] != 'pending' && history_item['value_to'] == 'pending'
|
||||
diff = escalation_time_diff( last_state_change, history_item['created_at'], sla_selected )
|
||||
puts "Diff count !=pending -> ==pending #{diff.to_s} - #{last_state_change} - #{history_item['created_at']}"
|
||||
total_time_without_pending = total_time_without_pending + diff
|
||||
total_time = total_time + diff
|
||||
last_state_is_pending = true
|
||||
|
||||
# use time if ticket got from e. g. open to open
|
||||
elsif history_item['value_from'] != 'pending' && history_item['value_to'] != 'pending'
|
||||
diff = escalation_time_diff( last_state_change, history_item['created_at'], sla_selected )
|
||||
puts "Diff count !=pending -> !=pending #{diff.to_s} - #{last_state_change} - #{history_item['created_at']}"
|
||||
total_time_without_pending = total_time_without_pending + diff
|
||||
total_time = total_time + diff
|
||||
last_state_is_pending = false
|
||||
elsif history_item['value_from'] == 'pending' && history_item['value_to'] != 'pending'
|
||||
diff = escalation_time_diff( last_state_change, history_item['created_at'], sla_selected )
|
||||
puts "Diff not count ==pending -> !=pending #{diff.to_s} - #{last_state_change} - #{history_item['created_at']}"
|
||||
total_time = total_time + diff
|
||||
last_state_is_pending = false
|
||||
# no pending state, do not count
|
||||
else
|
||||
puts "Diff do not count #{history_item['value_from']}->#{history_item['value_to']} -> #{history_item['created_at']}"
|
||||
last_state_is_pending = false
|
||||
end
|
||||
|
||||
# remember for next loop last state
|
||||
last_state = history_item['value_to']
|
||||
last_state_change = history_item['created_at']
|
||||
}
|
||||
|
||||
# if last state isnt pending, count rest
|
||||
if !last_state_is_pending && last_state_change && last_state_change < end_time
|
||||
diff = escalation_time_diff( last_state_change, end_time, sla_selected )
|
||||
puts "Diff count last state was not pending #{diff.to_s} - #{last_state_change} - #{end_time}"
|
||||
total_time_without_pending = total_time_without_pending + diff
|
||||
total_time = total_time + diff
|
||||
end
|
||||
|
||||
# if we have not had any state change
|
||||
if !last_state_change
|
||||
diff = escalation_time_diff( start_time, end_time, sla_selected )
|
||||
puts 'Diff state has not changed ' + diff.to_s
|
||||
total_time_without_pending = total_time_without_pending + diff
|
||||
total_time = total_time + diff
|
||||
end
|
||||
|
||||
#return sum
|
||||
if (type == 'real')
|
||||
return total_time_without_pending
|
||||
elsif (type == 'relative')
|
||||
relative = total_time - total_time_without_pending
|
||||
return relative
|
||||
else
|
||||
raise "ERROR: Unknown type #{type}"
|
||||
end
|
||||
end
|
||||
|
||||
def escalation_time_diff( start_time, end_time, sla_selected )
|
||||
if sla_selected
|
||||
diff = TimeCalculation.business_time_diff( start_time, end_time, sla_selected.data, sla_selected.timezone)
|
||||
else
|
||||
diff = TimeCalculation.business_time_diff( start_time, end_time )
|
||||
end
|
||||
diff
|
||||
end
|
||||
|
||||
class Number
|
||||
end
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ put working hours matrix and timezone in function, returns UTC working hours mat
|
|||
|
||||
=end
|
||||
|
||||
def self.business_time_diff(start_time, end_time, config, timezone = '')
|
||||
def self.business_time_diff(start_time, end_time, config = nil, timezone = '')
|
||||
if start_time.class == String
|
||||
start_time = Time.parse( start_time.to_s + 'UTC' )
|
||||
end
|
||||
|
@ -117,6 +117,12 @@ put working hours matrix and timezone in function, returns UTC working hours mat
|
|||
end_time = Time.parse( end_time.to_s + 'UTC' )
|
||||
end
|
||||
|
||||
# if no config is given, just return calculation directly
|
||||
if !config
|
||||
return ((end_time - start_time) / 60 ).round
|
||||
end
|
||||
|
||||
|
||||
working_hours = self.working_hours(start_time, config, timezone)
|
||||
|
||||
week_day_map = {
|
||||
|
@ -225,11 +231,18 @@ put working hours matrix and timezone in function, returns UTC working hours mat
|
|||
|
||||
=end
|
||||
|
||||
def self.dest_time(start_time, diff_in_min, config, timezone = '')
|
||||
def self.dest_time(start_time, diff_in_min, config = nil, timezone = '')
|
||||
if start_time.class == String
|
||||
start_time = Time.parse( start_time.to_s + ' UTC' )
|
||||
end
|
||||
|
||||
return start_time if diff_in_min == 0
|
||||
|
||||
# if no config is given, just return calculation directly
|
||||
if !config
|
||||
return start_time + (diff_in_min * 60)
|
||||
end
|
||||
|
||||
# loop
|
||||
working_hours = self.working_hours(start_time, config, timezone)
|
||||
|
||||
|
@ -277,8 +290,12 @@ put working hours matrix and timezone in function, returns UTC working hours mat
|
|||
# fillup to first full hour
|
||||
if first_loop
|
||||
|
||||
# get rest of this hour
|
||||
diff = 3600 - (start_time - start_time.beginning_of_hour)
|
||||
# get rest of this hour if diff_in_min in lower the one hour
|
||||
diff_to_count = 3600
|
||||
if diff_to_count > (diff_in_min * 60)
|
||||
diff_to_count = diff_in_min * 60
|
||||
end
|
||||
diff = diff_to_count - (start_time - start_time.beginning_of_hour)
|
||||
start_time += diff
|
||||
|
||||
# check if it's countable hour
|
||||
|
@ -307,6 +324,7 @@ put working hours matrix and timezone in function, returns UTC working hours mat
|
|||
|
||||
# check if it's business hour and count
|
||||
if working_hours[ week_day_map[week_day] ][ next_hour ]
|
||||
|
||||
# check if count is within this hour
|
||||
if count > 59 * 60
|
||||
diff = 3600
|
||||
|
|
|
@ -571,4 +571,122 @@ class TicketTest < ActiveSupport::TestCase
|
|||
|
||||
end
|
||||
|
||||
test 'ticket escalation suspend' do
|
||||
|
||||
|
||||
ticket = Ticket.create(
|
||||
:title => 'some title äöüß3',
|
||||
:group => Group.lookup( :name => 'Users'),
|
||||
:customer_id => 2,
|
||||
:ticket_state => Ticket::State.lookup( :name => 'new' ),
|
||||
:ticket_priority => Ticket::Priority.lookup( :name => '2 normal' ),
|
||||
:created_at => '2013-06-04 09:00:00 UTC',
|
||||
:updated_at => '2013-06-04 09:00:00 UTC',
|
||||
:updated_by_id => 1,
|
||||
:created_by_id => 1,
|
||||
)
|
||||
assert( ticket, 'ticket created' )
|
||||
|
||||
# set ticket at 10:00 to pending
|
||||
History.history_create(
|
||||
:history_type => 'updated',
|
||||
:history_object => 'Ticket',
|
||||
:history_attribute => 'ticket_state',
|
||||
:o_id => ticket.id,
|
||||
:id_to => 3,
|
||||
:id_from => 2,
|
||||
:value_from => 'open',
|
||||
:value_to => 'pending',
|
||||
:created_by_id => 1,
|
||||
:created_at => '2013-06-04 10:00:00',
|
||||
:updated_at => '2013-06-04 10:00:00'
|
||||
)
|
||||
|
||||
# set ticket at 10:30 to open
|
||||
History.history_create(
|
||||
:history_type => 'updated',
|
||||
:history_object => 'Ticket',
|
||||
:history_attribute => 'ticket_state',
|
||||
:o_id => ticket.id,
|
||||
:id_to => 2,
|
||||
:id_from => 3,
|
||||
:value_from => 'pending',
|
||||
:value_to => 'open',
|
||||
:created_by_id => 1,
|
||||
:created_at => '2013-06-04 10:30:00',
|
||||
:updated_at => '2013-06-04 10:30:00'
|
||||
)
|
||||
|
||||
# set ticket from 11:00 to pending
|
||||
#History.history_create(
|
||||
# :history_type => 'updated',
|
||||
# :history_object => 'Ticket',
|
||||
# :history_attribute => 'ticket_state',
|
||||
# :o_id => ticket.id,
|
||||
# :id_to => 3,
|
||||
# :id_from => 2,
|
||||
# :value_from => 'open',
|
||||
# :value_to => 'pending',
|
||||
# :created_by_id => 1,
|
||||
# :created_at => '2013-06-04 11:00:00',
|
||||
# :updated_at => '2013-06-04 11:00:00'
|
||||
#)
|
||||
|
||||
# set first response in time
|
||||
ticket.update_attributes(
|
||||
:first_response => '2013-06-04 10:45:00 UTC',
|
||||
)
|
||||
# set ticket from 11:30 to closed
|
||||
History.history_create(
|
||||
:history_type => 'updated',
|
||||
:history_object => 'Ticket',
|
||||
:history_attribute => 'ticket_state',
|
||||
:o_id => ticket.id,
|
||||
:id_to => 3,
|
||||
:id_from => 2,
|
||||
:value_from => 'open',
|
||||
:value_to => 'closed',
|
||||
:created_by_id => 1,
|
||||
:created_at => '2013-06-04 12:00:00',
|
||||
:updated_at => '2013-06-04 12:00:00'
|
||||
)
|
||||
|
||||
ticket.update_attributes(
|
||||
:close_time => '2013-06-04 12:00:00 UTC',
|
||||
)
|
||||
|
||||
# set sla's for timezone "Europe/Berlin" summertime (+2), so UTC times are 7:00-16:00
|
||||
sla = Sla.create(
|
||||
:name => 'test sla 1',
|
||||
:condition => {},
|
||||
:data => {
|
||||
"Mon"=>"Mon", "Tue"=>"Tue", "Wed"=>"Wed", "Thu"=>"Thu", "Fri"=>"Fri", "Sat"=>"Sat", "Sun"=>"Sun",
|
||||
"beginning_of_workday" => "9:00",
|
||||
"end_of_workday" => "18:00",
|
||||
},
|
||||
:timezone => 'Europe/Berlin',
|
||||
:first_response_time => 120,
|
||||
:update_time => 180,
|
||||
:close_time => 240,
|
||||
:active => true,
|
||||
:updated_by_id => 1,
|
||||
:created_by_id => 1,
|
||||
)
|
||||
ticket = Ticket.find(ticket.id)
|
||||
assert_equal( ticket.escalation_time.gmtime.to_s, '2013-06-04 12:00:00 UTC', 'ticket.escalation_time verify 1' ) #check escal. time because first resp. is already done
|
||||
assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-06-04 11:30:00 UTC', 'ticket.first_response_escal_date verify 1' )
|
||||
assert_equal( ticket.first_response_in_min, 75, 'ticket.first_response_in_min verify 3' )
|
||||
assert_equal( ticket.first_response_diff_in_min, 45, 'ticket.first_response_diff_in_min verify 3' )
|
||||
#assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-06-04 12:30:00 UTC', 'ticket.update_time_escal_date verify 1' )
|
||||
#assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-06-04 13:30:00 UTC', 'ticket.close_time_escal_date verify 1' )
|
||||
assert_equal( ticket.close_time_in_min, 150, 'ticket.close_time_in_min verify 3' )
|
||||
assert_equal( ticket.close_time_diff_in_min, 90, 'ticket.close_time_diff_in_min# verify 3' )
|
||||
delete = sla.destroy
|
||||
assert( delete, "sla destroy" )
|
||||
|
||||
delete = ticket.destroy
|
||||
assert( delete, "ticket destroy" )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -238,6 +238,13 @@ class WorkingTimeTest < ActiveSupport::TestCase
|
|||
'end_of_workday' => '6:00 pm',
|
||||
},
|
||||
},
|
||||
|
||||
# test 15
|
||||
{
|
||||
:start => '2013-08-29 16:01:00',
|
||||
:end => '2013-08-29 16:10:59',
|
||||
:diff => 10,
|
||||
},
|
||||
]
|
||||
tests.each { |test|
|
||||
diff = TimeCalculation.business_time_diff( test[:start], test[:end], test[:config], test[:timezone] )
|
||||
|
@ -495,6 +502,51 @@ class WorkingTimeTest < ActiveSupport::TestCase
|
|||
},
|
||||
},
|
||||
|
||||
# test 17
|
||||
{
|
||||
:start => '2013-10-21 04:01:00',
|
||||
:dest_time => '2013-10-21 06:00:00',
|
||||
:diff => 119,
|
||||
},
|
||||
|
||||
# test 18
|
||||
{
|
||||
:start => '2013-10-21 04:01:00',
|
||||
:dest_time => '2013-10-21 04:01:00',
|
||||
:diff => 0,
|
||||
},
|
||||
|
||||
# test 19
|
||||
{
|
||||
:start => '2013-04-12 21:20:15',
|
||||
:dest_time => '2013-04-12 21:20:15',
|
||||
:diff => 0,
|
||||
:config => {
|
||||
'Mon' => true,
|
||||
'Tue' => true,
|
||||
'Wed' => true,
|
||||
'Thu' => true,
|
||||
'Fri' => true,
|
||||
'beginning_of_workday' => '8:00 am',
|
||||
'end_of_workday' => '6:00 pm',
|
||||
},
|
||||
},
|
||||
|
||||
# test 20
|
||||
{
|
||||
:start => '2013-04-12 11:20:15',
|
||||
:dest_time => '2013-04-12 11:21:15',
|
||||
:diff => 1,
|
||||
:config => {
|
||||
'Mon' => true,
|
||||
'Tue' => true,
|
||||
'Wed' => true,
|
||||
'Thu' => true,
|
||||
'Fri' => true,
|
||||
'beginning_of_workday' => '8:00 am',
|
||||
'end_of_workday' => '6:00 pm',
|
||||
},
|
||||
},
|
||||
]
|
||||
tests.each { |test|
|
||||
dest_time = TimeCalculation.dest_time( test[:start] + ' UTC', test[:diff], test[:config], test[:timezone] )
|
||||
|
|
Loading…
Reference in a new issue