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

Constants included from Schedulable

Schedulable::SIMPLE_FORM_OPTIONS

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 Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#customer_filter_idObject (readonly)



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

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

#nameObject (readonly)



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

validates :name, :sales_rep_queue, presence: true

Class Method Details

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



89
90
91
92
93
94
95
# File 'app/models/sales_rep_queue_entry.rb', line 89

def self.name_map(rel, role_prefix, include_token = true)
  rel.map do |srw|
    res = srw.employee.full_name.to_s
    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:



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

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

Instance Method Details

#all_rep_names(include_token = true) ⇒ Object



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

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)


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

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



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'app/models/sales_rep_queue_entry.rb', line 127

def assign_combined_values(rep_type, val)
  rep_type = rep_type.to_s
  return if val.blank?

  valids = []
  val.each do |rep_id, weight|
    rep_id = rep_id.to_i
    weight = weight.to_i
    next unless weight > 0

    obj = sales_rep_weights.find { |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
    raise "Sales rep weight for #{obj.role} id #{rep_id} with value #{weight} is invalid, #{obj.errors_to_s}" unless obj.valid?

    valids << obj
  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



63
64
65
66
67
68
69
70
71
# File 'app/models/sales_rep_queue_entry.rb', line 63

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.not(customer_filters: { id: filter_in_use_ids }) if filter_in_use_ids.present?
    newrel
  end
end

#check_sales_rep_queue_rulesObject



151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'app/models/sales_rep_queue_entry.rb', line 151

def check_sales_rep_queue_rules
  return if primary_sales_rep_in_queue?

  if secondary_sales_rep_in_queue? || 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
  return unless local_sales_rep_in_queue? && !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

#customer_filterCustomerFilter



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

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

#default?Boolean

Returns:

  • (Boolean)


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

def default?
  customer_filter.nil?
end

#descriptionObject



85
86
87
# File 'app/models/sales_rep_queue_entry.rb', line 85

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

#find_sales_rep_weight_for(role, sales_rep_id) ⇒ Object



206
207
208
# File 'app/models/sales_rep_queue_entry.rb', line 206

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



185
186
187
188
189
190
191
192
# File 'app/models/sales_rep_queue_entry.rb', line 185

def get_assignable_weights(sales_rep_type)
  weights = sales_rep_weights.reload.assignable.where(role: sales_rep_type).select { |w| w.effective_remaining.positive? }
  if weights.blank?
    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



194
195
196
197
198
199
200
201
202
203
204
# File 'app/models/sales_rep_queue_entry.rb', line 194

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)


173
174
175
# File 'app/models/sales_rep_queue_entry.rb', line 173

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

#local_sales_rep_weights_combined=(val) ⇒ Object



123
124
125
# File 'app/models/sales_rep_queue_entry.rb', line 123

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

#local_sales_reps_names(include_token = true) ⇒ Object



105
106
107
# File 'app/models/sales_rep_queue_entry.rb', line 105

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



212
213
214
215
216
217
218
# File 'app/models/sales_rep_queue_entry.rb', line 212

def prevent_paired_rep_duplicates
  rep_ids = sales_rep_weights.map &:employee_id
  rep_ids += sales_rep_weights.filter_map { |srw| srw.employee.try(:employee_record).try(:backup_rep_id) } if apply_paired_sales_rep
  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)


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

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

#primary_sales_rep_weights_combined=(val) ⇒ Object



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

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

#primary_sales_reps_names(include_token = true) ⇒ Object



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

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



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

def reset_weights(roles = nil)
  roles = [roles].flatten.compact
  roles = SalesRepQueue::SALES_REP_TYPES if roles.blank?
  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:



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

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

#sales_rep_weightsActiveRecord::Relation<SalesRepWeight>

Returns:

See Also:



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

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

#secondary_sales_rep_in_queue?Boolean

Returns:

  • (Boolean)


169
170
171
# File 'app/models/sales_rep_queue_entry.rb', line 169

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

#secondary_sales_rep_weights_combined=(val) ⇒ Object



119
120
121
# File 'app/models/sales_rep_queue_entry.rb', line 119

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

#secondary_sales_reps_names(include_token = true) ⇒ Object



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

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_partial_pathObject

Partial lives under app/views/sales_rep_queues/, not the
conventional app/views/sales_rep_queue_entries/. Override so
render sales_rep_queue_entry / render @sales_rep_queue_entries
resolves from any controller.



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

def to_partial_path
  'sales_rep_queues/sales_rep_queue_entry'
end

#to_sObject



81
82
83
# File 'app/models/sales_rep_queue_entry.rb', line 81

def to_s
  name
end