Class: CustomerFilter

Inherits:
ApplicationRecord show all
Includes:
Memery, Models::Auditable
Defined in:
app/models/customer_filter.rb

Overview

== Schema Information

Table name: customer_filters
Database name: primary

id :integer not null, primary key
cities :string(255) default([]), is an Array
customer_states :string(255) default([]), is an Array
digest :string(32)
exclude_buying_group_ids :boolean default(FALSE)
exclude_catalog_ids :boolean default(FALSE)
exclude_cities :boolean default(FALSE)
exclude_customer_states :boolean default(FALSE)
exclude_parties :boolean default(FALSE)
exclude_postal_codes :boolean default(FALSE)
exclude_profile_ids :boolean default(FALSE)
exclude_report_groupings :boolean default(FALSE)
exclude_source_ids :boolean default(FALSE)
exclude_state_codes :boolean default(FALSE)
exclude_tier2_program_pricing_ids :boolean default(FALSE)
has_email :integer default(0), not null
has_fax :integer default(0), not null
has_phone :integer default(0), not null
has_sales_order :integer default(0), not null
is_auto_generated :boolean
name :string(120) not null
opportunity_count_max :integer
opportunity_count_min :integer
opportunity_value_max :integer
opportunity_value_min :integer
phone_number_starts_with :string(12)
position :integer
postal_codes :string(10) default([]), is an Array
report_groupings :string default([]), is an Array
state_codes :string(2) default([]), is an Array
created_at :datetime not null
updated_at :datetime not null
creator_id :integer
store_id :integer
updater_id :integer

Indexes

index_customer_filters_on_digest (digest) UNIQUE
index_customer_filters_on_is_auto_generated (is_auto_generated)
index_customer_filters_on_store_id_and_name (store_id,name) UNIQUE
index_phone_number_starts_with_trgrm (phone_number_starts_with) USING gin

Foreign Keys

customer_filters_store_id_fk (store_id => stores.id)

Defined Under Namespace

Classes: QueryBuilder

Constant Summary collapse

DEFAULT =

"All w/out DB/ETL/HD/COSTCO"

138
CONDITIONS =

Represent all the accessor methods to conditional values

%i[party_ids customer_states report_groupings catalogs store profiles buying_groups
sources state_codes postal_codes cities tier2_program_pricings phone_number_starts_with
has_sales_order].freeze
UNIQUE_FIELDS =

These fields will be used to compare uniqueness of customer filters

%i[store_id exclude_catalog_ids exclude_profile_ids exclude_state_codes exclude_cities
exclude_postal_codes exclude_buying_group_ids exclude_source_ids exclude_tier2_program_pricing_ids
exclude_report_groupings exclude_customer_states exclude_parties
state_codes cities postal_codes report_groupings customer_states
catalog_ids profile_ids buying_group_ids source_ids
tier2_program_pricing_ids party_ids has_phone has_email has_fax has_sales_order].freeze
NA =

Na.

0
YES =

Yes.

1
NO =

No.

2
TRIPLE_STATE =

Triple state.

{ NA => 'Not Applicable', YES => 'Must Have One', NO => 'Must Have None' }.freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

Belongs to collapse

Has many collapse

Has and belongs to 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::Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#activity_typesActiveRecord::Associations::CollectionProxy<ActivityType>

Returns activity types using this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<ActivityType>)

    activity types using this filter



99
# File 'app/models/customer_filter.rb', line 99

has_many :activity_types, inverse_of: :customer_filter

#auto_apply_couponsActiveRecord::Associations::CollectionProxy<Coupon>

Returns coupons auto-applied via this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<Coupon>)

    coupons auto-applied via this filter



105
# File 'app/models/customer_filter.rb', line 105

has_many :auto_apply_coupons, inverse_of: :auto_apply_customer_filter, class_name: 'Coupon', foreign_key: :auto_apply_customer_filter_id

#buying_groupsActiveRecord::Associations::CollectionProxy<BuyingGroup>

Returns buying groups matched by this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<BuyingGroup>)

    buying groups matched by this filter



118
# File 'app/models/customer_filter.rb', line 118

has_and_belongs_to_many :buying_groups

#catalogsActiveRecord::Associations::CollectionProxy<Catalog>

Returns catalogs matched by this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<Catalog>)

    catalogs matched by this filter



112
# File 'app/models/customer_filter.rb', line 112

has_and_belongs_to_many :catalogs

#couponsActiveRecord::Associations::CollectionProxy<Coupon>

Returns coupons using this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<Coupon>)

    coupons using this filter



102
# File 'app/models/customer_filter.rb', line 102

has_many :coupons, inverse_of: :customer_filter

#iq_accessory_filtersActiveRecord::Associations::CollectionProxy<IqAccessoryFilter>

Returns IQ accessory filters using this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<IqAccessoryFilter>)

    IQ accessory filters using this filter



108
# File 'app/models/customer_filter.rb', line 108

has_many :iq_accessory_filters, inverse_of: :customer_filter

#partiesActiveRecord::Associations::CollectionProxy<Party>

Returns individual parties matched by this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<Party>)

    individual parties matched by this filter



124
# File 'app/models/customer_filter.rb', line 124

has_and_belongs_to_many :parties, validate: false

#profilesActiveRecord::Associations::CollectionProxy<Profile>

Returns profiles matched by this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<Profile>)

    profiles matched by this filter



115
# File 'app/models/customer_filter.rb', line 115

has_and_belongs_to_many :profiles

#sales_rep_queue_entriesActiveRecord::Associations::CollectionProxy<SalesRepQueueEntry>

Returns queue entries using this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<SalesRepQueueEntry>)

    queue entries using this filter



93
# File 'app/models/customer_filter.rb', line 93

has_many :sales_rep_queue_entries, inverse_of: :customer_filter

#sourcesActiveRecord::Associations::CollectionProxy<Source>

Returns sources matched by this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<Source>)

    sources matched by this filter



121
# File 'app/models/customer_filter.rb', line 121

has_and_belongs_to_many :sources

#storeStore?

Returns the store this filter is scoped to, when set.

Returns:

  • (Store, nil)

    the store this filter is scoped to, when set



90
# File 'app/models/customer_filter.rb', line 90

belongs_to :store, optional: true

#tier2_program_pricingsActiveRecord::Associations::CollectionProxy<Coupon::Tier2ProgramPricing>

Returns program pricings matched by this filter.

Returns:



127
128
129
# File 'app/models/customer_filter.rb', line 127

has_and_belongs_to_many :tier2_program_pricings, class_name: 'Coupon::Tier2ProgramPricing',
join_table: 'customer_filters_program_pricings',
association_foreign_key: 'tier2_program_pricing_id'

#topicsActiveRecord::Associations::CollectionProxy<Topic>

Returns topics using this filter.

Returns:

  • (ActiveRecord::Associations::CollectionProxy<Topic>)

    topics using this filter



96
# File 'app/models/customer_filter.rb', line 96

has_many :topics, inverse_of: :customer_filter

Class Method Details

.create_single_customer_filter(customer) ⇒ CustomerFilter?

Finds or creates the auto-generated single-customer filter for a customer.

Parameters:

  • customer (Customer)

    the customer to build the filter around

Returns:

  • (CustomerFilter, nil)

    the found or created filter, nil for non-Customer input



152
153
154
155
156
157
158
159
160
# File 'app/models/customer_filter.rb', line 152

def self.create_single_customer_filter(customer)
  cf = nil
  if customer.is_a?(Customer) # ensure we are dealing with a real customer
    party_criteria = { parties: { id: customer.id }, is_auto_generated: true }
    create_criteria = { name: "#{customer.full_name} - #{customer.reference_number} - Customer Preference", is_auto_generated: true, party_ids: [customer.id] }
    cf = CustomerFilter.joins(:parties).where(party_criteria).first_or_create(create_criteria)
  end
  cf
end

.duplicate_filtersActiveRecord::Relation<CustomerFilter>

A relation of CustomerFilters that are duplicate filters. Active Record Scope

Returns:

See Also:



135
# File 'app/models/customer_filter.rb', line 135

scope :duplicate_filters, -> { where("EXISTS(select digest from customer_filters cf where cf.digest = customer_filters.digest group by digest having count(digest) > 1)") }

.options_for_select {|cfs| ... } ⇒ Array<Array(String, Integer)>

Builds the [name, id] option pairs for select dropdowns.

Yield Parameters:

  • cfs (ActiveRecord::Relation<CustomerFilter>)

    optional block to narrow the relation

Returns:

  • (Array<Array(String, Integer)>)

    sorted name/id pairs



143
144
145
146
147
# File 'app/models/customer_filter.rb', line 143

def self.options_for_select
  cfs = CustomerFilter.all
  cfs = yield(cfs) if block_given?
  cfs.sorted.pluck(:name, :id)
end

.sortedActiveRecord::Relation<CustomerFilter>

A relation of CustomerFilters that are sorted. Active Record Scope

Returns:

See Also:



134
# File 'app/models/customer_filter.rb', line 134

scope :sorted, -> { order("customer_filters.name") }

Instance Method Details

#all_catalog_idsArray<Integer>

Convenience method that will retrieve all catalog_ids associated with the filter

Returns:

  • (Array<Integer>)

    sorted unique catalog ids implied by this filter



581
582
583
584
585
586
587
588
589
590
591
592
593
# File 'app/models/customer_filter.rb', line 581

def all_catalog_ids
  # Convenience method that will retrieve all catalog_ids associated with the filter
  cids = []
  cids += parties.map { |p| p.try(:catalog_id) }
  if catalog_ids.present?
    if exclude_catalog_ids
      cids -= catalog_ids
    else
      cids += catalog_ids
    end
  end
  cids.compact.uniq.sort
end

#applies_to_customer?(customer) ⇒ Boolean

Runs the full rule chain against a customer.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when every rule matches



307
308
309
# File 'app/models/customer_filter.rb', line 307

def applies_to_customer?(customer)
  build_matcher(customer).match?
end

#build_matcher(customer) ⇒ CustomerRuleMatcher

Builds the rule matcher that evaluates every *_match? rule against a customer.

Parameters:

  • customer (Customer)

    the customer to evaluate

Returns:

  • (CustomerRuleMatcher)

    the configured matcher



475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'app/models/customer_filter.rb', line 475

def build_matcher(customer)
  CustomerRuleMatcher.new(self, customer,
                :party_match?,
                :customer_state_match?,
                :report_grouping_match?,
                :catalog_match?,
                :store_match?,
                :profile_match?,
                :tier2_program_pricing_match?,
                :buying_group_match?,
                :source_match?,
                :state_code_match?,
                :postal_code_match?,
                :city_match?,
                :has_phone_match?,
                :has_fax_match?,
                :has_email_match?,
                :has_phone_number_match?,
                :has_sales_order_match?,
                :has_opportunity_match?)
end

#buying_group_match?(customer) ⇒ Boolean

Returns true when the customer's buying group matches the filter.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer's buying group matches the filter



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

def buying_group_match?(customer)
  meta_assoc_match buying_groups, customer.buying_group_id, exclude_buying_group_ids
end

#calculate_digestString

Returns MD5 hex digest of the raw digest fields.

Returns:

  • (String)

    MD5 hex digest of the raw digest fields



530
531
532
533
534
# File 'app/models/customer_filter.rb', line 530

def calculate_digest
  dg = Digest::MD5.hexdigest raw_digest_fields
  logger.debug "Calculated digest #{dg} for customer filter #{id}"
  dg
end

#catalog_match?(customer) ⇒ Boolean

Returns true when the customer's catalog matches the filter.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer's catalog matches the filter



205
206
207
# File 'app/models/customer_filter.rb', line 205

def catalog_match?(customer)
  meta_assoc_match catalogs, customer.catalog_id, exclude_catalog_ids
end

#chain_buying_groups_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by buying_group_ids.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by buying_group_ids



449
450
451
452
453
454
455
456
457
# File 'app/models/customer_filter.rb', line 449

def chain_buying_groups_query(customers_query)
  return customers_query if buying_group_ids.blank?

  if exclude_buying_group_ids
    customers_query.where.not(buying_group_id: buying_group_ids)
  else
    customers_query.where(buying_group_id: buying_group_ids)
  end
end

#chain_catalogs_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by catalog_ids.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by catalog_ids



345
346
347
348
349
350
351
352
353
# File 'app/models/customer_filter.rb', line 345

def chain_catalogs_query(customers_query)
  return customers_query if catalog_ids.blank?

  if exclude_catalog_ids
    customers_query.where.not(catalog_id: catalog_ids)
  else
    customers_query.where(catalog_id: catalog_ids)
  end
end

#chain_cities_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by cities.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by cities



393
394
395
396
397
398
399
400
401
# File 'app/models/customer_filter.rb', line 393

def chain_cities_query(customers_query)
  return customers_query if cities.blank?

  if exclude_cities
    customers_query.joins(:addresses).where(Address[:city].not_in(cities).or(Address[:city].eq(nil)))
  else
    customers_query.joins(:addresses).where(Address[:city].in(cities))
  end
end

#chain_customer_states_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by customer_states.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by customer_states



413
414
415
416
417
418
419
420
421
# File 'app/models/customer_filter.rb', line 413

def chain_customer_states_query(customers_query)
  return customers_query if customer_states.blank?

  if exclude_customer_states
    customers_query.where.not(state: customer_states)
  else
    customers_query.where(state: customer_states)
  end
end

#chain_parties_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by party_ids.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by party_ids



437
438
439
440
441
442
443
444
445
# File 'app/models/customer_filter.rb', line 437

def chain_parties_query(customers_query)
  return customers_query if party_ids.blank?

  if exclude_parties
    customers_query.where.not(id: party_ids)
  else
    customers_query.where(id: party_ids)
  end
end

#chain_postal_codes_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by postal_codes.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by postal_codes



381
382
383
384
385
386
387
388
389
# File 'app/models/customer_filter.rb', line 381

def chain_postal_codes_query(customers_query)
  return customers_query if postal_codes.blank?

  if exclude_postal_codes
    customers_query.joins(:addresses).where(Address[:zip].not_in(postal_codes).or(Address[:zip].eq(nil)))
  else
    customers_query.joins(:addresses).where(Address[:zip].in(postal_codes))
  end
end

#chain_profiles_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by profile_ids.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by profile_ids



333
334
335
336
337
338
339
340
341
# File 'app/models/customer_filter.rb', line 333

def chain_profiles_query(customers_query)
  return customers_query if profile_ids.blank?

  if exclude_profile_ids
    customers_query.where(Customer[:profile_id].not_in(profile_ids).or(Customer[:profile_id].eq(nil)))
  else
    customers_query.where(profile_id: profile_ids)
  end
end

#chain_program_pricing_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by tier2_program_pricing_ids.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by tier2_program_pricing_ids



425
426
427
428
429
430
431
432
433
# File 'app/models/customer_filter.rb', line 425

def chain_program_pricing_query(customers_query)
  return customers_query if tier2_program_pricing_ids.blank?

  if exclude_tier2_program_pricing_ids
    customers_query.where.not(tier2_program_pricing_id: tier2_program_pricing_ids)
  else
    customers_query.where(tier2_program_pricing_id: tier2_program_pricing_ids)
  end
end

#chain_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Narrows a customer relation by every populated filter condition.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    the narrowed relation



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'app/models/customer_filter.rb', line 315

def chain_query(customers_query)
  customers_query = chain_report_groupings_query(customers_query)
  customers_query = chain_buying_groups_query(customers_query)
  customers_query = chain_parties_query(customers_query)
  customers_query = chain_program_pricing_query(customers_query)
  customers_query = chain_customer_states_query(customers_query)
  customers_query = chain_store_query(customers_query)
  customers_query = chain_cities_query(customers_query)
  customers_query = chain_postal_codes_query(customers_query)
  customers_query = chain_state_codes_query(customers_query)
  customers_query = chain_sources_query(customers_query)
  customers_query = chain_catalogs_query(customers_query)
  chain_profiles_query(customers_query)
  # Todo phone match, email match, fax match, order match
end

#chain_report_groupings_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by report_groupings.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by report_groupings



461
462
463
464
465
466
467
468
469
470
# File 'app/models/customer_filter.rb', line 461

def chain_report_groupings_query(customers_query)
  return customers_query if report_groupings.blank?

  if exclude_report_groupings
    # a customer with nil is considered excluded too we have to test for this condition specifically in sql
    customers_query.where(Customer[:report_grouping].eq(nil).or(Customer[:report_grouping].not_in(report_groupings)))
  else
    customers_query.where(Customer[:report_grouping].in(report_groupings))
  end
end

#chain_sources_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by source_ids.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by source_ids



357
358
359
360
361
362
363
364
365
# File 'app/models/customer_filter.rb', line 357

def chain_sources_query(customers_query)
  return customers_query if source_ids.blank?

  if exclude_source_ids
    customers_query.where(Customer[:source_id].not_in(source_ids).or(Customer[:source_id].eq(nil)))
  else
    customers_query.where(Customer[:source_id].in(source_ids))
  end
end

#chain_state_codes_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by state_codes.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by state_codes



369
370
371
372
373
374
375
376
377
# File 'app/models/customer_filter.rb', line 369

def chain_state_codes_query(customers_query)
  return customers_query if state_codes.blank?

  if exclude_state_codes
    customers_query.where(Customer[:state_code].not_in(state_codes).or(Customer[:state_code].eq(nil)))
  else
    customers_query.where(Customer[:state_code].in(state_codes))
  end
end

#chain_store_query(customers_query) ⇒ ActiveRecord::Relation<Customer>

Returns relation filtered by store_id.

Parameters:

  • customers_query (ActiveRecord::Relation<Customer>)

    the relation to narrow

Returns:

  • (ActiveRecord::Relation<Customer>)

    relation filtered by store_id



405
406
407
408
409
# File 'app/models/customer_filter.rb', line 405

def chain_store_query(customers_query)
  return customers_query if store_id.blank?

  customers_query.joins(:catalog).where(Catalog[:store_id].eq(store_id))
end

#city_match?(customer) ⇒ Boolean

Returns true when the customer's city matches the filter.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer's city matches the filter



241
242
243
# File 'app/models/customer_filter.rb', line 241

def city_match?(customer)
  meta_string_match customer.city, cities, exclude_cities
end

#customer_state_match?(customer) ⇒ Boolean

Returns true when the customer's lifecycle state matches the filter.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer's lifecycle state matches the filter



223
224
225
# File 'app/models/customer_filter.rb', line 223

def customer_state_match?(customer)
  meta_string_match customer.state, customer_states, exclude_customer_states
end

#duplicate_filtersActiveRecord::Relation<CustomerFilter>

Returns filters sharing this filter's digest.

Returns:

  • (ActiveRecord::Relation<CustomerFilter>)

    filters sharing this filter's digest



498
499
500
501
502
# File 'app/models/customer_filter.rb', line 498

def duplicate_filters
  filters = CustomerFilter.where(digest: calculate_digest)
  filters = filters.where(CustomerFilter[:id].not_eq(id)) unless new_record?
  filters
end

#fields_arrayArray<String>

Returns sorted "field:value" strings for populated UNIQUE_FIELDS.

Returns:

  • (Array<String>)

    sorted "field:value" strings for populated UNIQUE_FIELDS



510
511
512
513
514
515
516
517
# File 'app/models/customer_filter.rb', line 510

def fields_array
  raw_fields = UNIQUE_FIELDS.each_with_object([]) do |field, array|
    val = send(field)
    val = val.compact.sort if !val.nil? && val.is_a?(Array)
    array << "#{field}:#{val}" if val.present?
  end
  (raw_fields || []).sort
end

#friendly_digestString

Returns human-readable sentence of the populated filter fields.

Returns:

  • (String)

    human-readable sentence of the populated filter fields



520
521
522
# File 'app/models/customer_filter.rb', line 520

def friendly_digest
  fields_array.to_sentence
end

#has_a_condition?Boolean

Returns true when at least one filter condition is populated.

Returns:

  • (Boolean)

    true when at least one filter condition is populated



575
576
577
# File 'app/models/customer_filter.rb', line 575

def has_a_condition?
  CONDITIONS.any? { |cnd| send(cnd).present? }
end

#has_duplicate_filter?Boolean

Returns true when another filter has the same digest.

Returns:

  • (Boolean)

    true when another filter has the same digest



505
506
507
# File 'app/models/customer_filter.rb', line 505

def has_duplicate_filter?
  duplicate_filters.present?
end

#has_email_match?(customer) ⇒ Boolean

Returns true per the has_email triple-state against email contact points.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true per the has_email triple-state against email contact points



291
292
293
# File 'app/models/customer_filter.rb', line 291

def has_email_match?(customer)
  relation_triple_state_presence_check(customer.all_contact_points.emails, has_email)
end

#has_fax_match?(customer) ⇒ Boolean

Returns true per the has_fax triple-state against fax contact points.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true per the has_fax triple-state against fax contact points



259
260
261
# File 'app/models/customer_filter.rb', line 259

def has_fax_match?(customer)
  relation_triple_state_presence_check(customer.all_contact_points.faxes, has_fax)
end

#has_opportunity_match?(customer) ⇒ Boolean

Checks open sales-opportunity count/value bounds.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when no bounds are set or the customer's opportunities fall within them



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'app/models/customer_filter.rb', line 272

def has_opportunity_match?(customer)
  # The lack of filter is a true check
  return true unless opportunity_value_min || opportunity_value_max || opportunity_count_min || opportunity_count_max

  opp_query = customer.opportunities.open_opportunities.where(opportunity_type: 'S')

  opp_query = opp_query.where(value: opportunity_value_min..) if opportunity_value_min.present?
  opp_query = opp_query.where(value: opportunity_value_max..) if opportunity_value_max.present?
  if opportunity_count_max.present?
    return false if opp_query.size > opportunity_count_max
  end
  if opportunity_count_min.present?
    return false if opp_query.size < opportunity_count_min
  end
  true # Logically you'll exit before this
end

#has_phone_match?(customer) ⇒ Boolean

Returns true per the has_phone triple-state against voice-callable contact points.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true per the has_phone triple-state against voice-callable contact points



253
254
255
# File 'app/models/customer_filter.rb', line 253

def has_phone_match?(customer)
  relation_triple_state_presence_check(customer.all_contact_points.voice_callable, has_phone)
end

#has_phone_number_match?(customer) ⇒ Boolean, ...

Returns true when no dialable phones, else the first phone matching the prefix.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean, ContactPoint, nil)

    true when no dialable phones, else the first phone matching the prefix



297
298
299
300
301
302
# File 'app/models/customer_filter.rb', line 297

def has_phone_number_match?(customer)
  phones = customer.all_contact_points.dialable
  return true if phones.empty?

  phones.find { |cp| meta_string_match cp.detail, phone_number_starts_with, false, :allow_partial }
end

#has_sales_order_match?(customer) ⇒ Boolean

Returns true per the has_sales_order triple-state against active sales orders.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true per the has_sales_order triple-state against active sales orders



265
266
267
# File 'app/models/customer_filter.rb', line 265

def has_sales_order_match?(customer)
  relation_triple_state_presence_check(customer.orders.so_only.active, has_sales_order)
end

#meta_assoc_match(assoc, value, exclude) ⇒ Boolean

Convenience method to rescue us from repetition. Association to check against, value to check for, exclude turned on or off
Optimized to use in-memory lookup when association is preloaded to avoid N+1 queries

Parameters:

  • assoc (ActiveRecord::Associations::CollectionProxy)

    the association to check against

  • value (Integer, nil)

    the record id to look for

  • exclude (Boolean)

    whether the match is inverted (exclude mode)

Returns:

  • (Boolean)

    true when the association match (or exclusion) holds



542
543
544
545
546
547
548
549
550
551
552
553
554
555
# File 'app/models/customer_filter.rb', line 542

def meta_assoc_match(assoc, value, exclude)
  # Use loaded? check to avoid N+1 - if preloaded, check in memory
  if assoc.loaded?
    return true if assoc.empty?

    value_in_list = assoc.any? { |record| record.id == value }
  else
    # When not preloaded, fall back to database queries
    return true if assoc.empty?

    value_in_list = assoc.exists?(id: value)
  end
  exclude ? !value_in_list : value_in_list
end

#meta_string_match(value, values, exclude, allow_partial = false) ⇒ Boolean

Matches a scalar value against a list of strings, honoring exclude mode.

Parameters:

  • value (String, nil)

    the value to check

  • values (Array<String>)

    the filter values to match against

  • exclude (Boolean)

    whether the match is inverted (exclude mode)

  • allow_partial (Boolean) (defaults to: false)

    when true, a prefix match counts

Returns:

  • (Boolean)

    true when values is blank or the (possibly inverted) match holds



563
564
565
566
567
568
569
570
571
572
# File 'app/models/customer_filter.rb', line 563

def meta_string_match(value, values, exclude, allow_partial = false)
  return true if values.blank?

  value_in_list = if allow_partial
                    value&.starts_with?(*values)
                  else
                    value.in?(values)
                  end
  exclude ? !value_in_list : value_in_list
end

#party_match?(customer) ⇒ Boolean

Returns true when the customer is (or is excluded from) the party list.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer is (or is excluded from) the party list



217
218
219
# File 'app/models/customer_filter.rb', line 217

def party_match?(customer)
  meta_assoc_match parties, customer.id, exclude_parties
end

#postal_code_match?(customer) ⇒ Boolean

Returns true when the customer's postal code matches the filter (partial allowed).

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer's postal code matches the filter (partial allowed)



235
236
237
# File 'app/models/customer_filter.rb', line 235

def postal_code_match?(customer)
  meta_string_match customer.zip, postal_codes, exclude_postal_codes, :allow_partial
end

#profile_match?(customer) ⇒ Boolean

Returns true when the customer's profile matches the filter.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer's profile matches the filter



187
188
189
# File 'app/models/customer_filter.rb', line 187

def profile_match?(customer)
  meta_assoc_match profiles, customer.profile_id, exclude_profile_ids
end

#raw_digest_fieldsString

Returns pipe-joined raw digest input string.

Returns:

  • (String)

    pipe-joined raw digest input string



525
526
527
# File 'app/models/customer_filter.rb', line 525

def raw_digest_fields
  fields_array.join('|').to_s
end

#referenced?Boolean

Whether any queue entry, topic, activity type, or coupon references this filter.

Returns:

  • (Boolean)

    true when the filter is in use



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

def referenced?
  (
    sales_rep_queue_entries.present? or
    topics.present? or
    activity_types.present? or
    coupons.present? or
    auto_apply_coupons.present?
  )
end

#report_grouping_match?(customer) ⇒ Boolean

Returns true when the customer's report grouping matches the filter.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer's report grouping matches the filter



247
248
249
# File 'app/models/customer_filter.rb', line 247

def report_grouping_match?(customer)
  meta_string_match customer.effective_report_grouping, report_groupings, exclude_report_groupings
end

#source_match?(customer) ⇒ Boolean

Returns true when the customer's source matches the filter.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer's source matches the filter



211
212
213
# File 'app/models/customer_filter.rb', line 211

def source_match?(customer)
  meta_assoc_match sources, customer.source_id, exclude_source_ids
end

#state_code_match?(customer) ⇒ Boolean

Returns true when the customer's state code matches the filter.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer's state code matches the filter



229
230
231
# File 'app/models/customer_filter.rb', line 229

def state_code_match?(customer)
  meta_string_match customer.state_code, state_codes, exclude_state_codes
end

#store_match?(customer) ⇒ Boolean

Returns true when the filter has no store or the customer's store matches.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the filter has no store or the customer's store matches



181
182
183
# File 'app/models/customer_filter.rb', line 181

def store_match?(customer)
  store.nil? or customer.store == store
end

#tier2_program_pricing_match?(customer) ⇒ Boolean

Returns true when the customer's tier2 program pricing matches the filter.

Parameters:

  • customer (Customer)

    the customer to check

Returns:

  • (Boolean)

    true when the customer's tier2 program pricing matches the filter



193
194
195
# File 'app/models/customer_filter.rb', line 193

def tier2_program_pricing_match?(customer)
  meta_assoc_match tier2_program_pricings, customer.tier2_program_pricing_id, exclude_tier2_program_pricing_ids
end

#to_sString

Returns name and id, e.g. "My Filter (12)".

Returns:

  • (String)

    name and id, e.g. "My Filter (12)"



163
164
165
# File 'app/models/customer_filter.rb', line 163

def to_s
  "#{name} (#{id})"
end