Class: LocatorRecord

Inherits:
ApplicationRecord show all
Extended by:
FriendlyId
Includes:
Models::Auditable, Models::LiquidMethods, Models::Taggable
Defined in:
app/models/locator_record.rb

Overview

== Schema Information

Table name: locator_records
Database name: primary

id :integer not null, primary key
confirmed_at :datetime
date_of_wy_visit :datetime
date_wyu_last_passed :datetime
has_countertop_display :boolean
has_had_wy_visit :boolean
has_in_floor_display :boolean
has_in_house_service_team :boolean
has_marketing_materials :boolean
has_passed_wyu :boolean
is_electrician :boolean
is_inactive :boolean default(FALSE), not null
is_installer :boolean
is_remodel_contractor :boolean
is_retailer :boolean
is_wy_certified :boolean
jobs_a_year :string
last_confirmed_at :datetime
maintains_wy_stock :boolean
preferred_dealer :boolean
profile_ids :text
slug :string
star_status_level :integer
state :string(255)
supports_comfort_products :boolean
supports_flooring :boolean
supports_roof_deicing :boolean
supports_snowmelting :boolean
created_at :datetime
updated_at :datetime
address_id :integer
customer_id :integer

Indexes

idx_state (state)
index_locator_records_confirmed_at (confirmed_at)
index_locator_records_customer_id (customer_id)
index_locator_records_on_address_id (address_id)
index_locator_records_on_slug (slug)
index_locator_records_on_supports_roof_deicing (supports_roof_deicing)

Constant Summary collapse

STAR_STATUS_LEVELS =

Star status levels.

%w[0 1 2 3 4 5].freeze
JOBS_A_YEAR =

Jobs a year.

%w[1-5 5-10 10-50 50+].freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has many collapse

Methods included from Models::Taggable

#tag_records, #taggings

Delegated Instance Attributes collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::Taggable

#add_tag, all_tags, #has_tag?, normalize_tag_names, not_tagged_with, #remove_tag, #tag_list, #tag_list=, #taggable_type_for_tagging, tagged_with, #tags, #tags=, tags_cloud, tags_exclude, tags_include, with_all_tags, with_any_tags, without_all_tags, without_any_tags

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

Class Method Details

.activeActiveRecord::Relation<LocatorRecord>

A relation of LocatorRecords that are active. Active Record Scope

Returns:

See Also:



76
# File 'app/models/locator_record.rb', line 76

scope :active, -> { where(is_inactive: false) }

.are_confirmedActiveRecord::Relation<LocatorRecord>

A relation of LocatorRecords that are are confirmed. Active Record Scope

Returns:

See Also:



77
# File 'app/models/locator_record.rb', line 77

scope :are_confirmed, -> { where(state: 'confirmed') }

.filter_optionsHash{String => String}

Label → capability-attribute map for the locator filter dropdown.

Returns:

  • (Hash{String => String})


125
126
127
128
129
130
131
132
133
134
135
136
# File 'app/models/locator_record.rb', line 125

def self.filter_options
  { 'Supports Comfort Products' => 'supports_comfort_products' }
  # { "Retailer" => "is_retailer",
  #   "Installer" => "is_installer",
  #   "Electrician" => "is_electrician",
  #   "Remodeling Contractor" => "is_remodel_contractor",
  #   "Supports Floor Heating" => "supports_flooring",
  #   "Supports Snow Melting" => "supports_snowmelting",
  #   "Supports Roof & Gutter Deicing" => "supports_roof_deicing",
  #   "Supports Comfort Products" => "supports_comfort_products",
  #   "Preferred Stocking Kit Dealer" => "preferred_dealer" }
end

.near_coordinatesActiveRecord::Relation<LocatorRecord>

A relation of LocatorRecords that are near coordinates. Active Record Scope

Returns:

See Also:



78
79
80
81
82
83
84
# File 'app/models/locator_record.rb', line 78

scope :near_coordinates, ->(coordinates, distance) {
  are_confirmed
    .active
    .joins(:address, :customer)
    .merge(Address.near(coordinates, distance))
    .select('locator_records.*')
}

.notify_non_qualifying_dealersvoid

This method returns an undefined value.

Sends first deletion notices to expired confirmed dealers and emails an internal report.



467
468
469
470
471
472
473
474
475
476
477
# File 'app/models/locator_record.rb', line 467

def self.notify_non_qualifying_dealers
  customers_to_delete = Customer.locator_expired.joins(locator_record: :contact_points)
                                .where(LocatorRecord[:state].eq('confirmed'))
                                .where(ContactPoint[:category].eq('email'))
  customers_to_delete.find_each(batch_size: 100) do |c|
    c.locator_record.send_dealer_deletion_notice
  end
  return if customers_to_delete.empty?

  InternalReportsMailer.dealer_locator_communication_report(customers_to_delete.map(&:locator_record), 'Dealers Set to Delete - First notice').deliver
end

.process_task(task_name, task_condition, task_include_associations, task_method, trial_run = false, logger = nil, _stop_hours = 3, batch = 100) ⇒ void

This method returns an undefined value.

Runs a batch task over locator records matching a condition, resumable across runs.

Parameters:

  • task_name (String)

    label used in log output

  • task_condition (String)

    SQL WHERE fragment selecting the records

  • task_include_associations (Array, Hash, Symbol, nil)

    associations to eager-load

  • task_method (Symbol, String)

    instance method to invoke on each record

  • trial_run (Boolean) (defaults to: false)

    when true, records are not modified

  • logger (Logger, nil) (defaults to: nil)

    logger to write progress to

  • _stop_hours (Integer, nil) (defaults to: 3)

    deprecated time budget (unused)

  • batch (Integer) (defaults to: 100)

    batch size for find_each



650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
# File 'app/models/locator_record.rb', line 650

def self.process_task(task_name, task_condition, task_include_associations, task_method, trial_run = false, logger = nil, _stop_hours = 3, batch = 100)
  # Process a task on all locator records that meet the conditions, work for stop_hours, it will resume the next time the task is run
  logger ||= Rails.logger
  logger.info "* #{task_name} started #{Time.current}"
  logger.info '* TRIAL RUN MODE ' if trial_run
  logger.info "** Dealer Locator Records, finding all locator_records that meet criteria: #{task_condition}"
  locator_record_counter = 0
  locator_record_processed_counter = 0
  Time.current
  locator_record_ids_with_errors = []
  locator_records = LocatorRecord.where(task_condition).includes(task_include_associations).references(task_include_associations)
  locator_records.find_each(batch_size: batch) do |l|
    locator_record_counter += 1
    processed = l.send(task_method, trial_run)
    if processed[:status_code] == :ok
      locator_record_processed_counter += 1
      logger.info  "*** Locator record #{l.customer.name} #{l.id} completed, status message #{processed[:status_message]}"
    else
      logger.error "!!! Locator record #{l.customer.name} #{l.id} failed to process  with #{task_method}, status message was #{processed[:status_message]}"
      locator_record_ids_with_errors << [l.id, l.customer.try(:name), processed[:status_code], processed[:status_message]]
    end
  end
  # Send email to Elodie
  InternalReportsMailer.dealer_locator_communication_report(locator_records, task_name).deliver_later unless locator_records.empty?
  if @locator_record_ids_with_errors.present?
    ErrorReporting.error("*** #{task_name} Processed #{locator_record_processed_counter} and failed to process #{locator_record_ids_with_errors.size} records,  #{Time.current}. These are the locator record ids: #{locator_record_ids_with_errors}")
  end
  logger.info "*** #{task_name} Processed #{locator_record_processed_counter} and failed to process #{locator_record_ids_with_errors.size} records,  #{Time.current}"
  locator_record_ids_with_errors.each do |e|
    logger.error "**** Locator record error #{e.join(' ')}"
  end
end

.purge_invalid_locator_recordsvoid

This method returns an undefined value.

Destroys locator records whose customers no longer qualify (homeowner profiles).



458
459
460
461
462
463
# File 'app/models/locator_record.rb', line 458

def self.purge_invalid_locator_records
  # LocatorRecord.joins(:customer => :profile).where(Profile[:locator_eligible].eq(false)).find_each do |lr|
  LocatorRecord.joins(:customer).where(Customer[:profile_id].eq(ProfileConstants::HOMEOWNER)).find_each do |lr|
    lr.remove_dealer_from_locator(false)
  end
end

.star_level_options_for_select(nil_option = true) ⇒ Array<Array(String, Integer, nil)>

[label, value] pairs of star levels for select dropdowns.

Parameters:

  • nil_option (Boolean) (defaults to: true)

    whether to prepend a blank option

Returns:

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


160
161
162
163
164
# File 'app/models/locator_record.rb', line 160

def self.star_level_options_for_select(nil_option = true)
  base_options = []
  base_options = [[' ', nil]] if nil_option
  base_options.concat(STAR_STATUS_LEVELS.map { |rl| [rl.to_s, rl.to_i] })
end

.states_for_selectArray<Array(String, String)>

[label, value] pairs of locator states for select dropdowns.

Returns:

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


153
154
155
# File 'app/models/locator_record.rb', line 153

def self.states_for_select
  state_machines[:state].states.map { |s| [s.human_name, s.value] }
end

Instance Method Details

#addressAddress

Physical location shown on the dealer locator map.

Returns:

See Also:

Validations:



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

belongs_to :address, inverse_of: :locator_record, optional: true

#blacklistBoolean

Blacklists the dealer (marks the record inactive).

Returns:

  • (Boolean)

    whether the update succeeded



310
311
312
# File 'app/models/locator_record.rb', line 310

def blacklist
  update(is_inactive: true)
end

#capabilitiesArray<String>

Human-readable list of the dealer's supported product capabilities.

Returns:

  • (Array<String>)


142
143
144
145
146
147
148
149
# File 'app/models/locator_record.rb', line 142

def capabilities
  res = []
  res << 'Supports Floor Heating' if supports_flooring?
  res << 'Supports Snow Melting' if supports_snowmelting?
  res << 'Supports Roof & Gutter Deicing' if supports_roof_deicing?
  res << 'Supports Comfort Products' if supports_comfort_products?
  res
end

#communicationsActiveRecord::Relation<Communication>

Communications logged against this record, most recent first.

Returns:

See Also:



65
# File 'app/models/locator_record.rb', line 65

has_many :communications, -> { order(:id).reverse_order }, as: :resource, dependent: :nullify

#contact_pointsActiveRecord::Relation<ContactPoint>

Contact points (phone, email, website, fax) for the listing.

Returns:

See Also:



63
# File 'app/models/locator_record.rb', line 63

has_many :contact_points, inverse_of: :locator_record

CRM path for the linked customer.

Returns:

  • (String)


300
301
302
# File 'app/models/locator_record.rb', line 300

def crm_link
  UrlHelper.instance.customer_path(customer)
end

#customerCustomer

Customer (dealer) this locator record lists.

Returns:

See Also:



59
# File 'app/models/locator_record.rb', line 59

belongs_to :customer, inverse_of: :locator_record, optional: true

#dealer_confirmvoid

This method returns an undefined value.

Confirms the dealer listing (stamps last_confirmed_at and transitions state).



328
329
330
331
332
333
334
335
336
337
338
339
# File 'app/models/locator_record.rb', line 328

def dealer_confirm
  self.last_confirmed_at = Time.current
  if confirmed_at
    save
    dealer_confirmed! unless state == 'confirmed'
  else
    # ensure that the confirmation date and state change to confirmed is set only once on the *initial* confirmation
    self.confirmed_at = Time.current
    save
    dealer_confirmed!
  end
end

#emailString?

Email address of the listing.

Returns:

  • (String, nil)


209
210
211
212
213
# File 'app/models/locator_record.rb', line 209

def email
  contact_points.find { |cp| cp.category == 'email' }.detail
rescue StandardError
  nil
end

#email_idInteger?

Id of the email contact point.

Returns:

  • (Integer, nil)


201
202
203
204
205
# File 'app/models/locator_record.rb', line 201

def email_id
  contact_points.find { |cp| cp.category == 'email' }.id
rescue StandardError
  nil
end

#email_id=(cp_id) ⇒ void

This method returns an undefined value.

Replaces the email contact point with the given one.

Parameters:

  • cp_id (Integer, String, nil)

    contact point id; blank removes the email contact point



188
189
190
191
192
193
194
195
196
197
# File 'app/models/locator_record.rb', line 188

def email_id=(cp_id)
  current_cp = contact_points.find { |cp| cp.category == 'email' }
  contact_points.delete(current_cp) if current_cp
  return if cp_id.blank?

  cp = ContactPoint.find(cp_id)
  contact_points << cp
  # don't want duplicates say validation issue to block save
  # cp.save(:validate => false)
end

#faxString?

Fax number of the listing.

Returns:

  • (String, nil)


240
241
242
243
244
# File 'app/models/locator_record.rb', line 240

def fax
  contact_points.find { |cp| cp.category == 'fax' }.detail
rescue StandardError
  nil
end

#fax_idInteger?

Id of the fax contact point.

Returns:

  • (Integer, nil)


232
233
234
235
236
# File 'app/models/locator_record.rb', line 232

def fax_id
  contact_points.find { |cp| cp.category == 'fax' }.id
rescue StandardError
  nil
end

#fax_id=(cp_id) ⇒ void

This method returns an undefined value.

Replaces the fax contact point with the given one.

Parameters:

  • cp_id (Integer, String, nil)

    contact point id; blank removes the fax contact point



218
219
220
221
222
223
224
225
226
227
228
# File 'app/models/locator_record.rb', line 218

def fax_id=(cp_id)
  current_cp = contact_points.find { |cp| cp.category == 'fax' }
  contact_points.delete(current_cp) if current_cp
  contact_points << ContactPoint.find(cp_id) if cp_id.present?
  return if cp_id.blank?

  cp = ContactPoint.find(cp_id)
  contact_points << cp
  # don't want duplicates (say) validation issue to block save
  # cp.save(:validate => false)
end

#first_nameObject

Alias for Customer#first_name

Returns:

  • (Object)

    Customer#first_name

See Also:



138
# File 'app/models/locator_record.rb', line 138

delegate :first_name, to: :customer

#generate_confirmation_form_urlObject

On Aprl 25th 2022 we decided to stop sending email notifications for dealers



480
481
482
# File 'app/models/locator_record.rb', line 480

def generate_confirmation_form_url
  LOCATOR_RECORD_FORM_BASE_URL + "?locator_record_id=#{CGI.escape(Encryption.encrypt_string(id.to_s))}"
end

#get_callable_cpContactPoint?

First voice-callable contact point.

Returns:



170
171
172
173
174
# File 'app/models/locator_record.rb', line 170

def get_callable_cp
  contact_points.voice_callable.first
rescue StandardError
  nil
end

#get_cp_by_category(cat) ⇒ String?

Detail (value) of the first contact point in the given category.

Parameters:

  • cat (String)

    contact point category (e.g. 'email', 'fax')

Returns:

  • (String, nil)


179
180
181
182
183
# File 'app/models/locator_record.rb', line 179

def get_cp_by_category(cat)
  contact_points.find { |cp| cp.category == cat }.detail
rescue StandardError
  nil
end

HTML link to Google Maps for this listing's address.

Parameters:

  • label (String, nil)

    link text; defaults to 'View On Map'

Returns:

  • (String)

    HTML anchor tag



612
613
614
# File 'app/models/locator_record.rb', line 612

def map_link(label)
  "<a target = '_new' href='http://maps.google.com/maps?f=q&amp;source=embed&amp;hl=en&amp;geocode=&amp;q=#{address.street1}+#{address.street2}+#{address.street3}+#{address.city}+#{address.state_code}+#{address.zip}+#{address.country_iso3}&amp;sll=#{address.lat},#{address.lng}&amp;sspn=0.008969,0.021522&amp;ie=UTF8&amp;hq=&amp;hnear=#{address.street1}+#{address.street2}+#{address.street3}+#{address.city}+#{address.state_code}+#{address.zip}+#{address.country_iso3}&amp;z=16&amp;ll=#{address.lat},#{address.lng}'>#{label || 'View On Map'}</a>"
end

#must_be_an_organizationvoid (protected)

This method returns an undefined value.

Validates the customer is an organization, not a homeowner.



628
629
630
631
632
# File 'app/models/locator_record.rb', line 628

def must_be_an_organization
  return unless customer&.is_homeowner?

  errors.add :base, "Homeowner can't be in dealer locator dealer must be an organization"
end

#nameObject

Alias for Customer#name

Returns:

  • (Object)

    Customer#name

See Also:



166
# File 'app/models/locator_record.rb', line 166

delegate :name, to: :customer

#not_blacklistedvoid (protected)

This method returns an undefined value.

Validates a blacklisted customer cannot be added to the locator.



620
621
622
623
624
# File 'app/models/locator_record.rb', line 620

def not_blacklisted
  return unless customer&.locator_black_listing && new_record?

  errors.add :base, "Dealer can't be in locator dealer is blacklisted"
end

#phoneString?

Phone number of the listing.

Returns:

  • (String, nil)


270
271
272
# File 'app/models/locator_record.rb', line 270

def phone
  contact_points.find { |cp| ContactPoint::CAN_VOICE.include? cp.category }&.detail
end

#phone_idInteger?

Id of the primary voice-callable contact point.

Returns:

  • (Integer, nil)


262
263
264
265
266
# File 'app/models/locator_record.rb', line 262

def phone_id
  contact_points.voice_callable.first.id
rescue StandardError
  nil
end

#phone_id=(cp_id) ⇒ void

This method returns an undefined value.

Replaces the phone contact point with the given one.

Parameters:

  • cp_id (Integer, String, nil)

    contact point id; blank removes the phone contact point



249
250
251
252
253
254
255
256
257
258
# File 'app/models/locator_record.rb', line 249

def phone_id=(cp_id)
  current_cp = contact_points.find { |cp| %w[phone cell].include?(cp.category) }
  contact_points.delete(current_cp) if current_cp
  return if cp_id.blank?

  cp = ContactPoint.find(cp_id)
  contact_points << cp
  # don't want duplicates (say) validation issue to block save
  # cp.save(:validate => false)
end

#profilesActiveRecord::Relation<Profile>

Customer profiles attached to this record via profile_ids.

Returns:

  • (ActiveRecord::Relation<Profile>)


322
323
324
# File 'app/models/locator_record.rb', line 322

def profiles
  Profile.where(id: profile_ids)
end

#remove_dealer(trial_run = false) ⇒ Hash

Sends the dealer-deleted notice and destroys the record.

Parameters:

  • trial_run (Boolean) (defaults to: false)

    when true, sends nothing and skips the destroy

Returns:

  • (Hash)

    { status_code:, status_message: } result



394
395
396
397
398
399
400
401
402
403
404
# File 'app/models/locator_record.rb', line 394

def remove_dealer(trial_run = false)
  stat = { status_code: :ok, status_message: "Dealer deleted, locator record id #{id}" }
  co = CommunicationBuilder.new(resource: self, template_system_code: 'DEALER_DELETED').create
  if co.errors.any?
    stat[:status_code] = :error
    stat[:status_message] = "There were problem deleting a dealer to locator record #{id} : #{co.errors.full_messages}"
  else
    destroy unless trial_run
  end
  stat
end

#remove_dealer_from_locator(trial_run = false) ⇒ Hash

Removes the dealer from the locator unless the customer is whitelisted.

Parameters:

  • trial_run (Boolean) (defaults to: false)

    when true, skips the destroy

Returns:

  • (Hash)

    { status_code:, status_message: } result



448
449
450
451
452
453
454
# File 'app/models/locator_record.rb', line 448

def remove_dealer_from_locator(trial_run = false)
  stat = { status_code: :ok, status_message: 'Dealer is white listed, removal ignored' }
  unless customer.locator_white_listing
    destroy unless trial_run
  end
  stat
end

#schedule_dealer_locator_activity(task_type) ⇒ void

This method returns an undefined value.

Auto-creates a CRM follow-up activity when a dealer confirms and requests materials.

Parameters:

  • task_type (String)

    activity task type (e.g. 'DISPINF', 'BINDER')



344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'app/models/locator_record.rb', line 344

def schedule_dealer_locator_activity(task_type)
  activity_type_id = begin
    ActivityType.find_by(task_type: task_type).id
  rescue StandardError
    8
  end
  activity = Activity.new
  activity.activity_type_id = activity_type_id
  activity.description = activity.activity_type.description + ", DL invite sent ~ #{created_at.to_fs(:crm_date_only)}"
  activity.new_note = "This #{task_type} activity was auto-generated by the customer confirming their dealer/installer record on #{Time.current.to_fs(:crm_date_only)}, and requesting marketing materials or displays."
  activity.party = customer
  activity.target_datetime = 1.day.from_now
  activity.assigned_resource = customer.primary_sales_rep
  activity.save(validate: false)
end

#send_confirmation_emailvoid

This method returns an undefined value.

Sends the dealer-confirmed email; reports errors to ErrorReporting.



438
439
440
441
442
443
# File 'app/models/locator_record.rb', line 438

def send_confirmation_email
  co = CommunicationBuilder.new(resource: self, template_system_code: 'DEALER_CONFIRMED').create
  return unless co.errors.any?

  ErrorReporting.error("There were problem sending confirmation email to locator record #{id} : #{co.errors.full_messages}")
end

#send_dealer_deletion_notice(trial_run = false) ⇒ Hash

Sends the dealer a deletion notice and transitions the state machine.

Parameters:

  • trial_run (Boolean) (defaults to: false)

    when true, sends nothing and skips the state transition

Returns:

  • (Hash)

    { status_code:, status_message: } result



363
364
365
366
367
368
369
370
371
372
373
374
# File 'app/models/locator_record.rb', line 363

def send_dealer_deletion_notice(trial_run = false)
  stat = { status_code: :ok, status_message: "Sent notice to dealer for deletion, locator record id #{id}" }
  co = CommunicationBuilder.new(resource: self, template_system_code: 'DEALER_SET_TO_DELETE').create
  if co.errors.any?
    stat[:status_code] = :error
    stat[:status_message] = "There were problem sending notice to dealer for deletion to locator record #{id} : #{co.errors.full_messages}"
    ErrorReporting.error("There were problem sending notice to dealer for deletion to locator record #{id} : #{co.errors.full_messages}")
  elsif !trial_run && state == 'confirmed'
    dealer_deletion_noticed!
  end
  stat
end

#send_dealer_deletion_notice_7_day_reminder(trial_run = false) ⇒ Hash

Sends the 7-day deletion reminder and transitions the state machine.

Parameters:

  • trial_run (Boolean) (defaults to: false)

    when true, sends nothing and skips the state transition

Returns:

  • (Hash)

    { status_code:, status_message: } result



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

def send_dealer_deletion_notice_7_day_reminder(trial_run = false)
  stat = { status_code: :ok, status_message: "Sent remainder to dealer for deletion, locator record id #{id}" }
  co = CommunicationBuilder.new(resource: self, template_system_code: 'DEALER_DELETE_IN7DAY').create
  if co.errors.any?
    stat[:status_code] = :error
    stat[:status_message] = "There were problem reminding a dealer locator record #{id} : #{co.errors.full_messages}"
  else
    dealer_deletion_remainded! unless trial_run
  end
  stat
end

#send_dealer_invite(trial_run = false) ⇒ Hash

Sends the dealer invite and transitions to the invited state.

Parameters:

  • trial_run (Boolean) (defaults to: false)

    when true, sends nothing and skips the state transition

Returns:

  • (Hash)

    { status_code:, status_message: } result



409
410
411
412
413
414
415
416
417
418
419
# File 'app/models/locator_record.rb', line 409

def send_dealer_invite(trial_run = false)
  stat = { status_code: :ok, status_message: "Dealer invited, locator record id #{id}" }
  co = CommunicationBuilder.new(resource: self, template_system_code: 'DEALER_INVITE').create
  if co.errors.any?
    stat[:status_code] = :error
    stat[:status_message] = "There were problem sending dealer invite to locator record #{id} : #{co.errors.full_messages}"
  elsif not_invited? && !trial_run
    dealer_invited!
  end
  stat
end

#send_invited_dealers_30_day_reminder(trial_run = false) ⇒ Hash

Sends the 30-day invitation reminder and transitions the state machine.

Parameters:

  • trial_run (Boolean) (defaults to: false)

    when true, sends nothing and skips the state transition

Returns:

  • (Hash)

    { status_code:, status_message: } result



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

def send_invited_dealers_30_day_reminder(trial_run = false)
  stat = { status_code: :ok, status_message: "Dealer 30 day invitation reminder sent, locator record id #{id}" }
  co = CommunicationBuilder.new(resource: self, template_system_code: 'DEALER_REMINDER').create
  if co.errors.any?
    stat[:status_code] = :error
    stat[:status_message] = "There were problem sending dealer invite 30 days reminders to locator record #{id} : #{co.errors.full_messages}"
  else
    invite_reminded! unless trial_run
  end
  stat
end

#slug_candidatesString (protected)

FriendlyId slug candidates — the customer name.

Returns:

  • (String)


636
637
638
# File 'app/models/locator_record.rb', line 636

def slug_candidates
  customer.name
end

#unblacklistBoolean

Removes the blacklist (marks the record active).

Returns:

  • (Boolean)

    whether the update succeeded



316
317
318
# File 'app/models/locator_record.rb', line 316

def unblacklist
  update(is_inactive: false)
end

#update_from_user_params(params) ⇒ String

Applies dealer-submitted form params: updates customer/contact points/address,
confirms the dealer, and schedules display-material activities.

Parameters:

  • params (Hash)

    form params from the dealer confirmation form

Returns:

  • (String)

    'ok' on success, otherwise the joined error messages



488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
# File 'app/models/locator_record.rb', line 488

def update_from_user_params(params)
  customer = self.customer
  # do tasks like
  # - create DISPINF, DISPHCTD, DISPTW or BINDER
  # - update customer name if valid, and sane
  # - create new contact points in customer and link to locator record if necessary based on comparison to existing cp details, scanning all other customer and contacts contact points for detail and type matching
  # - create new address in customer and link to locator record if necessary based on comparison to existing addresses
  update(params[:locator_record]) if params[:locator_record]
  ###################################################################################
  # check to see if the dealer name should be updated
  new_dealer_name = params[:dealer_name].to_s.strip
  # only do if it's not blank and the customer name doesn't already contain it (strip both first)
  unless new_dealer_name.blank? || customer.name.strip.index(new_dealer_name)
    # ok, now see if it's at least 3 characters long and contains some alphabetical character
    if new_dealer_name.length >= 3 && new_dealer_name =~ /([A-Z]|[a-z])/
      customer.name = new_dealer_name
      customer.save
    end
  end
  ###################################################################################
  # check to see if the dealer contact points should be updated
  cp_types = %w[email phone fax website]
  new_dealer_cps = []
  cp_types.each do |cp_type|
    # get contact point detail from params (remove any 'http://' in the detail)
    new_dealer_cp_detail = params["dealer_#{cp_type}"].to_s.strip.gsub('http://', '').downcase
    # logger.warn "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!confirm_by_email_link: Checking!!! #{cp_type}: #{new_dealer_cp_detail}"
    new_dealer_cp = ContactPoint.new(detail: new_dealer_cp_detail, category: cp_type)
    # logger.warn "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!confirm_by_email_link: new_dealer_cp: #{new_dealer_cp.inspect}"
    # only add to the new contact point validation list if it's not blank
    new_dealer_cps << new_dealer_cp if new_dealer_cp_detail.present?
    # test if new cp is valid, allow blank since that will force blank out that contact point
    next unless new_dealer_cp.errors.empty? || new_dealer_cp_detail.blank?

    # logger.warn "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!confirm_by_email_link: TRUE: new_dealer_cp.errors.empty? || new_dealer_cp_detail.blank?"
    # handle callable vs the other types
    lr_cp = if cp_type == 'phone'
              get_callable_cp
            else
              get_cp_by_category(cp_type)
            end
    # test if it has the same detail
    next if (begin
      lr_cp.detail
    rescue StandardError
      nil
    end).to_s.strip.downcase == new_dealer_cp_detail

    # logger.warn "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!confirm_by_email_link: FALSE: ((lr_cp.detail rescue nil).to_s.strip.downcase == new_dealer_cp_detail)"
    # not the same, remove this one, if it's blank we assume it is a delete request
    contact_points.delete(lr_cp) if lr_cp.present?
    next if new_dealer_cp_detail.blank?

    # logger.warn "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!confirm_by_email_link: FALSE: new_dealer_cp_detail.blank?"
    # if it's not blank and different, scan each of the customer/contacts' contact points of type cp_type to see if it's one of those
    # handle callable vs the other types
    new_cp = if cp_type == 'phone'
               customer.contacts_and_self_callable_contact_points.find { |cp| cp.detail.to_s.strip.downcase == new_dealer_cp_detail }
             else
               customer.contacts_and_self_contact_points_by_category(cp_type).find { |cp| cp.detail.to_s.strip.downcase == new_dealer_cp_detail }
             end
    # logger.warn "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!confirm_by_email_link: new_cp: type: #{new_cp.category rescue nil}, detail: #{new_cp.detail rescue nil}"
    new_cp ||= customer.contact_points.create(detail: new_dealer_cp_detail, category: cp_type)
    # add the contact point to the locator record
    contact_points << new_cp
  end
  ###################################################################################
  # check that address needs to be updated
  # check that address is valid
  new_dealer_address = Address.new(params[:dealer_address])
  # puts "params[:dealer_address]: #{params[:dealer_address]}"
  new_dealer_address.party_id = customer.id
  valid_address = new_dealer_address.geocode_me
  if valid_address && new_dealer_address.valid?
    # puts "valid_address: #{valid_address}"
    #  and isn't the same as the existing
    unless address.same_as(new_dealer_address)
      # puts "not the same as the existing address"
      # see if it's in the customer already
      dealer_address = customer.all_related_addresses.find { |a| a.same_as(new_dealer_address) }
      unless dealer_address
        # puts "not the same as any existing address in customer"
        # nope so see if it's really close geocode wise to one of the existing, use 1000 ft since there's a big variation in these geocodings
        dealer_address = customer.all_related_addresses.find { |a| new_dealer_address.distance_to(a) < 0.189 }
        unless dealer_address
          # puts "not real close to any existing address in customer"
          # nope so create it in the customer
          new_dealer_address.save
          # puts "new_dealer_address.errors: #{new_dealer_address.errors_to_s}"
          # puts "saving new: new_dealer_address.id: #{new_dealer_address.id rescue 'nil'}"
          dealer_address = new_dealer_address
          customer.addresses << dealer_address
        end
      end
      # puts "new_dealer_address.id: #{new_dealer_address.id rescue 'nil'}"
      self.address = dealer_address
      save
    end
  end
  if errors.empty? && new_dealer_address.errors.empty? && new_dealer_cps.all? { |cp| cp.errors.empty? }
    dealer_confirm
    res = 'ok'
    ###################################################################################
    # check to see if the dealer ordered display materials
    schedule_dealer_locator_activity('DISPINF') if params[:order_infloor_display] == '1'
    schedule_dealer_locator_activity('DISPHCTD') if params[:order_countertop_display] == '1'
    schedule_dealer_locator_activity('BINDER') if params[:order_marketing_materials] == '1'
  else
    all_error_messages = []
    all_error_messages.concat(new_dealer_address.errors.full_messages.dup)
    new_dealer_cps.each do |cp|
      # logger.warn "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!confirm_by_email_link: cp errors: #{cp.errors.full_messages.dup}, detail: #{cp.category rescue 'nil'}, detail: #{cp.detail rescue 'nil'}" unless cp.errors.full_messages.empty?
      all_error_messages.concat(cp.errors.full_messages.dup)
    end
    all_error_messages.concat(errors.full_messages.dup)
    # logger.warn "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!confirm_by_email_link: lr errors: #{self.errors.full_messages.dup}" unless self.errors.full_messages.empty?
    res = all_error_messages.join(' and ').to_s
  end
  res
end

#websiteString?

Website URL of the listing.

Returns:

  • (String, nil)


294
295
296
# File 'app/models/locator_record.rb', line 294

def website
  contact_points.by_category('website').first&.detail
end

#website_idInteger?

Id of the website contact point.

Returns:

  • (Integer, nil)


288
289
290
# File 'app/models/locator_record.rb', line 288

def website_id
  contact_points.by_category('website').first&.id
end

#website_id=(cp_id) ⇒ void

This method returns an undefined value.

Replaces the website contact point with the given one.

Parameters:

  • cp_id (Integer, String, nil)

    contact point id; blank removes the website contact point



277
278
279
280
281
282
283
284
# File 'app/models/locator_record.rb', line 277

def website_id=(cp_id)
  current_cp = contact_points.find { |cp| cp.category == 'website' }
  contact_points.delete(current_cp) if current_cp
  return if cp_id.blank?

  cp = ContactPoint.find(cp_id)
  contact_points << cp
end

#whitelistvoid

This method returns an undefined value.

No-op legacy hook kept for callers that toggle listing visibility.



306
# File 'app/models/locator_record.rb', line 306

def whitelist; end