Class: Customer::SalesRepChanger

Inherits:
Object
  • Object
show all
Defined in:
app/services/customer/sales_rep_changer.rb

Overview

This class is responsible for handling everything related to a customer's
sales rep change

Defined Under Namespace

Classes: InvalidCustomerState, UnrecognizedRepAttribute

Constant Summary collapse

REP_FIELDS =

Recognised rep fields.

%i[primary_sales_rep_id secondary_sales_rep_id local_sales_rep_id service_rep_id].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(customer, options = {}) ⇒ SalesRepChanger

Returns a new instance of SalesRepChanger.

Parameters:

  • customer (Customer)

    the customer whose reps are changing

  • options (Hash) (defaults to: {})

    change options

Options Hash (options):

  • skip_activity_creation (Boolean)

    do not create rep-change activities

  • logger (Logger)

    logger to use (defaults to Rails.logger)



21
22
23
24
25
26
# File 'app/services/customer/sales_rep_changer.rb', line 21

def initialize(customer, options = {})
  @customer = customer
  @options = options || {}
  @skip_activity_creation = options[:skip_activity_creation]
  @logger = @options[:logger] || Rails.logger
end

Instance Attribute Details

#fresh_primary_repObject (readonly)

Returns the value of attribute fresh_primary_rep.



11
12
13
# File 'app/services/customer/sales_rep_changer.rb', line 11

def fresh_primary_rep
  @fresh_primary_rep
end

#loggerObject

Returns the value of attribute logger.



10
11
12
# File 'app/services/customer/sales_rep_changer.rb', line 10

def logger
  @logger
end

#primary_rep_assignedObject (readonly)

Returns the value of attribute primary_rep_assigned.



11
12
13
# File 'app/services/customer/sales_rep_changer.rb', line 11

def primary_rep_assigned
  @primary_rep_assigned
end

#primary_rep_changedObject (readonly)

Returns the value of attribute primary_rep_changed.



11
12
13
# File 'app/services/customer/sales_rep_changer.rb', line 11

def primary_rep_changed
  @primary_rep_changed
end

#primary_rep_replacedObject (readonly)

Returns the value of attribute primary_rep_replaced.



11
12
13
# File 'app/services/customer/sales_rep_changer.rb', line 11

def primary_rep_replaced
  @primary_rep_replaced
end

#rep_changesObject (readonly)

Returns the value of attribute rep_changes.



11
12
13
# File 'app/services/customer/sales_rep_changer.rb', line 11

def rep_changes
  @rep_changes
end

#skip_activity_creationObject (readonly)

Returns the value of attribute skip_activity_creation.



11
12
13
# File 'app/services/customer/sales_rep_changer.rb', line 11

def skip_activity_creation
  @skip_activity_creation
end

Instance Method Details

#analyze_changesObject



50
51
52
53
54
55
56
# File 'app/services/customer/sales_rep_changer.rb', line 50

def analyze_changes
  @primary_rep_replaced = @customer.primary_sales_rep_id_was && @customer.primary_sales_rep_id_changed?
  @fresh_primary_rep = @customer.primary_sales_rep_id_was.nil? && @customer.primary_sales_rep_id
  @primary_rep_changed = @customer.primary_sales_rep_id_changed?
  @primary_rep_assigned = @customer.primary_sales_rep_id.present?
  @rep_changes = determine_rep_changes
end

#assign_lead_activitiesObject

Assign lead activities



122
123
124
125
126
127
128
129
130
131
# File 'app/services/customer/sales_rep_changer.rb', line 122

def assign_lead_activities
  return :skip_activity_creation_set if @skip_activity_creation
  return :no_primary_assigned unless @primary_rep_assigned

  if primary_rep_replaced
    @customer.create_intro_activity # New account manager activity
  else
    NewLead::LeadActivityAssigner.new.process(@customer)
  end
end

#assign_reps(rep_attributes = {}) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'app/services/customer/sales_rep_changer.rb', line 28

def assign_reps(rep_attributes = {})
  return if rep_attributes.blank?

  invalid_attributes = rep_attributes.reject { |k| REP_FIELDS.include? k.to_sym }
  raise(UnrecognizedRepAttribute, "Attribute not recognized #{invalid_attributes.keys.join(',')}, can only be #{REP_FIELDS.join(', ')}", caller) if invalid_attributes.present?

  @customer.attributes = rep_attributes
  analyze_changes
  reset_sales_watch
  begin
    return false unless @customer.save
  rescue ActiveRecord::RecordNotUnique => e
    raise unless e.message.include?('activities_parties')

    @customer.reload
    @customer.attributes = rep_attributes
    return false unless @customer.save
  end

  handle_rep_changes
end

#determine_rep_changesObject



58
59
60
61
62
63
64
# File 'app/services/customer/sales_rep_changer.rb', line 58

def determine_rep_changes
  changes = {}
  REP_FIELDS.each do |rf|
    changes[rf] = @customer.send("#{rf}_change") if @customer.send("#{rf}_changed?")
  end
  changes
end

#handle_rep_changesObject



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'app/services/customer/sales_rep_changer.rb', line 70

def handle_rep_changes
  return true unless rep_changed?

  changes_for_log = @rep_changes.transform_values do |value|
    if value.is_a?(Array)
      value.map { |v| v.respond_to?(:id) ? v.id : v }
    else
      value.respond_to?(:id) ? value.id : value
    end
  end

  logger.debug('Rep change detected', customer_id: @customer.id, changes: changes_for_log)
  reset_sales_watch
  reset_open_opportunities
  reset_activity_assignments
  assign_lead_activities
  true
end

#rep_changed?Boolean

Returns:

  • (Boolean)


66
67
68
# File 'app/services/customer/sales_rep_changer.rb', line 66

def rep_changed?
  @rep_changes.any?
end

#reset_activity_assignmentsObject

Re run all open activities through the queue assignment system



106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'app/services/customer/sales_rep_changer.rb', line 106

def reset_activity_assignments
  @customer.all_activities.open_activities.each do |activity|
    logger.info "Evaluating activity #{activity}"
    # If activity is allocated on a week end of 'after hours', go to the next business day.  5pm is our end of day cutoff
    due_datetime = activity.target_datetime
    due_datetime = WorkingHours.advance_to_closing_time(Time.current.next_business_day) if activity.overdue? || activity.target_datetime_outside_business_hours?
    # We pick the best resource for the activity, we are going to ignore in this context
    # the possible activity overload check by passing ignore_target_date
    # For instance if a rep already has his/her max of activities for that day, we ignore and we assign the 'ideal' rep
    activity.auto_assign(due_datetime, nil, ignore_target_date: true)
    logger.info " -- assigned to #{activity.assigned_resource.try(:name)}"
    activity.save
  end
end

#reset_open_opportunitiesObject

Any open opportunity will get their reps reset to the new primary, secondary and local



98
99
100
101
102
103
# File 'app/services/customer/sales_rep_changer.rb', line 98

def reset_open_opportunities
  @customer.opportunities.not_won_or_lost.each do |opportunity|
    opportunity.synchronize_reps
    opportunity.save!
  end
end

#reset_sales_watchObject

REset the sales watch if rep is nillified



90
91
92
93
94
95
# File 'app/services/customer/sales_rep_changer.rb', line 90

def reset_sales_watch
  return :reset_not_needed if @primary_rep_assigned

  logger.info 'Rep changed to nil, resetting watch'
  @customer.watch = Customer.watches[:not_watched_auto]
end