Browse Source

Improved sla calculation. Added Unit Tests.

Martin Edenhofer 12 years ago
parent
commit
315a936bd4

+ 93 - 56
app/models/ticket.rb

@@ -473,25 +473,13 @@ class Ticket < ApplicationModel
     }
   end
 
-  def escalation_calculation
+  def _escalation_calculation_get_sla
 
-    # set escalation off if ticket is already closed
-    ticket_state      = Ticket::State.lookup( :id => self.ticket_state_id )
-    ticket_state_type = Ticket::StateType.lookup( :id => ticket_state.state_type_id )
-    ignore_escalation = ['removed', 'closed', 'merged', 'pending action']
-    if ignore_escalation.include?(ticket_state_type.name)
-      self.escalation_time            = nil
-#      self.first_response_escal_date  = nil
-#      self.close_time_escal_date      = nil
-      self.save
-      return true
-    end
-
-    # get sla
     sla_selected = nil
     Sla.where( :active => true ).each {|sla|
-      if sla.condition
-        puts sla.condition.inspect
+      if !sla.condition || sla.condition.empty?
+        sla_selected = sla
+      elsif sla.condition
         hit = false
         map = [
           [ 'tickets.ticket_priority_id', 'ticket_priority_id' ],
@@ -515,6 +503,59 @@ class Ticket < ApplicationModel
       end
     }
 
+    # get and set calendar settings
+    if sla_selected
+      BusinessTime::Config.beginning_of_workday = sla_selected.data['beginning_of_workday']
+      BusinessTime::Config.end_of_workday       = sla_selected.data['end_of_workday']
+      days = []
+      ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].each {|day|
+        if sla_selected.data[day]
+          days.push day.downcase.to_sym
+        end
+      }
+      BusinessTime::Config.work_week = days
+    end
+    return sla_selected
+  end
+
+  def _escalation_calculation_dest_time(start_time, diff_min)
+    start_time = Time.parse( start_time.to_s )
+    dest_time = (diff_min / 60).round.business_hour.after( start_time )
+    return dest_time
+  end
+
+  def _escalation_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
+    return escalation_time
+  end
+
+  def _escalation_calculation_business_time_diff(start_time, end_time)
+    start_time  = Time.parse(start_time.to_s)
+    end_time    = Time.parse(end_time.to_s)
+    diff = start_time.business_time_until(end_time) / 60
+    diff.round
+  end
+
+  def escalation_calculation
+
+    # set escalation off if ticket is already closed
+    ticket_state      = Ticket::State.lookup( :id => self.ticket_state_id )
+    ticket_state_type = Ticket::StateType.lookup( :id => ticket_state.state_type_id )
+    ignore_escalation = ['removed', 'closed', 'merged', 'pending action']
+    if ignore_escalation.include?( ticket_state_type.name )
+      self.escalation_time            = nil
+#      self.first_response_escal_date  = nil
+#      self.close_time_escal_date      = nil
+      self.save
+      return true
+    end
+
+    # get sla for ticket
+    sla_selected = self._escalation_calculation_get_sla
+
     # reset escalation if no sla is set
     if !sla_selected
       self.escalation_time            = nil
@@ -524,75 +565,71 @@ class Ticket < ApplicationModel
       return true
     end
 
-    # get calendar settings
-    BusinessTime::Config.beginning_of_workday = sla_selected.data['beginning_of_workday']
-    BusinessTime::Config.end_of_workday       = sla_selected.data['end_of_workday']
-    days = []
-    ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].each {|day|
-      if sla_selected.data[day]
-        days.push day.downcase.to_sym
-      end
-    }
-    BusinessTime::Config.work_week = days
 #    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
 
     # first response
     if sla_selected.first_response_time
-      created_at = Time.parse(self.created_at.to_s)
-      self.first_response_escal_date = (sla_selected.first_response_time / 60).round.business_hour.after( created_at )
+      self.first_response_escal_date = self._escalation_calculation_dest_time( created_at, sla_selected.first_response_time )
 
       # set ticket escalation
-      if !self.first_response && (!self.escalation_time || self.escalation_time > self.first_response_escal_date)
-        self.escalation_time = self.first_response_escal_date
-      end
+      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
+#      created_at        = Time.parse(self.created_at.to_s)
+#      first_response_at = Time.parse(self.first_response.to_s)
+#      diff = created_at.business_time_until(first_response_at) / 60
+#      self.first_response_in_min = diff.round
+      self.first_response_in_min = self._escalation_calculation_business_time_diff( self.created_at, self.first_response )
 
-    if self.first_response && !self.first_response_in_min
-      created_at        = Time.parse(self.created_at.to_s)
-      first_response_at = Time.parse(self.first_response.to_s)
-      diff = created_at.business_time_until(first_response_at) / 60
-      self.first_response_in_min = diff.round
     end
-
-    # set time over sla
+    # set sla time
     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
 
-#    # update time
-#    if sla_selected.close_time
-#      created_at = Time.parse(self.created_at.to_s)
-#      self.close_time_escal_date = (sla_selected.close_time / 60).round.business_hour.after( created_at )
+    # update time
+    if sla_selected.update_time
+      last_update = self.last_contact_agent
+      if !last_update
+        last_update = self.created_at
+      end
+      self.update_time_escal_date = self._escalation_calculation_dest_time( last_update, sla_selected.update_time )
+
+      # set ticket escalation
+      self.escalation_time = self._escalation_calculation_higher_time( self.escalation_time, self.update_time_escal_date, false )
+    end
+
+#    if self.last_contact_agent && !self.update_time_in_min
+#      created_at         = Time.parse(self.created_at.to_s)
+#      last_contact_agent = Time.parse(self.last_contact_agent.to_s)
+#      diff = created_at.business_time_until(closed_at) / 60
+#      self.close_time_in_min = diff.round
 #    end
 
     # close time
     if sla_selected.close_time
-      created_at = Time.parse(self.created_at.to_s)
-      self.close_time_escal_date = (sla_selected.close_time / 60).round.business_hour.after( created_at )
+      self.close_time_escal_date = self._escalation_calculation_dest_time( self.created_at, sla_selected.close_time )
 
       # set ticket escalation
-      if !self.close_time && (!self.escalation_time || self.escalation_time > self.close_time_escal_date)
-        self.escalation_time = self.close_time_escal_date
-      end
+      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
+#      created_at  = Time.parse(self.created_at.to_s)
+#      closed_at   = Time.parse(self.close_time.to_s)
+#      diff = created_at.business_time_until(closed_at) / 60
+#      self.close_time_in_min = diff.round
+      self.close_time_in_min = self._escalation_calculation_business_time_diff( self.created_at, self.close_time )
 
-    if self.close_time && !self.close_time_in_min
-      created_at  = Time.parse(self.created_at.to_s)
-      closed_at   = Time.parse(self.close_time.to_s)
-      diff = created_at.business_time_until(closed_at) / 60
-      self.close_time_in_min = diff.round
     end
-
-    # set time over sla
+    # set sla time
     if sla_selected.close_time && self.close_time_in_min
       self.close_time_diff_in_min = sla_selected.close_time - self.close_time_in_min
     end
-
     self.save
   end
 

+ 13 - 0
db/migrate/20130321124312_ticket_escalation_update_time.rb

@@ -0,0 +1,13 @@
+class TicketEscalationUpdateTime < ActiveRecord::Migration
+  def up
+    add_column :tickets, :update_time_escal_date,     :timestamp, :null => true
+    add_column :tickets, :updtate_time_sla_time,      :timestamp, :null => true
+    add_column :tickets, :update_time_in_min,         :integer,   :null => true
+    add_column :tickets, :update_time_diff_in_min,    :integer,   :null => true
+    add_index :tickets, [:update_time_in_min]
+    add_index :tickets, [:update_time_diff_in_min]
+  end
+
+  def down
+  end
+end

+ 127 - 0
test/unit/ticket_test.rb

@@ -0,0 +1,127 @@
+# encoding: utf-8
+require 'test_helper'
+ 
+class TicketTest < ActiveSupport::TestCase
+  test 'ticket create' do
+    ticket = Ticket.create(
+      :title           => 'some title äöüß',
+      :group           => Group.lookup( :name => 'Users'),
+      :customer_id     => 2,
+      :ticket_state    => Ticket::State.lookup( :name => 'new' ),
+      :ticket_priority => Ticket::Priority.lookup( :name => '2 normal' ),
+      :updated_by_id   => 1,
+      :created_by_id   => 1,
+    )
+    assert( ticket, "ticket created" )
+
+    assert_equal( ticket.title, 'some title äöüß', 'ticket.title verify' )
+    assert_equal( ticket.group.name, 'Users', 'ticket.group verify' )
+    assert_equal( ticket.ticket_state.name, 'new', 'ticket.state verify' )
+
+    delete = ticket.destroy
+    assert( delete, "ticket destroy" )
+  end
+
+  test 'ticket sla' do
+    ticket = Ticket.create(
+      :title           => 'some title äöüß',
+      :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-03-21 09:30:00 UTC',
+      :updated_at      => '2013-03-21 09: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' )
+
+    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" => "8:00",
+        "end_of_workday"       => "18:00",
+      },
+      :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-03-21 11:30:00 UTC', 'ticket.escalation_time verify 1' )
+    assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-03-21 11:30:00 UTC', 'ticket.first_response_escal_date verify 1' )
+    assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-03-21 12:30:00 UTC', 'ticket.update_time_escal_date verify 1' )
+    assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-03-21 13:30:00 UTC', 'ticket.close_time_escal_date verify 1' )
+    delete = sla.destroy
+    assert( delete, "sla destroy 1" )
+
+    sla = Sla.create(
+      :name => 'test sla 2',
+      :condition => { "tickets.ticket_priority_id" =>["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",
+      },
+      :first_response_time => 60,
+      :update_time   => 120,
+      :close_time    => 180,
+      :active        => true,
+      :updated_by_id => 1,
+      :created_by_id => 1,
+    )
+    ticket = Ticket.find(ticket.id)
+    assert_equal( ticket.escalation_time.gmtime.to_s, '2013-03-21 10:30:00 UTC', 'ticket.escalation_time verify 2' )
+    assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-03-21 10:30:00 UTC', 'ticket.first_response_escal_date verify 2' )
+    assert_equal( ticket.first_response, nil, 'ticket.first_response verify 2' )
+    assert_equal( ticket.first_response_in_min, nil, 'ticket.first_response_in_min verify 2' )
+    assert_equal( ticket.first_response_diff_in_min, nil, 'ticket.first_response_diff_in_min verify 2' )
+    assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-03-21 11:30:00 UTC', 'ticket.update_time_escal_date verify 2' )
+    assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-03-21 12:30:00 UTC', 'ticket.close_time_escal_date verify 2' )
+
+    ticket.update_attributes(
+#      :first_response_escal_date => '2013-03-26 09:30:00 UTC',
+      :first_response            => '2013-03-21 10:00:00 UTC',
+    )
+    ticket.escalation_calculation
+    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' )
+    assert_equal( ticket.first_response.gmtime.to_s, '2013-03-21 10:00:00 UTC', 'ticket.first_response verify 3' )
+    assert_equal( ticket.first_response_in_min, 30, 'ticket.first_response_in_min verify 3' )
+    assert_equal( ticket.first_response_diff_in_min, 30, 'ticket.first_response_diff_in_min verify 3' )
+    assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-03-21 11:30:00 UTC', 'ticket.update_time_escal_date verify 3' )
+    assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-03-21 12:30:00 UTC', 'ticket.close_time_escal_date verify 3' )
+ 
+     ticket.update_attributes(
+#      :first_response_escal_date => '2013-03-26 09:30:00 UTC',
+      :first_response            => '2013-03-21 14:00:00 UTC',
+    )
+    ticket.escalation_calculation
+    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' )
+    assert_equal( ticket.first_response.gmtime.to_s, '2013-03-21 14:00:00 UTC', 'ticket.first_response verify 4' )
+    assert_equal( ticket.first_response_in_min, 270, 'ticket.first_response_in_min verify 4' )
+    assert_equal( ticket.first_response_diff_in_min, -210, 'ticket.first_response_diff_in_min verify 4' )
+    assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-03-21 11:30:00 UTC', 'ticket.update_time_escal_date verify 4' )
+    assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-03-21 12:30:00 UTC', 'ticket.close_time_escal_date verify 4' )
+ 
+
+
+    delete = sla.destroy
+    assert( delete, "sla destroy 2" )
+
+    delete = ticket.destroy
+    assert( delete, "ticket destroy" )
+    delete = sla.destroy
+    assert( delete, "sla destroy" )
+  end
+end