Class: SalesRepQueueEntry

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable
Defined in:
app/models/sales_rep_queue_entry.rb

Overview

== Schema Information

Table name: sales_rep_queue_entries
Database name: primary

id :integer not null, primary key
apply_paired_sales_rep :boolean
local_sales_rep_ids :integer default([]), is an Array
local_sales_rep_stack :integer default([]), is an Array
local_sales_rep_weights :integer default([]), is an Array
name :string(255)
position :integer
primary_sales_rep_ids :integer default([]), is an Array
primary_sales_rep_stack :integer default([]), is an Array
primary_sales_rep_weights :integer default([]), is an Array
secondary_sales_rep_ids :integer default([]), is an Array
secondary_sales_rep_stack :integer default([]), is an Array
secondary_sales_rep_weights :integer default([]), is an Array
created_at :datetime
updated_at :datetime
creator_id :integer
customer_filter_id :integer
sales_rep_queue_id :integer
updater_id :integer

Indexes

idx_sales_rep_queue_id (sales_rep_queue_id)
sales_rep_queue_entries_customer_filter_id_idx (customer_filter_id)

Foreign Keys

sales_rep_queue_entries_customer_filter_id_fk (customer_filter_id => customer_filters.id)

Constant Summary

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Instance Attribute Summary collapse

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has many collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::Auditable

#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#customer_filter_idObject (readonly)



48
# File 'app/models/sales_rep_queue_entry.rb', line 48

validates :customer_filter_id, uniqueness: { scope: :sales_rep_queue_id }

#nameObject (readonly)



46
# File 'app/models/sales_rep_queue_entry.rb', line 46

validates :name, :sales_rep_queue, presence: true

Class Method Details

.name_map(rel, role_prefix, include_token = true) ⇒ Object



81
82
83
84
85
86
87
# File 'app/models/sales_rep_queue_entry.rb', line 81

def self.name_map(rel, role_prefix, include_token=true)
  rel.map do |srw|
    res = "#{srw.employee.full_name}"
    res << " [#{role_prefix}:#{srw.weight}:#{srw.effective_remaining}(#{srw.remaining})]" if include_token
    res
  end
end

.sortedActiveRecord::Relation<SalesRepQueueEntry>

A relation of SalesRepQueueEntries that are sorted. Active Record Scope

Returns:

See Also:



53
# File 'app/models/sales_rep_queue_entry.rb', line 53

scope :sorted, -> { order("sales_rep_queue_entries.position") }

Instance Method Details

#all_rep_names(include_token = true) ⇒ Object



101
102
103
104
105
# File 'app/models/sales_rep_queue_entry.rb', line 101

def all_rep_names(include_token=true)
  (primary_sales_reps_names(include_token) +
   secondary_sales_reps_names(include_token) +
   local_sales_reps_names(include_token)).uniq
end

#applies_to_customer?(customer) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
# File 'app/models/sales_rep_queue_entry.rb', line 69

def applies_to_customer?(customer)
  sales_rep_weights.present? and (customer_filter ? customer_filter.applies_to_customer?(customer) : true)
end

#assign_combined_values(rep_type, val) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'app/models/sales_rep_queue_entry.rb', line 119

def assign_combined_values(rep_type, val)
  rep_type = rep_type.to_s
  return unless val.present?
  valids = []
  val.each do |rep_id, weight|
    rep_id = rep_id.to_i
    weight = weight.to_i
    if weight > 0
      obj = sales_rep_weights.detect{ |srw| srw.employee_id == rep_id && srw.role == rep_type }
      if obj.nil?
        obj = sales_rep_weights.build(employee_id: rep_id, role: rep_type, weight: weight)
      else
        obj.weight = weight
      end
      if obj.valid?
        valids << obj
      else
        raise "Sales rep weight for #{obj.role} id #{rep_id} with value #{weight} is invalid, #{obj.errors_to_s}"
      end
    end
  end
  # Remove unecessary records
  sales_rep_weights.select{|srw| srw.role == rep_type}.reject{|srw| valids.map(&:id).include?(srw.id) }.each(&:destroy)
end

#available_customer_filtersObject



55
56
57
58
59
60
61
62
63
# File 'app/models/sales_rep_queue_entry.rb', line 55

def available_customer_filters
  CustomerFilter.options_for_select do |rel|
    filter_in_use_ids = SalesRepQueueEntry.where(sales_rep_queue_id: sales_rep_queue_id).pluck(:customer_filter_id) - [customer_filter_id]
    filter_in_use_ids = filter_in_use_ids.compact.uniq
    newrel = rel.where(store_id: sales_rep_queue.store_id)
    newrel = newrel.where("customer_filters.id NOT IN (?)", filter_in_use_ids) if filter_in_use_ids.present?
    newrel
  end
end

#check_sales_rep_queue_rulesObject



144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'app/models/sales_rep_queue_entry.rb', line 144

def check_sales_rep_queue_rules
  unless primary_sales_rep_in_queue?
    if secondary_sales_rep_in_queue? or local_sales_rep_in_queue?
      errors.add(:base, " must have at least one non-zero entry (or Local Sales Rep Weights can have one non-zero entry if 'Apply Paired Sales Rep' is checked)")
    elsif secondary_sales_rep_in_queue?
      errors.add(:base, " must have at least one non-zero entry if Secondary Sales Rep Weights has one non-zero entry")
    end
    if local_sales_rep_in_queue? and !apply_paired_sales_rep?
      # here we can only allow a local to be defined if primary is defined, or if at least we use paired sales rep
      errors.add(:base, " must have at least one non-zero entry if Local Sales Rep Weights has one non-zero entry and 'Apply Paired Sales Rep' is not checked")
    end
  end
end

#customer_filterCustomerFilter



42
# File 'app/models/sales_rep_queue_entry.rb', line 42

belongs_to :customer_filter, inverse_of: :sales_rep_queue_entries, optional: true

#default?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'app/models/sales_rep_queue_entry.rb', line 65

def default?
  customer_filter.nil?
end

#descriptionObject



77
78
79
# File 'app/models/sales_rep_queue_entry.rb', line 77

def description
  "#{sales_rep_queue} - #{name} : #{all_rep_names(true).join(', ')}"
end

#find_sales_rep_weight_for(role, sales_rep_id) ⇒ Object



199
200
201
# File 'app/models/sales_rep_queue_entry.rb', line 199

def find_sales_rep_weight_for(role, sales_rep_id)
  sales_rep_weights.where(role: role, employee_id: sales_rep_id).pick(:weight) || 0
end

#get_assignable_weights(sales_rep_type) ⇒ Object



178
179
180
181
182
183
184
185
# File 'app/models/sales_rep_queue_entry.rb', line 178

def get_assignable_weights(sales_rep_type)
  weights = sales_rep_weights.reload.assignable.where(role: sales_rep_type).select{|w| w.effective_remaining.positive? }
  unless weights.present?
    reset_weights(sales_rep_type)
    weights = sales_rep_weights.reload.assignable.where(role: sales_rep_type).select{|w| w.effective_remaining.positive? }
  end
  weights.to_a
end

#get_sales_rep(sales_rep_type = :primary_sales_rep) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
# File 'app/models/sales_rep_queue_entry.rb', line 187

def get_sales_rep(sales_rep_type = :primary_sales_rep)
  weights = get_assignable_weights(sales_rep_type)
  if weights.present?
    weights.shuffle!
    weight = weights.shift
    weight.remaining = [weight.remaining - 1, 0].max
    weight.save
    rep = weight.employee
  end
  rep
end

#local_sales_rep_in_queue?Boolean

Returns:

  • (Boolean)


166
167
168
# File 'app/models/sales_rep_queue_entry.rb', line 166

def local_sales_rep_in_queue?
  sales_rep_weights.where(role: :local_sales_rep).exists?
end

#local_sales_rep_weights_combined=(val) ⇒ Object



115
116
117
# File 'app/models/sales_rep_queue_entry.rb', line 115

def local_sales_rep_weights_combined=(val)
  assign_combined_values :local_sales_rep, val
end

#local_sales_reps_names(include_token = true) ⇒ Object



97
98
99
# File 'app/models/sales_rep_queue_entry.rb', line 97

def local_sales_reps_names(include_token=true)
  self.class.name_map sales_rep_weights.local_sales_reps.with_employee_sorted, "L", include_token
end

#prevent_paired_rep_duplicatesObject

To prevent duplicate assignment of a rep in multiple role we must validate each rep and potential backup rep in each role
If any are possibly listed twice then we error out



205
206
207
208
209
210
211
212
213
# File 'app/models/sales_rep_queue_entry.rb', line 205

def prevent_paired_rep_duplicates
  rep_ids = sales_rep_weights.map &:employee_id
  if apply_paired_sales_rep
    rep_ids += sales_rep_weights.map{|srw| srw.employee.try(:employee_record).try(:backup_rep_id) }.compact
  end
  duplicate_employee_ids = rep_ids.group_by{|e| e}.select { |k, v| v.size > 1 }.map(&:first)
  duplicate_employee_names = Employee.where(id: duplicate_employee_ids).pluck(:full_name)
  errors.add(:base, "#{duplicate_employee_names.to_sentence} can potentially be duplicated causing an assignment error, make sure reps only appear in one role.") if duplicate_employee_names.present?
end

#primary_sales_rep_in_queue?Boolean

Returns:

  • (Boolean)


158
159
160
# File 'app/models/sales_rep_queue_entry.rb', line 158

def primary_sales_rep_in_queue?
  sales_rep_weights.where(role: :primary_sales_rep).exists?
end

#primary_sales_rep_weights_combined=(val) ⇒ Object



107
108
109
# File 'app/models/sales_rep_queue_entry.rb', line 107

def primary_sales_rep_weights_combined=(val)
  assign_combined_values :primary_sales_rep, val
end

#primary_sales_reps_names(include_token = true) ⇒ Object



89
90
91
# File 'app/models/sales_rep_queue_entry.rb', line 89

def primary_sales_reps_names(include_token=true)
  self.class.name_map sales_rep_weights.primary_sales_reps.with_employee_sorted, "P", include_token
end

#reset_weights(roles = nil) ⇒ Object



170
171
172
173
174
175
176
# File 'app/models/sales_rep_queue_entry.rb', line 170

def reset_weights(roles=nil)
  roles = [roles].flatten.compact
  roles = SalesRepQueue::SALES_REP_TYPES unless roles.present?
  logger.debug "Retrieving a fresh stack of rep for #{roles.join} on sales queue #{id}"
  sales_rep_weights.where(role: roles).update_all("remaining = weight")
  sales_rep_weights.reload #Reloads the cache
end

#sales_rep_queueSalesRepQueue

Validations:



41
# File 'app/models/sales_rep_queue_entry.rb', line 41

belongs_to :sales_rep_queue, inverse_of: :sales_rep_queue_entries, optional: true

#sales_rep_weightsActiveRecord::Relation<SalesRepWeight>

Returns:

See Also:



44
# File 'app/models/sales_rep_queue_entry.rb', line 44

has_many :sales_rep_weights, inverse_of: :sales_rep_queue_entry, autosave: true

#secondary_sales_rep_in_queue?Boolean

Returns:

  • (Boolean)


162
163
164
# File 'app/models/sales_rep_queue_entry.rb', line 162

def secondary_sales_rep_in_queue?
  sales_rep_weights.where(role: :secondary_sales_rep).exists?
end

#secondary_sales_rep_weights_combined=(val) ⇒ Object



111
112
113
# File 'app/models/sales_rep_queue_entry.rb', line 111

def secondary_sales_rep_weights_combined=(val)
  assign_combined_values :secondary_sales_rep, val
end

#secondary_sales_reps_names(include_token = true) ⇒ Object



93
94
95
# File 'app/models/sales_rep_queue_entry.rb', line 93

def secondary_sales_reps_names(include_token=true)
  self.class.name_map sales_rep_weights.secondary_sales_reps.with_employee_sorted, "S", include_token
end

#to_sObject



73
74
75
# File 'app/models/sales_rep_queue_entry.rb', line 73

def to_s
  name
end