From a9e65611fe029818e84b89edb75d63e6483561cf Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Thu, 10 Sep 2015 21:09:50 +0200 Subject: [PATCH] Moved to biz for working time calculations. Moved to new sla model. --- Gemfile | 2 + Gemfile.lock | 10 + .../javascripts/app/models/calendar.js.coffee | 3 + .../javascripts/app/models/sla.js.coffee | 27 +- app/models/calendar.rb | 32 +- app/models/sla.rb | 2 + app/models/ticket/escalation.rb | 234 ++++---- app/models/ticket/state.rb | 5 +- db/migrate/20150969000001_update_sla.rb | 8 + .../20150970000001_update_ticket_state.rb | 12 + db/seeds.rb | 14 +- lib/time_calculation.rb | 361 ------------ test/unit/calendar_test.rb | 93 +++ test/unit/ticket_sla_test.rb | 286 +++++---- test/unit/working_time_test.rb | 555 ------------------ 15 files changed, 459 insertions(+), 1185 deletions(-) create mode 100644 db/migrate/20150969000001_update_sla.rb create mode 100644 db/migrate/20150970000001_update_ticket_state.rb delete mode 100644 lib/time_calculation.rb create mode 100644 test/unit/calendar_test.rb delete mode 100644 test/unit/working_time_test.rb diff --git a/Gemfile b/Gemfile index 812ce076e..3ef1964fb 100644 --- a/Gemfile +++ b/Gemfile @@ -36,6 +36,8 @@ gem 'mail', '~> 2.5.0' gem 'mime-types' +gem 'biz' + gem 'delayed_job_active_record' gem 'daemons' diff --git a/Gemfile.lock b/Gemfile.lock index 1ddf0419c..c12e50bed 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,7 @@ GEM remote: http://rubygems.org/ specs: + abstract_type (0.0.7) actionmailer (4.1.13) actionpack (= 4.1.13) actionview (= 4.1.13) @@ -39,11 +40,19 @@ GEM autoprefixer-rails (6.0.2) execjs json + biz (1.3.0) + abstract_type (~> 0.0.0) + clavius (~> 1.0) + equalizer (~> 0.0.0) + memoizable (~> 0.4.0) + tzinfo browser (1.0.1) buftok (0.2.0) builder (3.2.2) childprocess (0.5.6) ffi (~> 1.0, >= 1.0.11) + clavius (1.0.1) + memoizable (~> 0.4.0) coderay (1.1.0) coffee-rails (4.1.0) coffee-script (>= 2.2.0) @@ -262,6 +271,7 @@ PLATFORMS DEPENDENCIES activerecord-session_store autoprefixer-rails (>= 5.2) + biz browser coffee-rails coffee-script-source diff --git a/app/assets/javascripts/app/models/calendar.js.coffee b/app/assets/javascripts/app/models/calendar.js.coffee index f38889002..8de639031 100644 --- a/app/assets/javascripts/app/models/calendar.js.coffee +++ b/app/assets/javascripts/app/models/calendar.js.coffee @@ -2,3 +2,6 @@ class App.Calendar extends App.Model @configure 'Calendar', 'name', 'timezone', 'default', 'business_hours', 'ical_url', 'public_holidays' @extend Spine.Model.Ajax @url: @apiPath + '/calendars' + + displayName: -> + "#{@name} - #{@timezone}" diff --git a/app/assets/javascripts/app/models/sla.js.coffee b/app/assets/javascripts/app/models/sla.js.coffee index edd3de234..cebe59f3e 100644 --- a/app/assets/javascripts/app/models/sla.js.coffee +++ b/app/assets/javascripts/app/models/sla.js.coffee @@ -1,5 +1,5 @@ class App.Sla extends App.Model - @configure 'Sla', 'name', 'first_response_time', 'update_time', 'close_time', 'condition', 'timezone', 'data', 'active', 'updated_at' + @configure 'Sla', 'name', 'first_response_time', 'update_time', 'close_time', 'condition', 'calendar_id' @extend Spine.Model.Ajax @url: @apiPath + '/slas' @configure_attributes = [ @@ -7,31 +7,8 @@ class App.Sla extends App.Model { name: 'first_response_time', display: 'First Response Time', tag: 'input', type: 'text', limit: 100, null: true, note: 'In minutes, only business times are counted.' }, { name: 'update_time', display: 'Update Time', tag: 'input', type: 'text', limit: 100, null: true, note: 'In minutes, only business times are counted.' }, { name: 'close_time', display: 'Solution Time', tag: 'input', type: 'text', limit: 100, null: true, note: 'In minutes, only business times are counted.' }, + { name: 'calendar_id', display: 'Calendar', tag: 'select', relation: 'Calendar', null: false }, { name: 'condition', display: 'Conditions where SLA is used', tag: 'ticket_attribute_selection', null: true }, - { name: 'timezone', display: 'Timezone', tag: 'timezone', null: true }, - { - name: 'data' - display: 'Business Times' - tag: 'working_hour' - default: - Mon: true - Tue: true - Wed: true - Thu: true - Fri: true - beginning_of_workday: '8:00' - end_of_workday: '18:00' - null: true - nulloption: true - translate: true - options: - customer: 'Customer' - state: 'State' - priority: 'Priority' - group: 'Group' - owner: 'Owner' - }, - { name: 'active', display: 'Active', tag: 'active', default: true }, { name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 }, { name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 }, { name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 }, diff --git a/app/models/calendar.rb b/app/models/calendar.rb index 73e52401f..d27359426 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -4,6 +4,10 @@ class Calendar < ApplicationModel store :business_hours store :public_holidays + after_create :sync_default, :min_one_check + after_update :sync_default, :min_one_check + after_destroy :min_one_check + =begin get default calendar @@ -100,11 +104,6 @@ returns def self.timezones list = {} TZInfo::Timezone.all_country_zone_identifiers.each { |timezone| - - # ignore the following time zones - #next if t.name =~ /^GMT/ - #next if t.name =~ /^Etc/ - #next if t.name !~ /\// t = TZInfo::Timezone.get(timezone) diff = t.current_period.utc_total_offset / 60 / 60 list[ timezone ] = diff @@ -194,4 +193,27 @@ returns } events.sort.to_h end + + private + + # if changed calendar is default, set all others default to false + def sync_default + return if !default + Calendar.all.each {|calendar| + next if calendar.id == id + next if !calendar.default + calendar.default = false + calendar.save + } + end + + # check if min one is set to default true + def min_one_check + Calendar.all.each {|calendar| + return if calendar.default + } + first = Calendar.first + first.default = true + first.save + end end diff --git a/app/models/sla.rb b/app/models/sla.rb index e590fe8c6..df1031992 100644 --- a/app/models/sla.rb +++ b/app/models/sla.rb @@ -5,6 +5,8 @@ class Sla < ApplicationModel store :data validates :name, presence: true + belongs_to :calendar + after_create :escalation_calculation_rebuild after_update :escalation_calculation_rebuild after_destroy :escalation_calculation_rebuild diff --git a/app/models/ticket/escalation.rb b/app/models/ticket/escalation.rb index 371d1625f..42c9b8f3c 100644 --- a/app/models/ticket/escalation.rb +++ b/app/models/ticket/escalation.rb @@ -14,9 +14,9 @@ returns =end def self.rebuild_all - state_list_open = Ticket::State.by_category( 'open' ) + state_list_open = Ticket::State.by_category('open') - tickets = Ticket.where( state_id: state_list_open ) + tickets = Ticket.where(state_id: state_list_open) tickets.each(&:escalation_calculation) end @@ -37,117 +37,119 @@ returns # set escalation off if ticket is already closed state = Ticket::State.lookup( id: state_id ) + escalation_disabled = false if state.ignore_escalation? - - # nothing to change - return true if !escalation_time - - self.escalation_time = nil - # self.first_response_escal_date = nil - # self.close_time_escal_date = nil - self.callback_loop = true - save - return true + escalation_disabled = true end # get sla for ticket - sla_selected = escalation_calculation_get_sla + calendar = nil + sla = escalation_calculation_get_sla + if sla + calendar = sla.calendar + end - # reset escalation if no sla is set - if !sla_selected + # if no escalation is enabled + if !sla # nothing to change return true if !escalation_time - self.escalation_time = nil - # self.first_response_escal_date = nil - # self.close_time_escal_date = nil - self.callback_loop = true + self.escalation_time = nil + self.callback_loop = true save return true end - # puts sla_selected.inspect - # puts days.inspect - self.escalation_time = nil - self.first_response_escal_date = nil - self.update_time_escal_date = nil - self.close_time_escal_date = nil + # reset escalation attributes + self.escalation_time = nil + self.first_response_escal_date = nil + self.update_time_escal_date = nil + self.close_time_escal_date = nil - # first response - if sla_selected.first_response_time - - # get escalation date without pending time - self.first_response_escal_date = TimeCalculation.dest_time( 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( created_at, first_response_escal_date, 'relative', sla_selected, sla_selected.first_response_time ) - - # get new escalation time (original escal_date + time_in_pending) - self.first_response_escal_date = TimeCalculation.dest_time( first_response_escal_date, time_in_pending.to_i, sla_selected.data, sla_selected.timezone ) - - # set ticket escalation - self.escalation_time = calculation_higher_time( escalation_time, first_response_escal_date, first_response ) + biz = Biz::Schedule.new do |config| + config.hours = calendar.business_hours.symbolize_keys + #config.holidays = [Date.new(2014, 1, 1), Date.new(2014, 12, 25)] + config.time_zone = calendar.timezone end + + # fist response + # calculate first response escalation + if sla.first_response_time + self.first_response_escal_date = biz.time(sla.first_response_time, :minutes).after(created_at) + pending_time = pending_minutes(created_at, first_response_escal_date, biz) + if pending_time && pending_time > 0 + self.first_response_escal_date = biz.time(pending_time, :minutes).after(first_response_escal_date) + end + end + + # get response time in min if first_response - - # get response time in min between created and first response - self.first_response_in_min = escalation_suspend( created_at, first_response, 'real', sla_selected ) - + self.first_response_in_min = pending_minutes(created_at, first_response, biz, 'business_minutes') + else + self.escalation_time = first_response_escal_date end - # set time to show if sla is raised ot in - if sla_selected.first_response_time && first_response_in_min - self.first_response_diff_in_min = sla_selected.first_response_time - first_response_in_min + # set time to show if sla is raised or not + if sla.first_response_time && first_response_in_min + self.first_response_diff_in_min = sla.first_response_time - first_response_in_min end # update time + # calculate escalation last_update = last_contact_agent if !last_update last_update = created_at end - if sla_selected.update_time - self.update_time_escal_date = TimeCalculation.dest_time( last_update, sla_selected.update_time, sla_selected.data, sla_selected.timezone ) - - # get pending time between created and update escal. time - time_in_pending = escalation_suspend( last_update, update_time_escal_date, 'relative', sla_selected, sla_selected.update_time ) - - # get new escalation time (original escal_date + time_in_pending) - self.update_time_escal_date = TimeCalculation.dest_time( update_time_escal_date, time_in_pending.to_i, sla_selected.data, sla_selected.timezone ) - - # set ticket escalation - self.escalation_time = calculation_higher_time( escalation_time, update_time_escal_date, false ) + if sla.update_time + self.update_time_escal_date = biz.time(sla.update_time, :minutes).after(last_update) + pending_time = pending_minutes(last_update, update_time_escal_date, biz) + if pending_time && pending_time > 0 + self.update_time_escal_date = biz.time(pending_time, :minutes).after(update_time_escal_date) + end end + if (!self.escalation_time && self.update_time_escal_date) || self.update_time_escal_date < self.escalation_time + self.escalation_time = self.update_time_escal_date + end + + # get update time in min if last_contact_agent - self.update_time_in_min = TimeCalculation.business_time_diff( created_at, last_contact_agent, sla_selected.data, sla_selected.timezone ) + self.update_time_in_min = pending_minutes(created_at, last_contact_agent, biz, 'business_minutes') end # set sla time - if sla_selected.update_time && update_time_in_min - self.update_time_diff_in_min = sla_selected.update_time - update_time_in_min + if sla.update_time && update_time_in_min + self.update_time_diff_in_min = sla.update_time - update_time_in_min end # close time - if sla_selected.close_time - - # get escalation date without pending time - self.close_time_escal_date = TimeCalculation.dest_time( 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( created_at, close_time_escal_date, 'relative', sla_selected, sla_selected.close_time ) - - # get new escalation time (original escal_date + time_in_pending) - self.close_time_escal_date = TimeCalculation.dest_time( close_time_escal_date, extended_escalation.to_i, sla_selected.data, sla_selected.timezone ) - - # set ticket escalation - self.escalation_time = calculation_higher_time( escalation_time, close_time_escal_date, close_time ) + # calculate close time escalation + if sla.close_time + self.close_time_escal_date = biz.time(sla.close_time, :minutes).after(created_at) + pending_time = pending_minutes(created_at, first_response_escal_date, biz) + if pending_time && pending_time > 0 + self.close_time_escal_date = biz.time(pending_time, :minutes).after(close_time_escal_date) + end end - if close_time # && !self.close_time_in_min - self.close_time_in_min = escalation_suspend( created_at, close_time, 'real', sla_selected ) + + # get close time in min + if close_time + self.close_time_in_min = pending_minutes(created_at, close_time, biz, 'business_minutes') + else + if (!self.escalation_time && self.close_time_escal_date) || self.close_time_escal_date < self.escalation_time + if !escalation_disabled + self.escalation_time = self.close_time_escal_date + end + end end - # set sla time - if sla_selected.close_time && close_time_in_min - self.close_time_diff_in_min = sla_selected.close_time - close_time_in_min + + # set time to show if sla is raised or not + if sla.close_time && close_time_in_min + self.close_time_diff_in_min = sla.close_time - close_time_in_min + end + + if escalation_disabled + self.escalation_time = nil end return if !self.changed? @@ -173,7 +175,7 @@ returns sla_selected = nil sla_list = Cache.get( 'SLA::List::Active' ) if sla_list.nil? - sla_list = Sla.where( active: true ) + sla_list = Sla.all Cache.write( 'SLA::List::Active', sla_list, { expires_in: 1.hour } ) end sla_list.each {|sla| @@ -208,24 +210,18 @@ returns private - #type could be: - # real - time without supsend state - # relative - only suspend time + # get business minutes of pending time + # type = business_minutes (pending time in business minutes) + # type = non_business_minutes (pending time in non business minutes) + def pending_minutes(start_time, end_time, biz, type = 'non_business_minutes') - def escalation_suspend (start_time, end_time, type, sla_selected, sla_time = 0) - if type == 'relative' - end_time += sla_time * 60 - end - total_time_without_pending = 0 - total_time = 0 - #get history for ticket - history_list = history_get - - #loop through hist. changes and get time - last_state = nil - last_state_change = nil + working_time_in_min = 0 + total_time_in_min = 0 + last_state = nil + last_state_change = nil last_state_is_pending = false - history_list.each { |history_item| + pending_minutes = 0 + history_get.each { |history_item| # ignore if it isn't a state change next if !history_item['attribute'] @@ -256,14 +252,14 @@ returns counted = false end - diff = escalation_time_diff( last_state_change, history_item['created_at'], sla_selected ) + diff = biz.within(last_state_change, history_item['created_at']).in_minutes if counted # puts "Diff count #{history_item['value_from']} -> #{history_item['value_to']} / #{last_state_change} -> #{history_item['created_at']}" - total_time_without_pending = total_time_without_pending + diff + working_time_in_min = working_time_in_min + diff # else # puts "Diff not count #{history_item['value_from']} -> #{history_item['value_to']} / #{last_state_change} -> #{history_item['created_at']}" end - total_time = total_time + diff + total_time_in_min = total_time_in_min + diff if history_item['value_to'] == 'pending reminder' last_state_is_pending = true @@ -278,45 +274,23 @@ returns # 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 + diff = biz.within(last_state_change, end_time).in_minutes + working_time_in_min = working_time_in_min + diff + total_time_in_min = total_time_in_min + 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 + diff = biz.within(start_time, end_time).in_minutes + working_time_in_min = working_time_in_min + diff + total_time_in_min = total_time_in_min + 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 - fail "ERROR: Unknown type #{type}" + #puts "#{type}:working_time_in_min:#{working_time_in_min}|free_time:#{total_time_in_min - working_time_in_min}" + if type == 'non_business_minutes' + return total_time_in_min - working_time_in_min end + working_time_in_min 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 - - def calculation_higher_time(escalation_time, check_time, done_time) - return escalation_time if done_time - return check_time if !escalation_time - return escalation_time if !check_time - return check_time if escalation_time > check_time - escalation_time - end end diff --git a/app/models/ticket/state.rb b/app/models/ticket/state.rb index 1b9939ab6..5ebe899ea 100644 --- a/app/models/ticket/state.rb +++ b/app/models/ticket/state.rb @@ -50,7 +50,7 @@ returns: check if state is ignored for escalation - state = Ticket::State.lookup( :name => 'state name' ) + state = Ticket::State.lookup(name: 'state name') result = state.ignore_escalation? @@ -61,8 +61,7 @@ returns: =end def ignore_escalation? - ignore_escalation = %w(removed closed merged) - return true if ignore_escalation.include?( name ) + return true if ignore_escalation false end end diff --git a/db/migrate/20150969000001_update_sla.rb b/db/migrate/20150969000001_update_sla.rb new file mode 100644 index 000000000..4b2e6584c --- /dev/null +++ b/db/migrate/20150969000001_update_sla.rb @@ -0,0 +1,8 @@ +class UpdateSla < ActiveRecord::Migration + def up + add_column :slas, :calendar_id, :integer, null: false + remove_column :slas, :timezone + remove_column :slas, :data + remove_column :slas, :active + end +end \ No newline at end of file diff --git a/db/migrate/20150970000001_update_ticket_state.rb b/db/migrate/20150970000001_update_ticket_state.rb new file mode 100644 index 000000000..fde7d843a --- /dev/null +++ b/db/migrate/20150970000001_update_ticket_state.rb @@ -0,0 +1,12 @@ +class UpdateTicketState < ActiveRecord::Migration + def up + add_column :ticket_states, :ignore_escalation, :boolean, null: false, default: false + + Ticket::State.create_or_update( id: 3, name: 'pending reminder', state_type_id: Ticket::StateType.find_by(name: 'pending reminder').id, ignore_escalation: true ) + Ticket::State.create_or_update( id: 4, name: 'closed', state_type_id: Ticket::StateType.find_by(name: 'closed').id, ignore_escalation: true ) + Ticket::State.create_or_update( id: 5, name: 'merged', state_type_id: Ticket::StateType.find_by(name: 'merged').id, ignore_escalation: true ) + Ticket::State.create_or_update( id: 6, name: 'removed', state_type_id: Ticket::StateType.find_by(name: 'removed').id, active: false, ignore_escalation: true ) + Ticket::State.create_or_update( id: 7, name: 'pending close', state_type_id: Ticket::StateType.find_by(name: 'pending action').id, next_state_id: 4, ignore_escalation: true ) + + end +end \ No newline at end of file diff --git a/db/seeds.rb b/db/seeds.rb index 23e61c03f..03cfdfd07 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1527,13 +1527,13 @@ Ticket::StateType.create_if_not_exists( id: 5, name: 'closed' ) Ticket::StateType.create_if_not_exists( id: 6, name: 'merged' ) Ticket::StateType.create_if_not_exists( id: 7, name: 'removed' ) -Ticket::State.create_if_not_exists( id: 1, name: 'new', state_type_id: Ticket::StateType.where(name: 'new').first.id ) -Ticket::State.create_if_not_exists( id: 2, name: 'open', state_type_id: Ticket::StateType.where(name: 'open').first.id ) -Ticket::State.create_if_not_exists( id: 3, name: 'pending reminder', state_type_id: Ticket::StateType.where(name: 'pending reminder').first.id ) -Ticket::State.create_if_not_exists( id: 4, name: 'closed', state_type_id: Ticket::StateType.where(name: 'closed').first.id ) -Ticket::State.create_if_not_exists( id: 5, name: 'merged', state_type_id: Ticket::StateType.where(name: 'merged').first.id ) -Ticket::State.create_if_not_exists( id: 6, name: 'removed', state_type_id: Ticket::StateType.where(name: 'removed').first.id, active: false ) -Ticket::State.create_if_not_exists( id: 7, name: 'pending close', state_type_id: Ticket::StateType.where(name: 'pending action').first.id, next_state_id: 4 ) +Ticket::State.create_if_not_exists( id: 1, name: 'new', state_type_id: Ticket::StateType.find_by(name: 'new').id, ) +Ticket::State.create_if_not_exists( id: 2, name: 'open', state_type_id: Ticket::StateType.find_by(name: 'open').id ) +Ticket::State.create_if_not_exists( id: 3, name: 'pending reminder', state_type_id: Ticket::StateType.find_by(name: 'pending reminder').id, ignore_escalation: true ) +Ticket::State.create_if_not_exists( id: 4, name: 'closed', state_type_id: Ticket::StateType.find_by(name: 'closed').id, ignore_escalation: true ) +Ticket::State.create_if_not_exists( id: 5, name: 'merged', state_type_id: Ticket::StateType.find_by(name: 'merged').id, ignore_escalation: true ) +Ticket::State.create_if_not_exists( id: 6, name: 'removed', state_type_id: Ticket::StateType.find_by(name: 'removed').id, active: false, ignore_escalation: true ) +Ticket::State.create_if_not_exists( id: 7, name: 'pending close', state_type_id: Ticket::StateType.find_by(name: 'pending action').id, next_state_id: 4, ignore_escalation: true ) Ticket::Priority.create_if_not_exists( id: 1, name: '1 low' ) Ticket::Priority.create_if_not_exists( id: 2, name: '2 normal' ) diff --git a/lib/time_calculation.rb b/lib/time_calculation.rb deleted file mode 100644 index e563a05e2..000000000 --- a/lib/time_calculation.rb +++ /dev/null @@ -1,361 +0,0 @@ -module TimeCalculation - -=begin - -put working hours matrix and timezone in function, returns UTC working hours matrix - - working_hours_martix = TimeCalculation.working_hours('2013-10-27 20:00:15', working_hours_matrix, 'Europe/Berlin') - - working_hours_martix = { - :Mon => [nil,nil,nil,nil,nil,nil,nil,nil,true,true,true,true,true,true,true,true,true,true,true,nil,nil,nil,nil,nil], - :Tue => [nil,nil,nil,nil,nil,nil,nil,nil,true,true,true,true,true,true,true,true,true,true,true,nil,nil,nil,nil,nil], - :Wed => [nil,nil,nil,nil,nil,nil,nil,nil,true,true,true,true,true,true,true,true,true,true,true,nil,nil,nil,nil,nil], - :Thu => [nil,nil,nil,nil,nil,nil,nil,nil,true,true,true,true,true,true,true,true,true,true,true,nil,nil,nil,nil,nil], - :Fri => [nil,nil,nil,nil,nil,nil,nil,nil,true,true,true,true,true,true,true,true,true,true,true,nil,nil,nil,nil,nil], - :Sat => [], - :Sun => [], - } - -=end - - def self.working_hours(start_time, config, timezone) - time_diff = 0 - if timezone - begin - time_diff = Time.zone.parse(start_time.to_s).in_time_zone(timezone).utc_offset - rescue => e - Rails.logger.error "Can't fine tomezone #{timezone}" - Rails.logger.error e.inspect - Rails.logger.error e.backtrace - end - end - beginning_of_workday = Time.zone.parse("1977-10-27 #{config['beginning_of_workday']}") - end_of_workday = Time.zone.parse("1977-10-27 #{config['end_of_workday']}") - 3600 - config_ok = false - working_hours = {} - [:Mon, :Tue, :Wed, :Thu, :Fri, :Sat, :Sun].each {|day| - working_hours[day] = [] - - next if !config[day.to_s] - if config[day.to_s] != true && config[day.to_s] != day.to_s - next - end - - config_ok = true - (0..23).each {|hour| - time = Time.zone.parse("1977-10-27 #{hour}:00:00") - if time >= beginning_of_workday && time <= end_of_workday - working_hours[day].push true - else - working_hours[day].push nil - end - } - } - - if !config_ok - fail 'sla config is invalid! ' + config.inspect - end - - # shift working hours / if needed - if time_diff && time_diff != 0 - - hours_to_shift = (time_diff / 3600 ).round - move_items = { - Mon: [], - Tue: [], - Wed: [], - Thu: [], - Fri: [], - Sat: [], - Sun: [], - } - (1..hours_to_shift).each { - working_hours.each {|day, value| - - next if !value - - to_move = working_hours[day].shift - if day == :Mon - move_items[:Tue].push to_move - elsif day == :Tue - move_items[:Wed].push to_move - elsif day == :Wed - move_items[:Thu].push to_move - elsif day == :Thu - move_items[:Fri].push to_move - elsif day == :Fri - move_items[:Sat].push to_move - elsif day == :Sat - move_items[:Sun].push to_move - elsif day == :Sun - move_items[:Mon].push to_move - end - } - } - move_items.each {|day, value| - value.each {|item| - working_hours[day].push item - } - } - end - working_hours - end - -=begin - - returns business hours in minutes between to dates - - business_hours_in_min = Time.Calculation.business_time_diff( - '2013-10-27 14:00:15', - '2013-10-27 18:10:15', - working_hours_martix, - 'Europe/Berlin', - ) - -=end - - def self.business_time_diff(start_time, end_time, config = nil, timezone = '') - if start_time.class == String - start_time = Time.zone.parse( start_time.to_s + 'UTC' ) - end - if end_time.class == String - end_time = Time.zone.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 = { - 1 => :Mon, - 2 => :Tue, - 3 => :Wed, - 4 => :Thu, - 5 => :Fri, - 6 => :Sat, - 0 => :Sun, - } - - count = 0 - calculation = true - first_loop = true - while calculation - week_day = start_time.wday - day = start_time.day - month = start_time.month - year = start_time.year - hour = start_time.hour - - # check if it's vacation day - if config - if config['holidays'] - if config['holidays'].include?("#{year}-#{month}-#{day}") - - # jump to next day - start_time = start_time.beginning_of_day + 86_400 - next - end - end - end - - # check if it's countable day - if working_hours[ week_day_map[week_day] ].empty? - - # jump to next day - start_time = start_time.beginning_of_day + 86_400 - next - end - - # fillup to first full hour - if first_loop - diff = end_time - start_time - - if diff > 59 * 60 - diff = start_time - start_time.beginning_of_hour - end - start_time += diff - - # check if it's countable hour - if working_hours[ week_day_map[week_day] ][ hour ] - count += diff - end - end - first_loop = false - - # loop to next hour - (hour..23).each { |next_hour| - - # check if end time is lower - if start_time >= end_time - calculation = false - break - end - - # check if end_time is within this hour - diff = end_time - start_time - if diff > 59 * 60 - diff = 3600 - end - - # keep it in current day - if next_hour == 23 - start_time += diff - 1 - else - start_time += diff - end - - # check if it's business hour and count - if working_hours[ week_day_map[week_day] ][ next_hour ] - count += diff - end - } - - # loop to next day - start_time = start_time.beginning_of_day + 86_400 - end - - diff = count / 60 - diff.round - end - -=begin - - returns destination date of start time plus X minutes - - dest_time = Time.Calculation.dest_time( - '2013-10-27 14:00:15', - 120, - working_hours_martix, - 'Europe/Berlin', - ) - -=end - - def self.dest_time(start_time, diff_in_min, config = nil, timezone = '') - if start_time.class == String - start_time = Time.zone.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) - - week_day_map = { - 1 => :Mon, - 2 => :Tue, - 3 => :Wed, - 4 => :Thu, - 5 => :Fri, - 6 => :Sat, - 0 => :Sun, - } - - count = diff_in_min * 60 - calculation = true - first_loop = true - while calculation - week_day = start_time.wday - day = start_time.day - month = start_time.month - year = start_time.year - hour = start_time.hour - #puts "start outer loop #{start_time}-#{week_day}-#{year}-#{month}-#{day}-#{hour}|c#{count}" - - # check if it's vacation day - if config - if config['holidays'] - if config['holidays'].include?("#{year}-#{month}-#{day}") - - # jump to next day - start_time = start_time.beginning_of_day + 86_400 - next - end - end - end - - # check if it's countable day - if working_hours[ week_day_map[week_day] ].empty? - - # jump to next day - start_time = start_time.beginning_of_day + 86_400 - next - end - - # fillup to first full hour - if first_loop - - # 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 - if working_hours[ week_day_map[week_day] ][ hour ] - count -= diff - end - - # start on next hour of we moved to next - if diff != 0 - hour += 1 - end - end - - first_loop = false - - # loop to next hour - (hour..23).each { |next_hour| - - diff = 3600 - - # check if count positiv - if count <= 0 - calculation = false - break - end - - # 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 - else - diff = count - end - count -= diff - end - - # keep it in current day - if next_hour == 23 - start_time += diff - 1 - else - start_time += diff - end - } - - # check if count positiv - if count <= 0 - calculation = false - break - end - - # loop to next day - start_time = start_time.beginning_of_day + 86_400 - end - - start_time - end - -end diff --git a/test/unit/calendar_test.rb b/test/unit/calendar_test.rb new file mode 100644 index 000000000..61bc78192 --- /dev/null +++ b/test/unit/calendar_test.rb @@ -0,0 +1,93 @@ +# encoding: utf-8 +require 'test_helper' + +class CalendarTest < ActiveSupport::TestCase + test 'default test' do + + calendar1 = Calendar.create_or_update( + name: 'US 1', + timezone: 'America/Los_Angeles', + business_hours: { + mon: { '09:00' => '17:00' }, + tue: { '09:00' => '17:00' }, + wed: { '09:00' => '17:00' }, + thu: { '09:00' => '17:00' }, + fri: { '09:00' => '17:00' } + }, + default: true, + ical_url: nil, + updated_by_id: 1, + created_by_id: 1, + ) + + calendar2 = Calendar.create_or_update( + name: 'US 2', + timezone: 'America/Los_Angeles', + business_hours: { + mon: { '09:00' => '17:00' }, + tue: { '09:00' => '17:00' }, + wed: { '09:00' => '17:00' }, + thu: { '09:00' => '17:00' }, + fri: { '09:00' => '17:00' } + }, + default: false, + ical_url: nil, + updated_by_id: 1, + created_by_id: 1, + ) + + calendar3 = Calendar.create_or_update( + name: 'US 3', + timezone: 'America/Los_Angeles', + business_hours: { + mon: { '09:00' => '17:00' }, + tue: { '09:00' => '17:00' }, + wed: { '09:00' => '17:00' }, + thu: { '09:00' => '17:00' }, + fri: { '09:00' => '17:00' } + }, + default: true, + ical_url: nil, + updated_by_id: 1, + created_by_id: 1, + ) + + calendar1 = Calendar.find_by(name: 'US 1') + calendar2 = Calendar.find_by(name: 'US 2') + calendar3 = Calendar.find_by(name: 'US 3') + + assert_equal(false, calendar1.default) + assert_equal(false, calendar2.default) + assert_equal(true, calendar3.default) + + calendar2.default = true + calendar2.save + + calendar1 = Calendar.find_by(name: 'US 1') + calendar2 = Calendar.find_by(name: 'US 2') + calendar3 = Calendar.find_by(name: 'US 3') + + assert_equal(false, calendar1.default) + assert_equal(true, calendar2.default) + assert_equal(false, calendar3.default) + + calendar2.default = false + calendar2.save + + calendar1 = Calendar.find_by(name: 'US 1') + calendar2 = Calendar.find_by(name: 'US 2') + calendar3 = Calendar.find_by(name: 'US 3') + + assert_equal(true, calendar1.default) + assert_equal(false, calendar2.default) + assert_equal(false, calendar3.default) + + calendar1.destroy + calendar2 = Calendar.find_by(name: 'US 2') + calendar3 = Calendar.find_by(name: 'US 3') + + assert_equal(true, calendar2.default) + assert_equal(false, calendar3.default) + end + +end diff --git a/test/unit/ticket_sla_test.rb b/test/unit/ticket_sla_test.rb index d1d2caccf..1e1b0dca1 100644 --- a/test/unit/ticket_sla_test.rb +++ b/test/unit/ticket_sla_test.rb @@ -2,6 +2,7 @@ require 'test_helper' class TicketSlaTest < ActiveSupport::TestCase + test 'ticket sla' do # cleanup @@ -24,18 +25,29 @@ class TicketSlaTest < ActiveSupport::TestCase assert( ticket, 'ticket created' ) assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify' ) - sla = Sla.create( + calendar1 = Calendar.create_or_update( + name: 'EU 1', + timezone: 'Europe/Berlin', + business_hours: { + mon: { '09:00' => '17:00' }, + tue: { '09:00' => '17:00' }, + wed: { '09:00' => '17:00' }, + thu: { '09:00' => '17:00' }, + fri: { '09:00' => '17:00' } + }, + default: true, + ical_url: nil, + updated_by_id: 1, + created_by_id: 1, + ) + + sla = Sla.create_or_update( name: 'test sla 1', condition: {}, - data: { - 'Mon' => 'Mon', 'Tue' => 'Tue', 'Wed' => 'Wed', 'Thu' => 'Thu', 'Fri' => 'Fri', 'Sat' => 'Sat', 'Sun' => 'Sun', - 'beginning_of_workday' => '8:00', - 'end_of_workday' => '18:00', - }, first_response_time: 120, update_time: 180, close_time: 240, - active: true, + calendar_id: calendar1.id, updated_by_id: 1, created_by_id: 1, ) @@ -47,18 +59,30 @@ class TicketSlaTest < ActiveSupport::TestCase delete = sla.destroy assert( delete, 'sla destroy 1' ) - sla = Sla.create( + calendar2 = Calendar.create_or_update( + name: 'EU 2', + timezone: 'Europe/Berlin', + business_hours: { + mon: { '08:00' => '18:00' }, + tue: { '08:00' => '18:00' }, + wed: { '08:00' => '18:00' }, + thu: { '08:00' => '18:00' }, + fri: { '08:00' => '18:00' }, + sat: { '08:00' => '18:00' }, + sun: { '08:00' => '18:00' }, + }, + default: true, + ical_url: nil, + updated_by_id: 1, + created_by_id: 1, + ) + sla = Sla.create_or_update( name: 'test sla 2', condition: { 'tickets.priority_id' => %w(1 2 3) }, - data: { - 'Mon' => 'Mon', 'Tue' => 'Tue', 'Wed' => 'Wed', 'Thu' => 'Thu', 'Fri' => 'Fri', 'Sat' => 'Sat', 'Sun' => 'Sun', - 'beginning_of_workday' => '8:00', - 'end_of_workday' => '18:00', - }, + calendar_id: calendar2.id, first_response_time: 60, update_time: 120, close_time: 180, - active: true, updated_by_id: 1, created_by_id: 1, ) @@ -81,7 +105,6 @@ class TicketSlaTest < ActiveSupport::TestCase ticket.update_attributes( first_response: '2013-03-21 10:00:00 UTC', ) - puts ticket.inspect assert_equal( ticket.escalation_time.gmtime.to_s, '2013-03-21 11:30:00 UTC', 'ticket.escalation_time verify 3' ) assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-03-21 10:30:00 UTC', 'ticket.first_response_escal_date verify 3' ) @@ -101,7 +124,6 @@ class TicketSlaTest < ActiveSupport::TestCase ticket.update_attributes( first_response: '2013-03-21 14:00:00 UTC', ) - puts ticket.inspect assert_equal( ticket.escalation_time.gmtime.to_s, '2013-03-21 11:30:00 UTC', 'ticket.escalation_time verify 4' ) assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-03-21 10:30:00 UTC', 'ticket.first_response_escal_date verify 4' ) @@ -269,8 +291,8 @@ class TicketSlaTest < ActiveSupport::TestCase type: Ticket::Article::Type.where(name: 'email').first, updated_by_id: 1, created_by_id: 1, - created_at: '2013-03-29 08:00:03 UTC', - updated_at: '2013-03-29 08:00:03 UTC', + created_at: '2013-03-29 07:00:03 UTC', + updated_at: '2013-03-29 07:00:03 UTC', ) ticket = Ticket.find(ticket.id) @@ -355,20 +377,31 @@ class TicketSlaTest < ActiveSupport::TestCase assert( ticket, 'ticket created' ) assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify' ) - # set sla's for timezone "Europe/Berlin" wintertime (+1), so UTC times are 8:00-17: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', - }, + # set sla's for timezone "Europe/Berlin" wintertime (+1), so UTC times are 7:00-16:00 + calendar = Calendar.create_or_update( + name: 'EU 3', timezone: 'Europe/Berlin', + business_hours: { + mon: { '08:00' => '17:00' }, + tue: { '08:00' => '17:00' }, + wed: { '08:00' => '17:00' }, + thu: { '08:00' => '17:00' }, + fri: { '08:00' => '17:00' }, + sat: { '08:00' => '17:00' }, + sun: { '08:00' => '17:00' }, + }, + default: true, + ical_url: nil, + updated_by_id: 1, + created_by_id: 1, + ) + sla = Sla.create_or_update( + name: 'test sla 3', + condition: {}, + calendar_id: calendar.id, first_response_time: 120, update_time: 180, close_time: 240, - active: true, updated_by_id: 1, created_by_id: 1, ) @@ -397,23 +430,35 @@ class TicketSlaTest < ActiveSupport::TestCase assert( ticket, 'ticket created' ) assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify' ) - # 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', - }, + # set sla's for timezone "Europe/Berlin" summertime (+2), so UTC times are 6:00-15:00 + calendar = Calendar.create_or_update( + name: 'EU 4', timezone: 'Europe/Berlin', - first_response_time: 120, - update_time: 180, - close_time: 240, - active: true, + business_hours: { + mon: { '08:00' => '17:00' }, + tue: { '08:00' => '17:00' }, + wed: { '08:00' => '17:00' }, + thu: { '08:00' => '17:00' }, + fri: { '08:00' => '17:00' }, + sat: { '08:00' => '17:00' }, + sun: { '08:00' => '17:00' }, + }, + default: true, + ical_url: nil, updated_by_id: 1, created_by_id: 1, ) + sla = Sla.create_or_update( + name: 'test sla 4', + condition: {}, + calendar_id: calendar.id, + first_response_time: 120, + update_time: 180, + close_time: 240, + updated_by_id: 1, + created_by_id: 1, + ) + ticket = Ticket.find(ticket.id) assert_equal( ticket.escalation_time.gmtime.to_s, '2013-10-21 11:30:00 UTC', 'ticket.escalation_time verify 1' ) assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-10-21 11:30:00 UTC', 'ticket.first_response_escal_date verify 1' ) @@ -432,36 +477,30 @@ class TicketSlaTest < ActiveSupport::TestCase customer_id: 2, state: Ticket::State.lookup( name: 'new' ), priority: Ticket::Priority.lookup( name: '2 normal' ), - created_at: '2013-10-21 06:30:00 UTC', - updated_at: '2013-10-21 06:30:00 UTC', + created_at: '2013-10-21 05:30:00 UTC', + updated_at: '2013-10-21 05:30:00 UTC', updated_by_id: 1, created_by_id: 1, ) assert( ticket, 'ticket created' ) assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify' ) - # set sla's for timezone "Europe/Berlin" summertime (+2), so UTC times are 7:00-16:00 - sla = Sla.create( - name: 'test sla 1', + # set sla's for timezone "Europe/Berlin" summertime (+2), so UTC times are 6:00-15:00 + sla = Sla.create_or_update( + name: 'test sla 5', 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', + calendar_id: calendar.id, 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-10-21 09:00:00 UTC', 'ticket.escalation_time verify 1' ) - assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-10-21 09:00:00 UTC', 'ticket.first_response_escal_date verify 1' ) - assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-10-21 10:00:00 UTC', 'ticket.update_time_escal_date verify 1' ) - assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-10-21 11:00:00 UTC', 'ticket.close_time_escal_date verify 1' ) + assert_equal( ticket.escalation_time.gmtime.to_s, '2013-10-21 08:00:00 UTC', 'ticket.escalation_time verify 1' ) + assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-10-21 08:00:00 UTC', 'ticket.first_response_escal_date verify 1' ) + assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-10-21 09:00:00 UTC', 'ticket.update_time_escal_date verify 1' ) + assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-10-21 10:00:00 UTC', 'ticket.close_time_escal_date verify 1' ) delete = sla.destroy assert( delete, 'sla destroy' ) @@ -545,22 +584,34 @@ class TicketSlaTest < ActiveSupport::TestCase ) # 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', - }, + calendar = Calendar.create_or_update( + name: 'EU 5', timezone: 'Europe/Berlin', - first_response_time: 120, - update_time: 180, - close_time: 250, - active: true, + business_hours: { + mon: { '09:00' => '18:00' }, + tue: { '09:00' => '18:00' }, + wed: { '09:00' => '18:00' }, + thu: { '09:00' => '18:00' }, + fri: { '09:00' => '18:00' }, + sat: { '09:00' => '18:00' }, + sun: { '09:00' => '18:00' }, + }, + default: true, + ical_url: nil, updated_by_id: 1, created_by_id: 1, ) + sla = Sla.create_or_update( + name: 'test sla 5', + condition: {}, + calendar_id: calendar.id, + first_response_time: 120, + update_time: 180, + close_time: 250, + updated_by_id: 1, + created_by_id: 1, + ) + ticket = Ticket.find(ticket.id) assert_equal( ticket.escalation_time.gmtime.to_s, '2013-06-04 13:30:00 UTC', 'ticket.escalation_time verify 1' ) assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-06-04 11:30:00 UTC', 'ticket.first_response_escal_date verify 1' ) @@ -608,25 +659,37 @@ class TicketSlaTest < ActiveSupport::TestCase close_time: '2013-06-04 12:00:00 UTC', ) - 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', + calendar = Calendar.create_or_update( + name: 'EU 5', + timezone: 'Europe/Berlin', + business_hours: { + mon: { '09:00' => '18:00' }, + tue: { '09:00' => '18:00' }, + wed: { '09:00' => '18:00' }, + thu: { '09:00' => '18:00' }, + fri: { '09:00' => '18:00' }, + sat: { '09:00' => '18:00' }, + sun: { '09:00' => '18:00' }, }, + default: true, + ical_url: nil, + updated_by_id: 1, + created_by_id: 1, + ) + sla = Sla.create_or_update( + name: 'test sla 5', + condition: {}, + calendar_id: calendar.id, 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 14:00:00 UTC', 'ticket.escalation_time verify 1' ) - assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-06-04 14:00:00 UTC', 'ticket.first_response_escal_date verify 1' ) + assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify 1' ) + assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-06-04 13:00:00 UTC', 'ticket.first_response_escal_date verify 1' ) assert_equal( ticket.first_response_in_min, nil, 'ticket.first_response_in_min verify 3' ) assert_equal( ticket.first_response_diff_in_min, nil, 'ticket.first_response_diff_in_min verify 3' ) assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-06-04 15:00:00 UTC', 'ticket.update_time_escal_date verify 1' ) @@ -702,25 +765,37 @@ class TicketSlaTest < ActiveSupport::TestCase close_time: '2013-06-04 12:00:00 UTC', ) - 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', + calendar = Calendar.create_or_update( + name: 'EU 5', + timezone: 'Europe/Berlin', + business_hours: { + mon: { '09:00' => '18:00' }, + tue: { '09:00' => '18:00' }, + wed: { '09:00' => '18:00' }, + thu: { '09:00' => '18:00' }, + fri: { '09:00' => '18:00' }, + sat: { '09:00' => '18:00' }, + sun: { '09:00' => '18:00' }, }, + default: true, + ical_url: nil, + updated_by_id: 1, + created_by_id: 1, + ) + sla = Sla.create_or_update( + name: 'test sla 5', + condition: {}, + calendar_id: calendar.id, 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 13:30:00 UTC', 'ticket.escalation_time verify 1' ) - assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-06-04 13:30:00 UTC', 'ticket.first_response_escal_date verify 1' ) + assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify 1' ) + assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-06-04 12:30:00 UTC', 'ticket.first_response_escal_date verify 1' ) assert_equal( ticket.first_response_in_min, nil, 'ticket.first_response_in_min verify 3' ) assert_equal( ticket.first_response_diff_in_min, nil, 'ticket.first_response_diff_in_min verify 3' ) assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-06-04 14:30:00 UTC', 'ticket.update_time_escal_date verify 1' ) @@ -812,25 +887,37 @@ class TicketSlaTest < ActiveSupport::TestCase close_time: '2013-06-04 12:00:00 UTC', ) - 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', + calendar = Calendar.create_or_update( + name: 'EU 5', + timezone: 'Europe/Berlin', + business_hours: { + mon: { '09:00' => '18:00' }, + tue: { '09:00' => '18:00' }, + wed: { '09:00' => '18:00' }, + thu: { '09:00' => '18:00' }, + fri: { '09:00' => '18:00' }, + sat: { '09:00' => '18:00' }, + sun: { '09:00' => '18:00' }, }, + default: true, + ical_url: nil, + updated_by_id: 1, + created_by_id: 1, + ) + sla = Sla.create_or_update( + name: 'test sla 5', + condition: {}, + calendar_id: calendar.id, 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 13:00:00 UTC', 'ticket.escalation_time verify 1' ) - assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-06-04 13:00:00 UTC', 'ticket.first_response_escal_date verify 1' ) + assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify 1' ) + assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-06-04 12:30:00 UTC', 'ticket.first_response_escal_date verify 1' ) assert_equal( ticket.first_response_in_min, nil, 'ticket.first_response_in_min verify 3' ) assert_equal( ticket.first_response_diff_in_min, nil, 'ticket.first_response_diff_in_min verify 3' ) assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-06-04 14:00:00 UTC', 'ticket.update_time_escal_date verify 1' ) @@ -845,4 +932,5 @@ class TicketSlaTest < ActiveSupport::TestCase assert( delete, 'ticket destroy' ) end + end diff --git a/test/unit/working_time_test.rb b/test/unit/working_time_test.rb deleted file mode 100644 index f44ecf7b4..000000000 --- a/test/unit/working_time_test.rb +++ /dev/null @@ -1,555 +0,0 @@ -# encoding: utf-8 -require 'test_helper' -require 'time_calculation' - -class WorkingTimeTest < ActiveSupport::TestCase - test 'working time' do - tests = [ - - # test 1 - { - start: '2012-12-17 08:00:00', - end: '2012-12-18 08:00:00', - diff: 600, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 2 - { - start: '2012-12-17 08:00:00', - end: '2012-12-17 09:00:00', - diff: 60, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 3 - { - start: '2012-12-17 08:00:00', - end: '2012-12-17 08:15:00', - diff: 15, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 4 - { - start: '2012-12-23 08:00:00', - end: '2012-12-27 10:30:42', - diff: 151, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - 'holidays' => [ - '2012-12-24', '2012-12-25', '2012-12-26' - ], - }, - }, - - # test 5 - { - start: '2013-02-28 17:00:00', - end: '2013-02-28 23:59:59', - diff: 60, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 6 - { - start: '2013-02-28 17:00:00', - end: '2013-03-08 23:59:59', - diff: 3660, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 7 - { - start: '2012-02-28 17:00:00', - end: '2013-03-08 23:59:59', - diff: 160_860, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 8 - { - start: '2013-02-28 17:01:00', - end: '2013-02-28 18:10:59', - diff: 61, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 9 - { - start: '2013-02-28 18:01:00', - end: '2013-02-28 18:10:59', - 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 10 / summertime - { - start: '2013-02-28 18:01:00', - end: '2013-02-28 18:10:59', - diff: 0, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 11 / summertime - { - start: '2013-02-28 17:01:00', - end: '2013-02-28 17:10:59', - diff: 0, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 12 / wintertime - { - start: '2013-08-29 17:01:00', - end: '2013-08-29 17:10:59', - diff: 0, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 13 / summertime - { - start: '2013-02-28 16:01:00', - end: '2013-02-28 16:10:59', - diff: 10, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 14 / wintertime - { - start: '2013-08-29 16:01:00', - end: '2013-08-29 16:10:59', - diff: 0, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - '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] ) - assert_equal( diff, test[:diff], 'diff' ) - } - end - - test 'dest time' do - tests = [ - - # test 1 - { - start: '2012-12-17 08:00:00', - dest_time: '2012-12-17 18:00:00', - diff: 600, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 2 - { - start: '2012-12-17 08:00:00', - dest_time: '2012-12-18 08:30:00', - diff: 630, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 3 - { - start: '2012-12-17 08:00:00', - dest_time: '2012-12-18 18:00:00', - diff: 1200, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 4 - { - start: '2012-12-17 08:00:00', - dest_time: '2012-12-19 08:30:00', - diff: 1230, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 5 - { - start: '2012-12-17 08:00:00', - dest_time: '2012-12-21 18:00:00', - diff: 3000, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 6 - { - start: '2012-12-17 08:00:00', - dest_time: '2012-12-24 08:05:00', - diff: 3005, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 7 - { - start: '2012-12-17 08:00:00', - dest_time: '2012-12-31 08:05:00', - diff: 6005, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 8 - { - start: '2012-12-17 08:00:00', - dest_time: '2012-12-31 13:30:00', - diff: 6330, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 9 - { - start: '2013-04-12 21:20:15', - dest_time: '2013-04-15 10:00:00', - diff: 120, - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 11 / summertime 7am-5pm - { - start: '2013-03-08 21:20:15', - dest_time: '2013-03-11 09:00:00', - diff: 120, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 12 / wintertime 6am-4pm - { - start: '2013-09-06 21:20:15', - dest_time: '2013-09-09 08:00:00', - diff: 120, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '8:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 13 / wintertime - 7am-4pm - { - start: '2013-10-21 06:30:00', - dest_time: '2013-10-21 09:00:00', - diff: 120, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '9:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 14 / wintertime - 7am-4pm - { - start: '2013-10-21 04:34:15', - dest_time: '2013-10-21 09:00:00', - diff: 120, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '9:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 15 / wintertime - 7am-4pm - { - start: '2013-10-20 22:34:15', - dest_time: '2013-10-21 09:00:00', - diff: 120, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '9:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # test 16 / wintertime - 7am-4pm - { - start: '2013-10-21 07:00:15', - dest_time: '2013-10-21 09:00:15', - diff: 120, - timezone: 'Europe/Berlin', - config: { - 'Mon' => true, - 'Tue' => true, - 'Wed' => true, - 'Thu' => true, - 'Fri' => true, - 'beginning_of_workday' => '9:00 am', - 'end_of_workday' => '6:00 pm', - }, - }, - - # 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] ) - assert_equal( dest_time.gmtime, Time.zone.parse( test[:dest_time] + ' UTC' ), "dest time - #{test[:dest_time]}" ) - } - end - -end