Martin Edenhofer 9 лет назад
Родитель
Сommit
55059b987f

+ 19 - 13
app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee

@@ -15,8 +15,8 @@ class App.UiElement.ticket_selector
         model: 'Organization'
 
     operators_type =
-      '^datetime$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)']
-      '^timestamp$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)']
+      '^datetime$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'within next (relative)', 'within last (relative)', 'after (relative)']
+      '^timestamp$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'within next (relative)', 'within last (relative)', 'after (relative)']
       'boolean$': ['is', 'is not']
       '^input$': ['contains', 'contains not']
       '^textarea$': ['contains', 'contains not']
@@ -84,14 +84,6 @@ class App.UiElement.ticket_selector
       @buildValue(item, elementRow, groupAndAttribute, elements, undefined, undefined, attribute)
     )
 
-    # change operator
-    item.find('.js-operator select').bind('change', (e) =>
-      groupAndAttribute = $(e.target).find('.js-attributeSelector option:selected').attr('value')
-      operator = $(e.target).find('option:selected').attr('value')
-      elementRow = $(e.target).closest('.js-filterElement')
-      @buildValue(item, elementRow, groupAndAttribute, elements, undefined, operator, attribute)
-    )
-
     # build inital params
     console.log('initial', params[attribute.name])
     if !_.isEmpty(params[attribute.name])
@@ -174,11 +166,11 @@ class App.UiElement.ticket_selector
     )
 
   @buildValue: (elementFull, elementRow, groupAndAttribute, elements, value, operator, attribute) ->
+    console.log('buildValue', elementFull, elementRow, groupAndAttribute, elements, value, operator, attribute)
 
     # do nothing if item already exists
+    operator = elementRow.find('.js-operator option:selected').attr('value')
     name = "#{attribute.name}::#{groupAndAttribute}::value"
-    return if elementRow.find("[name=\"#{name}\"]").get(0)
-    return if elementRow.find("[data-name=\"#{name}\"]").get(0)
 
     # build new item
     attributeConfig = elements[groupAndAttribute]
@@ -192,7 +184,8 @@ class App.UiElement.ticket_selector
     item = ''
     if config && App.UiElement[config.tag]
       config['name'] = name
-      config['value'] = value
+      if attribute.value[groupAndAttribute]
+        config['value'] = _.clone(attribute.value[groupAndAttribute]['value'])
       if 'multiple' of config
         config.multiple = true
         config.nulloption = false
@@ -203,6 +196,12 @@ class App.UiElement.ticket_selector
         item = App.UiElement[tagSearch].render(config, {})
       else
         item = App.UiElement[config.tag].render(config, {})
+
+    if operator is 'before (relative)' || operator is 'within next (relative)' || operator is 'within last (relative)' || operator is 'after (relative)'
+      config['name'] = "#{attribute.name}::#{groupAndAttribute}"
+      config['value'] = _.clone(attribute.value[groupAndAttribute])
+      item = App.UiElement['time_range'].render(config, {})
+
     elementRow.find('.js-value').html(item)
 
   @buildAttributeSelector: (groups, elements) ->
@@ -265,6 +264,13 @@ class App.UiElement.ticket_selector
     operator = @buildOperator(elementFull, elementRow, groupAndAttribute, elements, current_operator, attribute)
     elementRow.find('.js-operator select').replaceWith(operator)
 
+    # render not value option
+    elementRow.find('.js-operator select').bind('change', (e) =>
+      groupAndAttribute = elementRow.find('.js-attributeSelector option:selected').attr('value')
+      operator = elementRow.find('.js-operator option:selected').attr('value')
+      @buildValue(elementFull, elementRow, groupAndAttribute, elements, undefined, operator, attribute)
+    )
+
   @humanText: (condition) ->
     none = App.i18n.translateContent('No filter.')
     return [none] if _.isEmpty(condition)

+ 17 - 0
app/assets/javascripts/app/controllers/_ui_element/time_range.coffee

@@ -0,0 +1,17 @@
+# coffeelint: disable=camel_case_classes
+class App.UiElement.time_range
+  @render: (attribute) ->
+    ranges =
+      minute: 'minute(s)'
+      hour: 'hour(s)'
+      day: 'day(s)'
+      month: 'month(s)'
+      year: 'year(s)'
+    for key, value of ranges
+      ranges[key] = App.i18n.translateInline(value)
+
+    values = {}
+    for count in [0..31]
+      values[count.toString()] = count.toString()
+
+    $( App.view('generic/time_range')( attribute: attribute, ranges: ranges, values: values ) )

+ 1 - 1
app/assets/javascripts/app/views/generic/ticket_selector.jst.eco

@@ -12,7 +12,7 @@
           <%- @Icon('arrow-down', 'dropdown-arrow') %>
         </div>
       </div>
-      <div class="controls js-value"></div>
+      <div class="controls js-value horizontal"></div>
     </div>
     <div class="filter-controls">
       <div class="filter-control filter-control-remove js-remove" title="<%- @T('Remove') %>">

+ 16 - 0
app/assets/javascripts/app/views/generic/time_range.jst.eco

@@ -0,0 +1,16 @@
+<div class="controls u-positionOrigin">
+  <select class="form-control js-value" name="<%= @attribute.name %>::value">
+    <% for key, value of @values: %>
+      <option value="<%= key %>" <% if @attribute.value && @attribute.value.value is key: %>selected<% end %>><%- value %></option>
+    <% end %>
+  </select>
+  <%- @Icon('arrow-down', 'dropdown-arrow') %>
+</div>
+<div class="controls u-positionOrigin">
+  <select class="form-control js-range" name="<%= @attribute.name %>::range">
+    <% for key, value of @ranges: %>
+      <option value="<%= key %>" <% if @attribute.value && @attribute.value.range is key: %>selected<% end %>><%- value %></option>
+    <% end %>
+  </select>
+  <%- @Icon('arrow-down', 'dropdown-arrow') %>
+</div>

+ 106 - 13
app/models/ticket.rb

@@ -118,9 +118,9 @@ returns
       access_condition = [ 'group_id IN (?)', group_ids ]
     else
       if !user.organization || ( !user.organization.shared || user.organization.shared == false )
-        access_condition = [ 'customer_id = ?', user.id ]
+        access_condition = [ 'tickets.customer_id = ?', user.id ]
       else
-        access_condition = [ '( customer_id = ? OR organization_id = ? )', user.id, user.organization.id ]
+        access_condition = [ '( tickets.customer_id = ? OR tickets.organization_id = ? )', user.id, user.organization.id ]
       end
     end
     access_condition
@@ -289,16 +289,24 @@ returns
 
 get count of tickets and tickets which match on selector
 
-  ticket_count, tickets = Ticket.selectors(params[:condition], 6)
+  ticket_count, tickets = Ticket.selectors(params[:condition], limit, current_user)
 
 =end
 
-  def self.selectors(selectors, limit = 10)
-    return if !selectors
+  def self.selectors(selectors, limit = 10, current_user = nil)
+    fail 'no selectors given' if !selectors
     query, bind_params, tables = selector2sql(selectors)
     return [] if !query
-    ticket_count = Ticket.where(query, *bind_params).joins(tables).count
-    tickets = Ticket.where(query, *bind_params).joins(tables).limit(limit)
+
+    if !current_user
+      ticket_count = Ticket.where(query, *bind_params).joins(tables).count
+      tickets = Ticket.where(query, *bind_params).joins(tables).limit(limit)
+      return [ticket_count, tickets]
+    end
+
+    access_condition = Ticket.access_condition(current_user)
+    ticket_count = Ticket.where(access_condition).where(query, *bind_params).joins(tables).count
+    tickets = Ticket.where(access_condition).where(query, *bind_params).joins(tables).limit(limit)
     [ticket_count, tickets]
   end
 
@@ -314,7 +322,16 @@ condition example
     'ticket.state_id' => {
       operator: 'is',
       value: [1,2,5]
-    }
+    },
+    'ticket.created_at' => {
+      operator: 'after (absolute)', # after,before
+      value: '2015-10-17T06:00:00.000Z',
+    },
+    'ticket.created_at' => {
+      operator: 'within next (relative)', # before,within,in,after
+      range: 'day', # minute|hour|day|month|year
+      value: '25',
+    },
   }
 
 =end
@@ -324,13 +341,27 @@ condition example
     query = ''
     bind_params = []
 
-    tables = []
+    tables = ''
     selectors.each {|attribute, selector|
       selector = attribute.split(/\./)
       next if !selector[1]
       next if selector[0] == 'ticket'
       next if tables.include?(selector[0])
-      tables.push selector[0].to_sym
+      if query != ''
+        query += ' AND '
+      end
+      if selector[0] == 'customer'
+        tables += ', users customers'
+        query += 'tickets.customer_id = customers.id'
+      elsif selector[0] == 'organization'
+        tables += ', organizations'
+        query += 'tickets.organization_id = organizations.id'
+      elsif selector[0] == 'owner'
+        tables += ', users owners'
+        query += 'tickets.owner_id = owners.id'
+      else
+        fail "invalid selector #{attribute.inspect}->#{selector.inspect}"
+      end
     }
 
     selectors.each {|attribute, selector_raw|
@@ -341,7 +372,7 @@ condition example
       fail "Invalid selector #{selector_raw.inspect}" if !selector_raw.respond_to?(:key?)
       selector = selector_raw.stringify_keys
       fail "Invalid selector, operator missing #{selector.inspect}" if !selector['operator']
-      return nil if !selector['value']
+      return nil if selector['value'].nil?
       return nil if selector['value'].respond_to?(:empty?) && selector['value'].empty?
       attributes = attribute.split(/\./)
       attribute = "#{attributes[0]}s.#{attributes[1]}"
@@ -365,12 +396,74 @@ condition example
       elsif selector['operator'] == 'after (absolute)'
         query += "#{attribute} >= ?"
         bind_params.push selector['value']
+      elsif selector['operator'] == 'within last (relative)'
+        query += "#{attribute} >= ?"
+        time = nil
+        if selector['range'] == 'minute'
+          time = Time.zone.now - selector['value'].to_i.minutes
+        elsif selector['range'] == 'hour'
+          time = Time.zone.now - selector['value'].to_i.hours
+        elsif selector['range'] == 'day'
+          time = Time.zone.now - selector['value'].to_i.days
+        elsif selector['range'] == 'month'
+          time = Time.zone.now - selector['value'].to_i.months
+        elsif selector['range'] == 'year'
+          time = Time.zone.now - selector['value'].to_i.years
+        else
+          fail "Unknown selector attributes '#{selector.inspect}'"
+        end
+        bind_params.push time
+      elsif selector['operator'] == 'within next (relative)'
+        query += "#{attribute} >= ?"
+        time = nil
+        if selector['range'] == 'minute'
+          time = Time.zone.now + selector['value'].to_i.minutes
+        elsif selector['range'] == 'hour'
+          time = Time.zone.now + selector['value'].to_i.hours
+        elsif selector['range'] == 'day'
+          time = Time.zone.now + selector['value'].to_i.days
+        elsif selector['range'] == 'month'
+          time = Time.zone.now + selector['value'].to_i.months
+        elsif selector['range'] == 'year'
+          time = Time.zone.now + selector['value'].to_i.years
+        else
+          fail "Unknown selector attributes '#{selector.inspect}'"
+        end
+        bind_params.push time
       elsif selector['operator'] == 'before (relative)'
         query += "#{attribute} <= ?"
-        bind_params.push Time.zone.now - selector['value'].to_i.minutes
+        time = nil
+        if selector['range'] == 'minute'
+          time = Time.zone.now - selector['value'].to_i.minutes
+        elsif selector['range'] == 'hour'
+          time = Time.zone.now - selector['value'].to_i.hours
+        elsif selector['range'] == 'day'
+          time = Time.zone.now - selector['value'].to_i.days
+        elsif selector['range'] == 'month'
+          time = Time.zone.now - selector['value'].to_i.months
+        elsif selector['range'] == 'year'
+          time = Time.zone.now - selector['value'].to_i.years
+        else
+          fail "Unknown selector attributes '#{selector.inspect}'"
+        end
+        bind_params.push time
       elsif selector['operator'] == 'after (relative)'
         query += "#{attribute} >= ?"
-        bind_params.push Time.zone.now + selector['value'].to_i.minutes
+        time = nil
+        if selector['range'] == 'minute'
+          time = Time.zone.now + selector['value'].to_i.minutes
+        elsif selector['range'] == 'hour'
+          time = Time.zone.now + selector['value'].to_i.hours
+        elsif selector['range'] == 'day'
+          time = Time.zone.now + selector['value'].to_i.days
+        elsif selector['range'] == 'month'
+          time = Time.zone.now + selector['value'].to_i.months
+        elsif selector['range'] == 'year'
+          time = Time.zone.now + selector['value'].to_i.years
+        else
+          fail "Unknown selector attributes '#{selector.inspect}'"
+        end
+        bind_params.push time
       else
         fail "Invalid operator '#{selector['operator']}' for '#{selector['value'].inspect}'"
       end

+ 626 - 0
test/unit/ticket_selector_test.rb

@@ -0,0 +1,626 @@
+# encoding: utf-8
+require 'test_helper'
+
+class TicketSelectorTest < ActiveSupport::TestCase
+
+  # create base
+  group = Group.create_or_update(
+    name: 'SelectorTest',
+    updated_at: '2015-02-05 16:37:00',
+    updated_by_id: 1,
+    created_by_id: 1,
+  )
+  roles  = Role.where( name: 'Agent' )
+  agent1 = User.create_or_update(
+    login: 'ticket-selector-agent1@example.com',
+    firstname: 'Notification',
+    lastname: 'Agent1',
+    email: 'ticket-selector-agent1@example.com',
+    password: 'agentpw',
+    active: true,
+    roles: roles,
+    groups: [group],
+    updated_at: '2015-02-05 16:37:00',
+    updated_by_id: 1,
+    created_by_id: 1,
+  )
+  agent2 = User.create_or_update(
+    login: 'ticket-selector-agent2@example.com',
+    firstname: 'Notification',
+    lastname: 'Agent2',
+    email: 'ticket-selector-agent2@example.com',
+    password: 'agentpw',
+    active: true,
+    roles: roles,
+    #groups: groups,
+    updated_at: '2015-02-05 16:38:00',
+    updated_by_id: 1,
+    created_by_id: 1,
+  )
+  roles  = Role.where( name: 'Customer' )
+  organization1 = Organization.create_if_not_exists(
+    name: 'Selector Org',
+    updated_at: '2015-02-05 16:37:00',
+    updated_by_id: 1,
+    created_by_id: 1,
+  )
+  customer1 = User.create_or_update(
+    login: 'ticket-selector-customer1@example.com',
+    firstname: 'Notification',
+    lastname: 'Customer1',
+    email: 'ticket-selector-customer1@example.com',
+    password: 'customerpw',
+    active: true,
+    organization_id: organization1.id,
+    roles: roles,
+    updated_at: '2015-02-05 16:37:00',
+    updated_by_id: 1,
+    created_by_id: 1,
+  )
+  customer2 = User.create_or_update(
+    login: 'ticket-selector-customer2@example.com',
+    firstname: 'Notification',
+    lastname: 'Customer2',
+    email: 'ticket-selector-customer2@example.com',
+    password: 'customerpw',
+    active: true,
+    organization_id: nil,
+    roles: roles,
+    updated_at: '2015-02-05 16:37:00',
+    updated_by_id: 1,
+    created_by_id: 1,
+  )
+
+  Ticket.where(group_id: group.id).destroy_all
+
+  test 'ticket create' do
+
+    ticket1 = Ticket.create(
+      title: 'some title1',
+      group: group,
+      customer_id: customer1.id,
+      owner_id: agent1.id,
+      state: Ticket::State.lookup( name: 'new' ),
+      priority: Ticket::Priority.lookup( name: '2 normal' ),
+      created_at: '2015-02-05 16:37:00',
+      #updated_at: '2015-02-05 17:37:00',
+      updated_by_id: 1,
+      created_by_id: 1,
+    )
+    assert( ticket1, 'ticket created' )
+    assert_equal( ticket1.customer.id, customer1.id )
+    assert_equal( ticket1.organization.id, organization1.id )
+    sleep 1
+
+    ticket2 = Ticket.create(
+      title: 'some title2',
+      group: group,
+      customer_id: customer2.id,
+      state: Ticket::State.lookup( name: 'new' ),
+      priority: Ticket::Priority.lookup( name: '2 normal' ),
+      created_at: '2015-02-05 16:37:00',
+      #updated_at: '2015-02-05 17:37:00',
+      updated_by_id: 1,
+      created_by_id: 1,
+    )
+    assert( ticket2, 'ticket created' )
+    assert_equal( ticket2.customer.id, customer2.id )
+    assert_equal( ticket2.organization_id, nil )
+    sleep 1
+
+    # search not matching
+    condition = {
+      'ticket.state_id' => {
+        operator: 'is',
+        value: [99],
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 0 )
+
+    # search matching
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.state_id' => {
+        operator: 'is',
+        value: [Ticket::State.lookup( name: 'new' ).id],
+      },
+    }
+
+    ticket_count, tickets = Ticket.selectors(condition, 10)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 1 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.state_id' => {
+        operator: 'is not',
+        value: [Ticket::State.lookup( name: 'open' ).id],
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 1 )
+
+    # search - created_at
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.created_at' => {
+        operator: 'after (absolute)', # before (absolute)
+        value: '2015-02-05T16:00:00.000Z',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 1 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.created_at' => {
+        operator: 'after (absolute)', # before (absolute)
+        value: '2015-02-05T18:00:00.000Z',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.created_at' => {
+        operator: 'before (absolute)',
+        value: '2015-02-05T18:00:00.000Z',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 1 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.created_at' => {
+        operator: 'before (absolute)',
+        value: '2015-02-05T16:00:00.000Z',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.created_at' => {
+        operator: 'before (relative)',
+        range: 'day', # minute|hour|day|month|
+        value: '10',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 1 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.created_at' => {
+        operator: 'within next (relative)',
+        range: 'year', # minute|hour|day|month|
+        value: '10',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.created_at' => {
+        operator: 'within last (relative)',
+        range: 'year', # minute|hour|day|month|
+        value: '10',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 1 )
+
+    # search - updated_at
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.updated_at' => {
+        operator: 'before (absolute)',
+        value: (Time.zone.now + 1.day).iso8601,
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 1 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.updated_at' => {
+        operator: 'before (absolute)',
+        value: (Time.zone.now - 1.day).iso8601,
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.updated_at' => {
+        operator: 'after (absolute)',
+        value: (Time.zone.now + 1.day).iso8601,
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.updated_at' => {
+        operator: 'after (absolute)',
+        value: (Time.zone.now - 1.day).iso8601,
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 1 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.updated_at' => {
+        operator: 'before (relative)',
+        range: 'day', # minute|hour|day|month|
+        value: '10',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.updated_at' => {
+        operator: 'within next (relative)',
+        range: 'year', # minute|hour|day|month|
+        value: '10',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'ticket.updated_at' => {
+        operator: 'within last (relative)',
+        range: 'year', # minute|hour|day|month|
+        value: '10',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 1 )
+
+    # invalid conditions
+    assert_raise RuntimeError do
+      ticket_count, tickets = Ticket.selectors(nil, 10)
+    end
+
+    # search with customers
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'customer.email' => {
+        operator: 'contains',
+        value: 'ticket-selector-customer1',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'customer.email' => {
+        operator: 'contains not',
+        value: 'ticket-selector-customer1-not_existing',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 2 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 1 )
+
+    # search with organizations
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'organization.name' => {
+        operator: 'contains',
+        value: 'selector',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+    # search with organizations
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'organization.name' => {
+        operator: 'contains',
+        value: 'selector',
+      },
+      'customer.email' => {
+        operator: 'contains',
+        value: 'ticket-selector-customer1',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 1 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+    condition = {
+      'ticket.group_id' => {
+        operator: 'is',
+        value: group.id,
+      },
+      'organization.name' => {
+        operator: 'contains',
+        value: 'selector',
+      },
+      'customer.email' => {
+        operator: 'contains not',
+        value: 'ticket-selector-customer1',
+      },
+    }
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, agent2)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
+    assert_equal( ticket_count, 0 )
+
+    ticket_count, tickets = Ticket.selectors(condition, 10, customer2)
+    assert_equal( ticket_count, 0 )
+
+  end
+
+end