Module: Models::CustomerSalesMarketing
- Extended by:
- ActiveSupport::Concern
- Included in:
- Customer
- Defined in:
- app/concerns/models/customer_sales_marketing.rb
Overview
Campaigns, sources, visit attribution, Reviews.io, watch flags, sales reps, activities.
Class Method Summary collapse
-
.rating_options_for_select ⇒ Array<Array<String>>
Customer-rating options for select inputs:
[label, code]pairs from A (retained high potential) through F (no potential). -
.rating_options_for_select_with_rank ⇒ Array<Array<String>>
Rating options with the rank code prefixed into the label (e.g.
"[A] Retained high potential account (1 call / 30 day)"). -
.watch_states_for_select ⇒ Array<Array<String>>
Watch-state options for select inputs as
[label, key]pairs.
Instance Method Summary collapse
-
#active_sales_subscriptions ⇒ ActiveRecord::Relation<AudienceMember>
Active audience memberships tied to outside-sales campaigns for this customer (by customer id or any known email address).
-
#active_subscriptions ⇒ ActiveRecord::Relation<AudienceMember>
Active audience memberships on any campaign for this customer (by customer id or any known email address).
-
#all_related_visits ⇒ ActiveRecord::Relation<Visit>
All visits recorded for any party belonging to this customer.
-
#broadcast_customer_source_changed ⇒ void
Published after_commit when source change passes the guard.
-
#campaigns ⇒ ActiveRecord::Relation<Campaign>
Campaigns this customer is subscribed to, matched by customer id or any of the customer's email addresses.
-
#can_spiff? ⇒ Boolean
Whether any Spiff is currently valid for this customer.
-
#check_for_open_sales_activity ⇒ void
Re-syncs the customer's open-sales-activity flag via CustomerSalesActivityFlagSync.
-
#company_review_url ⇒ String
Reviews.io review-link URL for this customer's company.
-
#create_intro_activity(description = nil) ⇒ Activity
The created introduction activity.
-
#human_watch_name ⇒ String
Human-readable label for the customer's watch state.
-
#last_completed_sales_activity_date ⇒ Date?
Date of the most recently completed sales activity.
-
#last_meeting_activities(num = 3) ⇒ ActiveRecord::Relation<Activity>
Most recently completed meeting activities.
-
#last_training_activities(num = 3) ⇒ ActiveRecord::Relation<Activity>
Most recently completed training activities.
-
#local_sales_rep_name ⇒ String?
Full name of the local sales rep.
-
#most_recent_attributable_visit_source(window: 90.days) ⇒ Source?
Source of the customer's most recent Google-Ads-attributable visit.
-
#open_activities_counter ⇒ Integer
Count of the customer's open, default-visible activities.
-
#open_sales_activity? ⇒ Boolean
Whether the customer has any open sales activities.
-
#primary_sales_rep_name ⇒ String?
Full name of the primary sales rep.
-
#profile_name ⇒ String?
Name of the customer's profile.
-
#protected_leads ⇒ ActiveRecord::Relation<Customer>
Leads protected by (assigned under) this customer.
-
#recalculate_profiling_information(_logger = Rails.logger) ⇒ true
-- imperative side effect: enqueues worker.
-
#resources_for_select(options = {}) ⇒ Activity::ResourceList
Processed resource list for select inputs.
-
#sales_manager ⇒ Party?
The sales manager (party) responsible for the customer's catalog.
-
#sales_reps ⇒ Array<Employee>
All sales reps attached to the customer (primary, secondary, local), deduplicated.
-
#sales_reps_ids ⇒ Array<Integer>
Ids of all sales reps attached to the customer, deduplicated.
-
#secondary_sales_rep_name ⇒ String?
Full name of the secondary sales rep.
-
#should_broadcast_source_change? ⇒ Boolean
Predicate used by Customer's after_commit guard.
-
#signup_visit ⇒ Visit?
The visit that created this customer, or the first recorded visit.
-
#source_for_opps_and_orders(effective_date = Date.current) ⇒ Source?
Default source for new opportunities/orders: an auto-assign campaign with an open per-member window wins over the account's own source.
-
#source_unknown? ⇒ Boolean
Whether the customer's acquisition source is unset or the placeholder 'Unknown' source.
-
#watched? ⇒ Boolean
Whether the customer is flagged for watching (automatically or forced).
Class Method Details
.rating_options_for_select ⇒ Array<Array<String>>
Customer-rating options for select inputs: [label, code] pairs from A
(retained high potential) through F (no potential).
257 258 259 260 261 262 263 264 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 257 def [['Retained high potential account (1 call / 30 day)', 'A'], ['Retained (1 call / 45 day)', 'B'], ['Retained Transactional (1 call / 60 day)', 'C'], %w[Challenging D], %w[Uncommunicative E], ['No Potential', 'F']] end |
.rating_options_for_select_with_rank ⇒ Array<Array<String>>
Rating options with the rank code prefixed into the label
(e.g. "[A] Retained high potential account (1 call / 30 day)").
270 271 272 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 270 def .map { |e| ["[#{e[1]}] #{e[0]}", e[1]] } end |
.watch_states_for_select ⇒ Array<Array<String>>
Watch-state options for select inputs as [label, key] pairs.
278 279 280 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 278 def watch_states_for_select Customer.watches.map { |key, _val| [Customer::WATCH_STATES[key], key] } end |
Instance Method Details
#active_sales_subscriptions ⇒ ActiveRecord::Relation<AudienceMember>
Active audience memberships tied to outside-sales campaigns for this
customer (by customer id or any known email address).
31 32 33 34 35 36 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 31 def active_sales_subscriptions emails = all_emails sql_cond = +'audience_members.customer_id = :customer_id' sql_cond << ' OR audience_members.email_address IN (:emails)' if emails.present? AudienceMember.active.where(sql_cond, customer_id: id, emails:).joins(audience: :campaigns).merge(Campaign.active.outside_sales).order('audience_members.created_at DESC'.sql_safe) end |
#active_subscriptions ⇒ ActiveRecord::Relation<AudienceMember>
Active audience memberships on any campaign for this customer (by
customer id or any known email address).
42 43 44 45 46 47 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 42 def active_subscriptions emails = all_emails sql_cond = +'audience_members.customer_id = :customer_id' sql_cond << ' OR audience_members.email_address IN (:emails)' if emails.present? AudienceMember.active.where(sql_cond, customer_id: id, emails:).joins(audience: :campaigns).merge(Campaign.active) end |
#all_related_visits ⇒ ActiveRecord::Relation<Visit>
All visits recorded for any party belonging to this customer.
77 78 79 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 77 def Visit.where(user_id: all_my_party_ids) end |
#broadcast_customer_source_changed ⇒ void
This method returns an undefined value.
Published after_commit when source change passes the guard.
CustomerSourceChangedHandler subscribes.
100 101 102 103 104 105 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 100 def broadcast_customer_source_changed Rails.configuration.event_store.publish( Events::CustomerSourceChanged.new(data: { customer_id: id, source_id: source_id }), stream_name: "Customer-#{id}" ) end |
#campaigns ⇒ ActiveRecord::Relation<Campaign>
Campaigns this customer is subscribed to, matched by customer id or any of
the customer's email addresses.
20 21 22 23 24 25 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 20 def campaigns emails = all_emails sql_cond = +'audience_members.customer_id = :customer_id' sql_cond << ' OR audience_members.email_address IN (:emails)' if emails.present? Campaign.joins(audiences: :audience_members).merge(AudienceMember.active).where(sql_cond, customer_id: id, emails:).order('audience_members.created_at DESC'.sql_safe) end |
#can_spiff? ⇒ Boolean
Whether any Spiff is currently valid for this customer.
52 53 54 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 52 def can_spiff? Spiff.valid_for_customer(self).present? end |
#check_for_open_sales_activity ⇒ void
This method returns an undefined value.
Re-syncs the customer's open-sales-activity flag via
CustomerSalesActivityFlagSync.
148 149 150 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 148 def check_for_open_sales_activity CustomerSalesActivityFlagSync.call(self) end |
#company_review_url ⇒ String
Reviews.io review-link URL for this customer's company.
110 111 112 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 110 def company_review_url Api::ReviewsIo::DynamicLinkBuilder.for_customer(self) end |
#create_intro_activity(description = nil) ⇒ Activity
Returns the created introduction activity.
70 71 72 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 70 def create_intro_activity(description = nil) create_activity('NEWAM', description:) end |
#human_watch_name ⇒ String
Human-readable label for the customer's watch state.
124 125 126 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 124 def human_watch_name Customer::WATCH_STATES[watch] || 'Unknown' end |
#last_completed_sales_activity_date ⇒ Date?
Date of the most recently completed sales activity.
131 132 133 134 135 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 131 def last_completed_sales_activity_date .completed.sales_activities .where.not(completion_datetime: nil) .order(Activity[:completion_datetime].desc).pick(:completion_datetime)&.to_date end |
#last_meeting_activities(num = 3) ⇒ ActiveRecord::Relation<Activity>
Returns most recently completed meeting activities.
64 65 66 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 64 def last_meeting_activities(num = 3) .meeting.completed.completion_order.limit(num) end |
#last_training_activities(num = 3) ⇒ ActiveRecord::Relation<Activity>
Returns most recently completed training activities.
58 59 60 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 58 def last_training_activities(num = 3) .training.completed.completion_order.limit(num) end |
#local_sales_rep_name ⇒ String?
Returns full name of the local sales rep.
217 218 219 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 217 def local_sales_rep_name local_sales_rep&.full_name end |
#most_recent_attributable_visit_source(window: 90.days) ⇒ Source?
Source of the customer's most recent Google-Ads-attributable visit.
180 181 182 183 184 185 186 187 188 189 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 180 def most_recent_attributable_visit_source(window: 90.days) visits .where(started_at: window.ago..) .with_any_marketing_key(Visit::GOOGLE_ADS_MARKETING_KEYS) .where.not(source_id: nil) .order(started_at: :desc) .limit(1) .pick(:source_id) &.then { |sid| Source.find_by(id: sid) } end |
#open_activities_counter ⇒ Integer
Count of the customer's open, default-visible activities.
162 163 164 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 162 def open_activities_counter .open_activities.visible_by_default.size end |
#open_sales_activity? ⇒ Boolean
Whether the customer has any open sales activities.
140 141 142 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 140 def open_sales_activity? .open_activities.sales_activities.present? end |
#primary_sales_rep_name ⇒ String?
Returns full name of the primary sales rep.
207 208 209 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 207 def primary_sales_rep_name primary_sales_rep&.full_name end |
#profile_name ⇒ String?
Returns name of the customer's profile.
222 223 224 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 222 def profile_name profile&.name end |
#protected_leads ⇒ ActiveRecord::Relation<Customer>
Leads protected by (assigned under) this customer.
155 156 157 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 155 def protected_leads Customer.protected_by(id) end |
#recalculate_profiling_information(_logger = Rails.logger) ⇒ true
-- imperative side effect: enqueues worker
246 247 248 249 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 246 def recalculate_profiling_information(_logger = Rails.logger) CustomerProfilingWorker.perform_async(id) true end |
#resources_for_select(options = {}) ⇒ Activity::ResourceList
Returns processed resource list for select inputs.
239 240 241 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 239 def resources_for_select( = {}) Activity::ResourceList.new.process(self, ) end |
#sales_manager ⇒ Party?
The sales manager (party) responsible for the customer's catalog.
229 230 231 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 229 def sales_manager catalog.sales_manager&.party end |
#sales_reps ⇒ Array<Employee>
All sales reps attached to the customer (primary, secondary, local),
deduplicated.
195 196 197 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 195 def sales_reps [primary_sales_rep, secondary_sales_rep, local_sales_rep].compact.uniq end |
#sales_reps_ids ⇒ Array<Integer>
Ids of all sales reps attached to the customer, deduplicated.
202 203 204 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 202 def sales_reps_ids [primary_sales_rep_id, secondary_sales_rep_id, local_sales_rep_id].compact.uniq end |
#secondary_sales_rep_name ⇒ String?
Returns full name of the secondary sales rep.
212 213 214 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 212 def secondary_sales_rep_name secondary_sales_rep&.full_name end |
#should_broadcast_source_change? ⇒ Boolean
Predicate used by Customer's after_commit guard. Captures the historical
condition: source_id just changed AND the new source is linked to a campaign.
saved_change_to_*? is available in after_commit (unlike *_changed?).
92 93 94 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 92 def should_broadcast_source_change? saved_change_to_source_id? && source&.linked_to_campaign? end |
#signup_visit ⇒ Visit?
The visit that created this customer, or the first recorded visit.
12 13 14 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 12 def signup_visit visit || visits.first end |
#source_for_opps_and_orders(effective_date = Date.current) ⇒ Source?
Default source for new opportunities/orders: an auto-assign campaign
with an open per-member window wins over the account's own source.
171 172 173 174 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 171 def source_for_opps_and_orders(effective_date = Date.current) auto_assign_campaign = resolve_auto_assign_campaign(effective_date) auto_assign_campaign&.source || source end |
#source_unknown? ⇒ Boolean
Whether the customer's acquisition source is unset or the placeholder
'Unknown' source.
85 86 87 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 85 def source_unknown? source.nil? || (source && (source.name == 'Unknown')) end |
#watched? ⇒ Boolean
Whether the customer is flagged for watching (automatically or forced).
117 118 119 |
# File 'app/concerns/models/customer_sales_marketing.rb', line 117 def watched? watch_auto? || watch_forced? end |