Class: CustomerSearch
Overview
== Schema Information
Table name: searches
Database name: primary
id :integer not null, primary key
name :string(255)
persist :boolean default(FALSE)
pinned :boolean default(FALSE), not null
query_params :jsonb
result_set_size :integer default(0)
selected_columns :string is an Array
set_limit :integer default(25)
sort_column :string(255)
sort_columns :string default([]), not null, is an Array
sort_direction :string(255) default("ASC")
type :string(255) not null
created_at :datetime
updated_at :datetime
employee_id :integer
Indexes
employee_id_pinned (employee_id,pinned)
employee_id_type (employee_id,type)
Constant Summary
collapse
- CUSTOM_CRITERIA_KEYS =
Keys consumed by apply_custom_criteria — excluded from the Ransack strict probe
so we don't get false-positive InvalidSearchError reports in AppSignal.
%w[
has_ordered_item_id has_ordered_product_category_id has_ordered_product_line_id
has_ordered_days has_ordered_days_time_range_gteq has_ordered_days_time_range_lteq
minimum_number_of_orders has_ordered_type
campaign_id_in campaign_id_not_in has_campaign_activity_status
custom_drop_reason_codes custom_drop_rep_ids
profile_in profile_not_in is_certified_installer
].freeze
Constants inherited
from Search
Search::DISTANCE_SEARCH_KEYS
Constants included
from Schedulable
Schedulable::SIMPLE_FORM_OPTIONS
Instance Attribute Summary
Attributes inherited from Search
#composite_columns, #pagy_count, #pagy_limit, #pagy_page, #sql_select_columns
Class Method Summary
collapse
Instance Method Summary
collapse
-
#address_id_fields ⇒ Object
-
#apply_custom_criteria(results) ⇒ Object
-
#drop_action_reps_for_select ⇒ Object
-
#mass_assign_reps(params, cur_user = nil) ⇒ Object
-
#mass_assign_source(params, cur_user = nil) ⇒ Object
-
#mass_assign_to_campaign(params, cur_user = nil) ⇒ Object
-
#mass_assign_to_subscriber_list(params, cur_user = nil) ⇒ Object
-
#mass_auto_assign_sales_rep(params, cur_user = nil) ⇒ Object
-
#mass_create_subscriber_list(params, cur_user = nil) ⇒ Object
-
#mass_delete_on_lead_qualify_and_guest_state(_params, cur_user = nil) ⇒ Object
-
#mass_drop_sales_rep(params, cur_user) ⇒ Object
-
#mass_locator_blacklist(params, cur_user) ⇒ Object
-
#mass_locator_unblacklist(params, cur_user) ⇒ Object
-
#mass_locator_unwhitelist(_params, cur_user) ⇒ Object
-
#mass_locator_whitelist(params, cur_user) ⇒ Object
-
#mass_online_account_invite(_params, cur_user = nil) ⇒ Object
-
#mass_schedule_activity(params, cur_user = nil) ⇒ Object
-
#mass_switch_rep_roles(_params, cur_user = nil) ⇒ Object
-
#set_defaults ⇒ Object
Default new customer searches to every state except guest.
#mass_googlemap
Methods inherited from Search
#all_composite_columns, allowed_role_ids, #append_custom_column, #append_ouput_column, #append_to_sql_select_columns, #applicable_sort_terms, #apply_array_match, #apply_criteria, #apply_customer_cross_reference, #apply_distance_search, #apply_select, #apply_sort, #available_events, available_output_columns, #available_output_columns, base_search_class_name, #cleanup_search_results, composite_column, #composite_column, database_columns, default_columns, default_sort, #discard_excess, #effective_name, #effective_short_name, #employee, #enqueue_navbar_pinned_refresh, #fast_count, favorites, friendly_column_name, #get_pinned_results, #get_sort_term, global_favorite, has_role_for_search?, #human_query_params, instantiate_query_template, #instantiate_resource_updater, #limit_options, main_resource_class, main_resource_table, mass_actions_for_select, #mass_export, maximum_unpersisted_queries, #normalize_for_mass_update, options, options_classes, #output_columns_for_select, #pagy_from_search, #perform, #perform_selected, #pinned_query, #prepend_custom_column, #prepend_output_column, query_favorites_templates, #ransack_probe_params, recent_searches_for_employee_id, #record_list, #refresh_pinned_results, #remove_other_pins, search_name, #search_name, #search_results, search_type, search_type_humanized, select_sort_columns, #select_sort_columns, #select_statement, #set_sort_term, #sort_column1, #sort_column1=, #sort_column2, #sort_column2=, #sort_column3, #sort_column3=, #sort_columns_for_select, #sort_columns_to_hash, #target_geocoding, unpersisted_queries_quote_reached?, #unrecord_list, #validated_selected_columns, view_resource_class, view_resource_klass, #view_resource_klass, view_resource_table, visible?, with_search_results
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
config
#after_commit
#publish_event
Class Method Details
.custom_criteria_keys ⇒ Object
94
95
96
|
# File 'app/models/customer_search.rb', line 94
def self.custom_criteria_keys
CUSTOM_CRITERIA_KEYS
end
|
.scope_options_for_mass_schedule_activity ⇒ Object
330
331
332
|
# File 'app/models/customer_search.rb', line 330
def self.scope_options_for_mass_schedule_activity
[['Customer Only', 'customer_only'], ['Primary Contact', 'primary_contact'], ['All Active Contacts', 'all_active_contacts']]
end
|
.select_options_for_profile ⇒ Object
398
399
400
|
# File 'app/models/customer_search.rb', line 398
def self.select_options_for_profile
[['Any Organization', 'ORG']] + Profile.options_for_select(false)
end
|
Instance Method Details
#address_id_fields ⇒ Object
394
395
396
|
# File 'app/models/customer_search.rb', line 394
def address_id_fields
[%w[Shipping shipping_address_id], %w[Billing billing_address_id], %w[Mailing mailing_address_id]]
end
|
#apply_custom_criteria(results) ⇒ Object
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
# File 'app/models/customer_search.rb', line 113
def apply_custom_criteria(results)
results = apply_distance_search(results, custom_table_join: 'INNER JOIN addresses on addresses.party_id = view_customers.id', custom_table_alias: 'addresses')
has_ordered_query = []
has_quoted_query = []
if query_params[:has_ordered_item_id].present?
item_ids = Array(query_params[:has_ordered_item_id]).map(&:to_i)
cond = LineItem[:item_id].in(item_ids).to_sql
has_ordered_query << cond
has_quoted_query << cond
end
if query_params[:has_ordered_product_category_id].present?
pc_ids = Array(query_params[:has_ordered_product_category_id]).map(&:to_i)
cond = "items.pc_path_ids <@ ANY(#{ProductCategory.where(id: pc_ids).select(:ltree_path_ids).to_sql})"
has_ordered_query << cond
has_quoted_query << cond
end
if query_params[:has_ordered_product_line_id].present?
pl_ids = Array(query_params[:has_ordered_product_line_id]).map(&:to_i)
cond = "items.primary_pl_path_ids <@ ANY(#{ProductLine.where(id: pl_ids).select(:ltree_path_ids).to_sql})"
has_ordered_query << cond
has_quoted_query << cond
end
if query_params[:has_ordered_days].present?
has_ordered_query << "orders.shipped_date >= '#{Date.current.days_ago(query_params[:has_ordered_days].to_i)}'"
has_quoted_query << "quotes.complete_datetime >= '#{Date.current.days_ago(query_params[:has_ordered_days].to_i)}'"
end
if query_params[:has_ordered_days_time_range_gteq].present?
if query_params[:has_ordered_days_time_range_gteq].present? && query_params[:has_ordered_days_time_range_lteq].present?
has_ordered_query << "orders.shipped_date between '#{Date.current.days_ago(query_params[:has_ordered_days_time_range_lteq].to_i)}' and '#{Date.current.days_ago(query_params[:has_ordered_days_time_range_gteq].to_i)}'"
has_quoted_query << "quotes.complete_datetime between '#{Date.current.days_ago(query_params[:has_ordered_days_time_range_lteq].to_i)}' and '#{Date.current.days_ago(query_params[:has_ordered_days_time_range_gteq].to_i)}'"
else
has_ordered_query << "orders.shipped_date >= '#{Date.current.days_ago(query_params[:has_ordered_days_time_range_gteq].to_i)}'"
has_quoted_query << "quotes.complete_datetime >= '#{Date.current.days_ago(query_params[:has_ordered_days_time_range_gteq].to_i)}'"
end
end
if has_ordered_query.any?
oq = "from line_items inner join orders on orders.id = line_items.resource_id and line_items.resource_type = 'Order' inner join items on items.id = line_items.item_id inner join item_product_lines on item_product_lines.item_id = items.id where orders.customer_id = view_customers.id and orders.state = 'invoiced' and #{has_ordered_query.join(' and ')}"
qq = "from line_items inner join quotes on quotes.id = line_items.resource_id and line_items.resource_type = 'Quote' inner join opportunities on opportunities.id = quotes.opportunity_id inner join items on items.id = line_items.item_id inner join item_product_lines on item_product_lines.item_id = items.id where opportunities.customer_id = view_customers.id and quotes.state = 'complete' and #{has_quoted_query.join(' and ')}"
order_query = "EXISTS(select 1 #{oq})"
quote_query = "EXISTS(select 1 #{qq})"
if query_params[:minimum_number_of_orders].present?
min_orders = query_params[:minimum_number_of_orders].to_i
order_query += " and (select COUNT(distinct resource_id) #{oq}) >= #{min_orders}"
quote_query += " and (select COUNT(distinct resource_id) #{qq}) >= #{min_orders}"
end
results = case query_params[:has_ordered_type]
when 'both'
results.where("(#{order_query} or #{quote_query})")
when 'quotes'
results.where(quote_query)
else
results.where(order_query)
end
end
if query_params[:campaign_id_in].present?
campaign_ids = Array(query_params[:campaign_id_in]).map(&:to_i)
campaigns = Campaign.where(campaign_type: 'outside_sales', id: campaign_ids)
direct_ids = campaigns.joins(:subscribers).select(Subscriber[:customer_id])
email_ids = campaigns.joins(subscribers: :contact_points).select(ContactPoint[:party_id])
results = results.where.any_of({ id: direct_ids }, { id: email_ids })
campaign_in = Activity[:campaign_id].in(campaign_ids).to_sql
case query_params[:has_campaign_activity_status]
when 'open'
results = results.where("EXISTS(select 1 from activities where activities.party_id = view_customers.id and #{campaign_in} and activity_type_id is not null and activity_result_type_id is null)")
when 'any'
results = results.where("EXISTS(select 1 from activities where activities.party_id = view_customers.id and #{campaign_in} and activity_type_id is not null)")
when 'none'
results = results.where("NOT EXISTS(select 1 from activities where activities.party_id = view_customers.id and #{campaign_in} and activity_type_id is not null)")
end
end
if query_params[:campaign_id_not_in].present?
campaign_not_in_ids = Array(query_params[:campaign_id_not_in]).map(&:to_i)
campaign_not_in = Campaign[:id].in(campaign_not_in_ids).to_sql
results = results.where("NOT EXISTS(select 1 from campaigns LEFT OUTER JOIN campaigns_subscriber_lists ON campaigns_subscriber_lists.campaign_id = campaigns.id LEFT OUTER JOIN subscriber_lists ON subscriber_lists.id = campaigns_subscriber_lists.subscriber_list_id LEFT OUTER JOIN subscribers ON subscribers.subscriber_list_id = subscriber_lists.id LEFT OUTER JOIN contact_points ON contact_points.detail = subscribers.email_address LEFT OUTER JOIN parties ON parties.id = contact_points.party_id where (subscribers.customer_id = view_customers.id or parties.id = view_customers.id) and #{campaign_not_in})")
end
drop_reason_codes = Array(query_params[:custom_drop_reason_codes]).filter_map(&:presence)
results = results.joins(:customer_drop_events).where(customer_drop_events: { reason_code: drop_reason_codes }) if drop_reason_codes.present?
drop_rep_ids = Array(query_params[:custom_drop_rep_ids]).filter_map(&:presence)
results = results.joins(:customer_drop_events).where(customer_drop_events: { sales_rep_id: drop_rep_ids }) if drop_rep_ids.present?
profile_ids = Array(query_params[:profile_in]).filter_map(&:presence)
if profile_ids.present?
results = results.where.not(profile_id: [ProfileConstants::HOMEOWNER]) if profile_ids.delete('ORG')
results = results.where(profile_id: profile_ids) if profile_ids.present?
end
not_profile_ids = Array(query_params[:profile_not_in]).filter_map(&:presence)
if not_profile_ids.present?
results = results.where(profile_id: ProfileConstants::HOMEOWNER) if not_profile_ids.delete('ORG')
results = results.where.not(profile_id: not_profile_ids) if not_profile_ids.present?
end
results = results.joins(:certifications) if query_params[:is_certified_installer].present? && query_params[:is_certified_installer].to_b
results
end
|
#drop_action_reps_for_select ⇒ Object
224
225
226
227
|
# File 'app/models/customer_search.rb', line 224
def drop_action_reps_for_select
selected_customer_ids = search_results.pluck(:resource_id)
Employee.assigned_sales_rep_options_for_select(customer_ids: selected_customer_ids)
end
|
#mass_assign_reps(params, cur_user = nil) ⇒ Object
229
230
231
232
233
234
235
236
237
|
# File 'app/models/customer_search.rb', line 229
def mass_assign_reps(params, cur_user = nil)
jid = MassSearch::CustomerAssignRepsWorker.perform_async(
'search_id' => id,
'action_params' => { 'resource_params' => (params[:resource] || {}).to_h, 'activity' => (params[:activity] || {}).to_h },
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_assign_source(params, cur_user = nil) ⇒ Object
253
254
255
256
257
258
259
260
261
|
# File 'app/models/customer_search.rb', line 253
def mass_assign_source(params, cur_user = nil)
jid = ::SearchResourceUpdateWorker.perform_async(
'search_id' => id,
'action_params' => { 'resource_params' => { 'source_id' => params[:source_id] } },
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_assign_to_campaign(params, cur_user = nil) ⇒ Object
283
284
285
286
287
288
289
290
291
292
293
294
|
# File 'app/models/customer_search.rb', line 283
def mass_assign_to_campaign(params, cur_user = nil)
campaign_params = params[:campaign] || {}
return { status: :error, message: 'You must specify an existing campaign or enter a name for a new campaign' } if campaign_params[:campaign_id].blank? && campaign_params[:new_campaign_name].blank?
jid = MassSearch::CustomerAssignToCampaignWorker.perform_async(
'search_id' => id,
'action_params' => { 'campaign' => campaign_params.to_h },
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_assign_to_subscriber_list(params, cur_user = nil) ⇒ Object
273
274
275
276
277
278
279
280
281
|
# File 'app/models/customer_search.rb', line 273
def mass_assign_to_subscriber_list(params, cur_user = nil)
jid = MassSearch::CustomerAssignToSubscriberListWorker.perform_async(
'search_id' => id,
'action_params' => { 'subscriber_list_id' => params[:subscriber_list_id] },
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_auto_assign_sales_rep(params, cur_user = nil) ⇒ Object
239
240
241
242
243
244
245
246
247
248
249
250
251
|
# File 'app/models/customer_search.rb', line 239
def mass_auto_assign_sales_rep(params, cur_user = nil)
jid = MassSearch::CustomerAutoAssignSalesRepWorker.perform_async(
'search_id' => id,
'action_params' => {
'remove_existing_reps' => params[:remove_existing_reps],
'skip_lead_assignment' => params[:skip_lead_assignment],
'activity' => (params[:activity] || {}).to_h
},
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_create_subscriber_list(params, cur_user = nil) ⇒ Object
263
264
265
266
267
268
269
270
271
|
# File 'app/models/customer_search.rb', line 263
def mass_create_subscriber_list(params, cur_user = nil)
jid = MassSearch::CustomerCreateSubscriberListWorker.perform_async(
'search_id' => id,
'action_params' => { 'subscriber_list' => (params[:subscriber_list] || {}).to_h },
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_delete_on_lead_qualify_and_guest_state(_params, cur_user = nil) ⇒ Object
306
307
308
309
310
311
312
313
314
|
# File 'app/models/customer_search.rb', line 306
def mass_delete_on_lead_qualify_and_guest_state(_params, cur_user = nil)
jid = MassSearch::CustomerDeleteLeadsWorker.perform_async(
'search_id' => id,
'action_params' => {},
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_drop_sales_rep(params, cur_user) ⇒ Object
334
335
336
337
338
339
340
341
342
|
# File 'app/models/customer_search.rb', line 334
def mass_drop_sales_rep(params, cur_user)
jid = MassSearch::CustomerDropSalesRepWorker.perform_async(
'search_id' => id,
'action_params' => { 'sales_rep_id' => params[:sales_rep_id], 'reason_code' => params[:reason_code], 'comment' => params[:comment] },
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_locator_blacklist(params, cur_user) ⇒ Object
354
355
356
357
358
359
360
361
362
|
# File 'app/models/customer_search.rb', line 354
def mass_locator_blacklist(params, cur_user)
jid = MassSearch::CustomerLocatorBlacklistWorker.perform_async(
'search_id' => id,
'action_params' => { 'reason' => params[:reason] },
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_locator_unblacklist(params, cur_user) ⇒ Object
374
375
376
377
378
379
380
381
382
|
# File 'app/models/customer_search.rb', line 374
def mass_locator_unblacklist(params, cur_user)
jid = MassSearch::CustomerLocatorUnblacklistWorker.perform_async(
'search_id' => id,
'action_params' => { 'inform_customer' => params[:inform_customer] },
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_locator_unwhitelist(_params, cur_user) ⇒ Object
364
365
366
367
368
369
370
371
372
|
# File 'app/models/customer_search.rb', line 364
def mass_locator_unwhitelist(_params, cur_user)
jid = MassSearch::CustomerLocatorUnwhitelistWorker.perform_async(
'search_id' => id,
'action_params' => {},
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_locator_whitelist(params, cur_user) ⇒ Object
344
345
346
347
348
349
350
351
352
|
# File 'app/models/customer_search.rb', line 344
def mass_locator_whitelist(params, cur_user)
jid = MassSearch::CustomerLocatorWhitelistWorker.perform_async(
'search_id' => id,
'action_params' => { 'reason' => params[:reason] },
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_online_account_invite(_params, cur_user = nil) ⇒ Object
384
385
386
387
388
389
390
391
392
|
# File 'app/models/customer_search.rb', line 384
def mass_online_account_invite(_params, cur_user = nil)
jid = MassSearch::CustomerOnlineAccountInviteWorker.perform_async(
'search_id' => id,
'action_params' => {},
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_schedule_activity(params, cur_user = nil) ⇒ Object
316
317
318
319
320
321
322
323
324
325
326
327
328
|
# File 'app/models/customer_search.rb', line 316
def mass_schedule_activity(params, cur_user = nil)
activity_params = (params[:activity] || {}).to_h
apply_scope = activity_params.delete('apply_scope')
campaign_id = activity_params.delete('campaign_id')
jid = MassSearch::CustomerScheduleActivityWorker.perform_async(
'search_id' => id,
'action_params' => { 'activity' => activity_params, 'apply_scope' => apply_scope, 'campaign_id' => campaign_id },
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#mass_switch_rep_roles(_params, cur_user = nil) ⇒ Object
296
297
298
299
300
301
302
303
304
|
# File 'app/models/customer_search.rb', line 296
def mass_switch_rep_roles(_params, cur_user = nil)
jid = MassSearch::CustomerSwitchRepRolesWorker.perform_async(
'search_id' => id,
'action_params' => {},
'user_id' => cur_user&.id,
'locale' => I18n.locale.to_s
)
{ status: :ok, job_id: jid }
end
|
#set_defaults ⇒ Object
Default new customer searches to every state except guest. Expressed as
state_in (positive list) rather than state_not_in: ['guest'] so the
Status filter renders as "is any of" with the included states visible —
clearer to users than an exclusion they have to mentally invert.
102
103
104
105
106
107
108
109
110
111
|
# File 'app/models/customer_search.rb', line 102
def set_defaults
super
return unless new_record?
qp = (query_params || {}).with_indifferent_access
return if qp[:state_in].present? || qp[:state_not_in].present?
non_guest_states = Customer.states_for_select.map(&:last) - ['guest']
self.query_params = (query_params || {}).merge('state_in' => non_guest_states)
end
|