Class: Address

Inherits:
ApplicationRecord show all
Includes:
Comparable, Models::Auditable, Models::LiquidMethods, PgSearch::Model
Defined in:
app/models/address.rb

Overview

== Schema Information

Table name: addresses
Database name: primary

id :integer not null, primary key
address_text :text
address_text_tsvector :tsvector
address_type :string(255)
city :string(255)
company_name_override :string(255)
country_iso3 :string(3)
county_code :string(255)
county_name :string(255)
description :string(255)
disable_address_correction :boolean
has_loading_dock :boolean
is_construction_site :boolean
is_placeholder :boolean default(FALSE), not null
is_residential :boolean default(TRUE), not null
is_store_mailing_address :boolean default(FALSE)
is_trade_show :boolean
is_warehouse :boolean default(FALSE)
jde_number :integer
lat :float
limited_access :boolean
lng :float
normalized_address :string
override_all_address_validation :boolean
person_name_override :string(255)
recipients_tsv :tsvector
region :string
require_signature_by_default :boolean default(FALSE), not null
requires_appointment :boolean
requires_inside_delivery :boolean
requires_liftgate :boolean
series_geocoded :boolean
service_area_mile_radius :integer
smartfit_available_updated_at :datetime default(2012-03-28 20:35:14.000000000 CDT -05:00), not null
state_code :string(20)
street1 :string(255)
street2 :string(255)
street3 :string(35)
supported_carriers :string(255) is an Array
supports_third_party_shipping :boolean
timezone_name :string
verified :boolean default(FALSE)
verified_for_shipping :boolean
zip :string(255)
created_at :datetime
updated_at :datetime
creator_id :integer
party_id :integer
updater_id :integer

Indexes

addresses_jde_number_index (jde_number)
addresses_party_id_index (party_id)
index_addresses_on_address_text_tsvector (address_text_tsvector) USING gin
index_addresses_on_country_iso3 (country_iso3)
index_addresses_on_county_code (county_code)
index_addresses_on_county_name (county_name)
index_addresses_on_lat_and_lng (lat,lng)
index_addresses_on_recipients_tsv (recipients_tsv) USING gin
index_addresses_on_state_code (state_code)
index_addresses_street1_like (street1)
index_addresses_zip_like (zip)
tsearch_city_idx (to_tsvector('english'::regconfig, (city)::text)) USING gin
tsearch_zip_idx (to_tsvector('english'::regconfig, (zip)::text)) USING gin

Foreign Keys

addresses_party_id_fk (party_id => parties.id) ON DELETE => nullify
fk_addresses_country_iso3 (country_iso3 => countries.iso3)

Defined Under Namespace

Classes: Creator, LineAbbreviator, NormalizedAddress, RecalculateShipping, Updater

Constant Summary collapse

PO_BOX_REGEXP =

Po box regexp.

/[p|P]\s*[o|O]\s*[b|B]\s*[o|O]\s*[x|X]\s*[a-zA-Z0-9]*|\b[P|p]+(OST|ost|o|O)?\.?\s*[O|o0]+(ffice|FFICE)?\.?\s*[B|b][O|o0]?[X|x]+\.?\s+\#?(\d+)*(\D+)*\b|[c|C]\s*[p|P]\s*[0-9]/
MAX_STREET_FIELD_LENGTH =

Maximum street field length.

35
ADDRESS_FIELDS_FOR_GEOCODING =

Address fields for geocoding.

%w[street1 street2 street3 city state_code country_iso3 zip].freeze
ADDRESS_FIELDS_TO_RECALCULATE_SHIPPING_FOR =

Address fields to recalculate shipping for.

(ADDRESS_FIELDS_FOR_GEOCODING + %w[is_residential require_signature_by_default]).freeze
FREIGHT_FIELDS =

Recognised freight fields.

%w[has_loading_dock requires_liftgate requires_inside_delivery is_construction_site limited_access
requires_appointment].freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has one collapse

Has 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

#admin_modeObject

Returns the value of attribute admin_mode.



99
100
101
# File 'app/models/address.rb', line 99

def admin_mode
  @admin_mode
end

#autocorrect_messageObject

Returns the value of attribute autocorrect_message.



99
100
101
# File 'app/models/address.rb', line 99

def autocorrect_message
  @autocorrect_message
end

#check_for_duplicatesObject

Returns the value of attribute check_for_duplicates.



99
100
101
# File 'app/models/address.rb', line 99

def check_for_duplicates
  @check_for_duplicates
end

#cityObject (readonly)

Validates street1, city, country iso3.

Validations:



155
# File 'app/models/address.rb', line 155

validates :street1, :city, :country_iso3, presence: true

#country_iso3Object (readonly)

Validates street1, city, country iso3.

Validations:



155
# File 'app/models/address.rb', line 155

validates :street1, :city, :country_iso3, presence: true

#do_not_accept_legacy_verifiedObject

Returns the value of attribute do_not_accept_legacy_verified.



99
100
101
# File 'app/models/address.rb', line 99

def do_not_accept_legacy_verified
  @do_not_accept_legacy_verified
end

#require_carrier_validationObject

Returns the value of attribute require_carrier_validation.



99
100
101
# File 'app/models/address.rb', line 99

def require_carrier_validation
  @require_carrier_validation
end

#set_default_billing_addressObject

Returns the value of attribute set_default_billing_address.



99
100
101
# File 'app/models/address.rb', line 99

def set_default_billing_address
  @set_default_billing_address
end

#set_default_mailing_addressObject

Returns the value of attribute set_default_mailing_address.



99
100
101
# File 'app/models/address.rb', line 99

def set_default_mailing_address
  @set_default_mailing_address
end

#set_default_shipping_addressObject

Returns the value of attribute set_default_shipping_address.



99
100
101
# File 'app/models/address.rb', line 99

def set_default_shipping_address
  @set_default_shipping_address
end

#skip_carrier_validationObject

Returns the value of attribute skip_carrier_validation.



99
100
101
# File 'app/models/address.rb', line 99

def skip_carrier_validation
  @skip_carrier_validation
end

#state_codeObject (readonly)

Validates state code, requires states or provinces.

Validations:

  • Presence ({ if: :requires_states_or_provinces? })
  • Length ({ maximum: 20 })
  • Allow_blank


157
# File 'app/models/address.rb', line 157

validates :state_code, presence: { if: :requires_states_or_provinces? }

#street1Object (readonly)

Validates street1, city, country iso3.

Validations:

  • Presence
  • Length ({ maximum: MAX_STREET_FIELD_LENGTH, too_long: "is too long (maximum is #{MAX_STREET_FIELD_LENGTH} characters, you may need to DISABLE address correction)" })


155
# File 'app/models/address.rb', line 155

validates :street1, :city, :country_iso3, presence: true

#street2Object (readonly)

Validates street1, street2, street3.

Validations:

  • Length ({ maximum: MAX_STREET_FIELD_LENGTH, too_long: "is too long (maximum is #{MAX_STREET_FIELD_LENGTH} characters, you may need to DISABLE address correction)" })


162
163
# File 'app/models/address.rb', line 162

validates :street1, :street2, :street3, length: { maximum: MAX_STREET_FIELD_LENGTH,
too_long: "is too long (maximum is #{MAX_STREET_FIELD_LENGTH} characters, you may need to DISABLE address correction)" }

#street3Object (readonly)

Validates street1, street2, street3.

Validations:

  • Length ({ maximum: MAX_STREET_FIELD_LENGTH, too_long: "is too long (maximum is #{MAX_STREET_FIELD_LENGTH} characters, you may need to DISABLE address correction)" })


162
163
# File 'app/models/address.rb', line 162

validates :street1, :street2, :street3, length: { maximum: MAX_STREET_FIELD_LENGTH,
too_long: "is too long (maximum is #{MAX_STREET_FIELD_LENGTH} characters, you may need to DISABLE address correction)" }

#suggested_addressesObject

Returns the value of attribute suggested_addresses.



99
100
101
# File 'app/models/address.rb', line 99

def suggested_addresses
  @suggested_addresses
end

#validate_with_carrierObject

Returns the value of attribute validate_with_carrier.



99
100
101
# File 'app/models/address.rb', line 99

def validate_with_carrier
  @validate_with_carrier
end

Class Method Details

.address_type_optionsObject

Address type options.



283
284
285
286
287
288
# File 'app/models/address.rb', line 283

def self.address_type_options
  [['Residential', 'residential'],
   ['Commercial Building with Receiving Dock', 'commercial_building_with_receiving_dock'],
   ['Commercial Building without Receiving Dock', 'commercial_building_without_receiving_dock'],
   ['Construction Site/Limited Access', 'construction_site']]
end

.canadaActiveRecord::Relation<Address>

A relation of Addresses that are canada. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Address>)

See Also:



187
# File 'app/models/address.rb', line 187

scope :canada, -> { where(country_iso3: 'CAN') }

.clear_geocoder_cacheObject

Clear geocoder cache.



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

def self.clear_geocoder_cache
  # Queue all our locator address for geocoding
  Geocoder::Lookup.get(:google).cache.expire(:all)
end

.geocode_address_string(address_string) ⇒ Object

Geocode address string.

Parameters:

  • address_string (Object)

    the address string



922
923
924
925
926
927
928
929
# File 'app/models/address.rb', line 922

def self.geocode_address_string(address_string)
  # aargh deal with &&^%^&% new Geocoder issue where it return no results when space is skipped in Canadian postal codes
  address_string = "#{address_string[0..2]} #{address_string[3..5]}" if address_string && (address_string.length == 6)
  results = Geocoder.search(address_string)
  return unless (geo = results.first)

  [geo.latitude, geo.longitude]
end

.is_warehouseActiveRecord::Relation<Address>

A relation of Addresses that are is warehouse. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Address>)

See Also:



185
# File 'app/models/address.rb', line 185

scope :is_warehouse, -> { where(is_warehouse: true) }

.linked_to_party_or_orderActiveRecord::Relation<Address>

A relation of Addresses that are linked to party or order. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Address>)

See Also:



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

scope :linked_to_party_or_order, -> {
  where(%{exists(select 1 from parties
                          where addresses.party_id is not null
                            and parties.id = addresses.party_id
                            and parties.type IN ('Customer','Contact'))
                   OR
                   exists(select 1 from orders
                          where orders.shipping_address_id = addresses.id) })
}

.placeholder_address(country = 'USA') ⇒ Object

Placeholder address.

Parameters:

  • country (Object) (defaults to: 'USA')

    the country



273
274
275
# File 'app/models/address.rb', line 273

def self.placeholder_address(country = 'USA')
  Address.find_by(is_placeholder: true, country_iso3: country)
end

.populate_regionObject

Populates the region field based on the country and state code.



1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
# File 'app/models/address.rb', line 1388

def self.populate_region
  Address.transaction do
    %w[US CA].each do |iso|
      ic = ISO3166::Country[iso]
      ic.subdivisions.each do |state_code, sub|
        n = sub.name
        Address.where(country_iso3: ic.alpha3, state_code: state_code, region: nil).update_all(region: n)
      end
    end
    # Now for the stragglers
    Address.where(region: nil).where.not(country_iso3: nil).where.not(state_code: nil).find_each do |a|
      next unless (ic = a.iso3166_country)
      next unless (sub = ic.find_subdivision_by_name(a.state_code))

      a.update_columns(region: sub.name, state_code: sub.code)
    end
  end
end

.usaActiveRecord::Relation<Address>

A relation of Addresses that are usa. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Address>)

See Also:



186
# File 'app/models/address.rb', line 186

scope :usa, -> { where(country_iso3: 'USA') }

.warehouse_addressesActiveRecord::Relation<Address>

A relation of Addresses that are warehouse addresses. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Address>)

See Also:



184
# File 'app/models/address.rb', line 184

scope :warehouse_addresses, -> { where(is_warehouse: true) }

Instance Method Details

#==(other) ⇒ Object

==.

Parameters:

  • other (Object)

    the other



416
417
418
419
420
# File 'app/models/address.rb', line 416

def ==(other)
  return false unless other.is_a?(Address)

  same_as(other)
end

#address_dropdown_formatObject

Address dropdown format.



546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
# File 'app/models/address.rb', line 546

def address_dropdown_format
  address = []
  address << street1.presence
  address << street2.presence
  address << street3.presence
  address << city.presence
  address << "#{state_code} #{zip}"
  address << country_iso3
  address = address.uniq.compact

  if Party.where(mailing_address_id: id).present?
    { id: id, text: "#{address.join(', ')} ---> (MAILING)" }
  elsif Party.where(billing_address_id: id).present?
    { id: id, text: "#{address.join(', ')} ---> (BILLING)" }
  elsif Party.where(shipping_address_id: id).present?
    { id: id, text: "#{address.join(', ')} ---> (SHIPPING)" }
  else
    { id: id, text: "#{address.join(', ')} ---> (OTHER)" }
  end
end

#address_element_changed?Boolean

Returns whether the record address element changed.

Returns:

  • (Boolean)

    whether the record address element changed



1148
1149
1150
# File 'app/models/address.rb', line 1148

def address_element_changed?
  (ADDRESS_FIELDS_TO_RECALCULATE_SHIPPING_FOR + FREIGHT_FIELDS).any? { |f| changes[f] || saved_changes[f] }
end

#address_element_changed_for_geocoding?Boolean

Returns whether the record address element changed for geocoding.

Returns:

  • (Boolean)

    whether the record address element changed for geocoding



1153
1154
1155
# File 'app/models/address.rb', line 1153

def address_element_changed_for_geocoding?
  ADDRESS_FIELDS_FOR_GEOCODING.any? { |f| changes[f] || saved_changes[f] }
end

#address_for_comparisonObject

Address for comparison.



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

def address_for_comparison
  # here we can sometimes get Null, NULL, null, etc. from some services for an address field, also replace common abbreviations with a standard set
  to_array(upcase: true, include_recipient: :overrides)
end

#address_for_geocoderObject

Address for geocoder.



568
569
570
571
572
# File 'app/models/address.rb', line 568

def address_for_geocoder
  addr = [street1, street2, city, state&.name || state_code, zip, country&.name]
  addr = addr.map(&:to_s).map(&:squish).filter_map(&:presence)
  addr.join(',')
end

#archiveable?Boolean

Returns whether the record archiveable.

Returns:

  • (Boolean)

    whether the record archiveable



1203
1204
1205
# File 'app/models/address.rb', line 1203

def archiveable?
  billing_customer.nil? && shipping_customer.nil? && mailing_customer.nil?
end

#belongs_to?(customer) ⇒ Boolean

INSTANCE METHODS

Parameters:

Returns:

  • (Boolean)


333
334
335
336
# File 'app/models/address.rb', line 333

def belongs_to?(customer)
  (party_id == customer.id) ||
    shipping_orders.find { |o| o.customer_id == customer.id }
end

#billing_credit_memosActiveRecord::Relation<CreditMemo>

Billing credit memos.

Returns:

See Also:



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

has_many   :billing_credit_memos, class_name: 'CreditMemo', foreign_key: :billing_address_id

#billing_customerCustomer

Billing customer.

Returns:

See Also:



114
# File 'app/models/address.rb', line 114

has_one    :billing_customer, class_name: 'Customer', dependent: :nullify, foreign_key: :billing_address_id, inverse_of: :billing_address

#billing_invoicesActiveRecord::Relation<Invoice>

Billing invoices.

Returns:

  • (ActiveRecord::Relation<Invoice>)

See Also:



130
# File 'app/models/address.rb', line 130

has_many   :billing_invoices, class_name: 'Invoice', foreign_key: :billing_address_id

#billing_ordersActiveRecord::Relation<Order>

Billing orders.

Returns:

  • (ActiveRecord::Relation<Order>)

See Also:



126
# File 'app/models/address.rb', line 126

has_many   :billing_orders, class_name: 'Order', foreign_key: :billing_address_id

#carrier_validation(autosave = false) ⇒ Object

Carrier validation.

Parameters:

  • autosave (Object) (defaults to: false)

    the autosave



1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
# File 'app/models/address.rb', line 1112

def carrier_validation(autosave = false)
  # puts "!!!!!!!!!!!!!carrier_validation"
  return false, errors_to_s, [] if errors.any?
  if is_warehouse || override_all_address_validation
    return true, (is_warehouse ? 'Address is warehouse' : 'Address is marked override all validation'), []
  end

  require 'wy_shipping'
  result = WyShipping.valid_address?(self)
  logger.info "External Validation with Carrier result #{result[:valid_address]}:#{result[:status_message]}."
  ErrorReporting.error("Carrier validation params missing. 'valid_address' not present. Result params: #{result}") if result[:valid_address].nil?
  res = result[:valid_address]
  if res == true && is_residential == true && # be conservative here and tend to residential unless all suggested addressed say address_residential_indicator 'no'
     result[:suggested_address_hashes].all? do |ah|
       ah['address_residential_indicator'] == 'no'
     end
    res = true
    self.is_residential = false
    self.autocorrect_message = 'Address was AUTOCORRECTED, from residential to commercial.'
    logger.info 'Adjusting address marking it residential false because carrier says so.'
  elsif res == true && is_residential == false && # be conservative here and tend to residential if any suggested addressed say address_residential_indicator 'yes'
        result[:suggested_address_hashes].any? do |ah|
          ah['address_residential_indicator'] == 'yes'
        end
    res = true
    self.is_residential = true
    self.autocorrect_message = 'Address was AUTOCORRECTED, from commercial to residential.'
    logger.info "External Validation with Carrier result #{result[:valid_address]}:#{result[:status_message]}."
    logger.info 'Adjusting address marking it residential true because carrier says so.'
  end
  self.verified_for_shipping = res
  save if verified_for_shipping && autosave
  [res, result[:status_message], result[:suggested_address_hashes] || []]
end

#check_for_location_changeObject

Check for location change.



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

def check_for_location_change
  return unless party && lat && lng && (saved_change_to_lat? || saved_change_to_lng?)

  Rails.configuration.event_store.publish(
    Events::LocationChanged.new(data: {
      party_id: party.id,
      latitude: lat,
      longitude: lng
    }),
    stream_name: "Party-#{party.id}"
  )
end

#company_nameObject

Company name.



1192
1193
1194
# File 'app/models/address.rb', line 1192

def company_name
  company_name_override.presence || inherited_company_name
end

#company_name=(value) ⇒ Object

Sets the company name.

Parameters:

  • value (Object)

    the new company name



1198
1199
1200
# File 'app/models/address.rb', line 1198

def company_name=(value)
  self.company_name_override = value unless inherited_company_name && value =~ /inherited_company_name/i
end

#correct_to!(candidate, reason: nil) ⇒ Boolean

Applies a provider-verified AddressValidation::Candidate and persists it,
skipping carrier re-validation (the candidate is already verified). Audited
via PaperTrail. Mirrors the autocorrect formatting used by
#external_validation_with_carrier.

Parameters:

Returns:

  • (Boolean)


259
260
261
262
263
264
265
266
267
268
269
# File 'app/models/address.rb', line 259

def correct_to!(candidate, reason: nil)
  self.street1 = I18n.transliterate(candidate.street1.to_s).upcase.presence || street1
  self.city = I18n.transliterate(candidate.city.to_s).upcase.presence || city
  self.state_code = candidate.state_code.presence || state_code
  self.zip = candidate.zip.presence || zip
  self.is_residential = candidate.residential unless candidate.residential.nil?
  self.verified_for_shipping = true
  self.skip_carrier_validation = true
  self.autocorrect_message = reason if reason
  save!
end

#correct_zipObject

Correct zip.



228
229
230
# File 'app/models/address.rb', line 228

def correct_zip
  ValidatesZipcode.valid?(zip, country_iso) if has_postal_codes?
end

#countryCountry

Returns the country this record belongs to.

Returns:

  • (Country)

    the country this record belongs to



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

belongs_to :country, foreign_key: 'country_iso3', primary_key: 'iso3'

#country_isoObject

Country iso.



379
380
381
# File 'app/models/address.rb', line 379

def country_iso
  iso3166_country&.alpha2
end

#country_iso=(val) ⇒ Object

Accessors when iso 2 character form is used

Parameters:

  • val (Object)

    the val



374
375
376
# File 'app/models/address.rb', line 374

def country_iso=(val)
  self.country_iso3 = ISO3166::Country[val]&.alpha3
end

#country_printable_name(locale: I18n.locale) ⇒ Object

Saves us a db call in 99.99% of cases

Parameters:

  • locale (Object) (defaults to: I18n.locale)

    the locale



874
875
876
877
# File 'app/models/address.rb', line 874

def country_printable_name(locale: I18n.locale)
  lang = locale.to_s.first(2).downcase
  iso3166_country.translation(lang)
end

#credit_lineObject

Credit line.



463
464
465
# File 'app/models/address.rb', line 463

def credit_line
  party&.has_terms? ? party.available_credit.to_i : 0
end

#data_hashObject

Data hash.



339
340
341
342
343
344
345
346
347
348
349
350
# File 'app/models/address.rb', line 339

def data_hash
  hsh = {
    street1: street1,
    city: city,
    state: state&.name || state_code,
    zip: zip,
    country: country.iso
  }
  # only add street2 if it's present, otherwise you'll end up with a 'NULL' string
  hsh[:street2] = street2 if street2.present?
  hsh
end

#default_billingObject

Default billing.



491
492
493
# File 'app/models/address.rb', line 491

def default_billing
  party && party.id.present? && (party.billing_address_id == id)
end

#default_billing=(new_value) ⇒ Object

Sets the default billing.

Parameters:

  • value (Object)

    the new default billing

  • new_value (Object)

    the new value



498
499
500
# File 'app/models/address.rb', line 498

def default_billing=(new_value)
  self.set_default_billing_address = true if new_value.to_b
end

#default_mailingObject

Default mailing.



468
469
470
# File 'app/models/address.rb', line 468

def default_mailing
  party&.id.present? && (party.mailing_address_id == id)
end

#default_mailing=(new_value) ⇒ Object

Sets the default mailing.

Parameters:

  • new_value (Object)

    the new value

  • value (Object)

    the new default mailing



486
487
488
# File 'app/models/address.rb', line 486

def default_mailing=(new_value)
  self.set_default_mailing_address = true if new_value.to_b
end

#default_shippingObject

Default shipping.



503
504
505
# File 'app/models/address.rb', line 503

def default_shipping
  party && party.id.present? && (party.shipping_address_id == id)
end

#default_shipping=(new_value) ⇒ Object

Sets the default shipping.

Parameters:

  • value (Object)

    the new default shipping

  • new_value (Object)

    the new value



510
511
512
# File 'app/models/address.rb', line 510

def default_shipping=(new_value)
  self.set_default_shipping_address = true if new_value.to_b
end

#dependentsObject

Dependents.



690
691
692
693
694
695
696
697
698
699
700
701
702
# File 'app/models/address.rb', line 690

def dependents
  deps = []
  deps += shipping_orders
  deps << billing_customer
  deps += shipping_quotes
  deps += 
  deps += mail_activities
  deps += billing_invoices
  deps += shipping_invoices
  deps += billing_credit_memos
  deps += shipping_credit_memos
  deps.uniq.compact
end

Destroy or unlink.



1208
1209
1210
1211
1212
1213
1214
# File 'app/models/address.rb', line 1208

def destroy_or_unlink
  if ok_to_delete?
    destroy
  else
    unlink
  end
end

#determine_address_typeObject

Determine address type.



291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'app/models/address.rb', line 291

def determine_address_type
  return unless persisted?

  if is_residential
    'residential'
  elsif is_construction_site
    'construction_site'
  elsif has_loading_dock
    'commercial_building_with_receiving_dock'
  elsif requires_liftgate
    'commercial_building_without_receiving_dock'
  end
end

#distance_from(target_lat, target_lng) ⇒ Object

Distance from.

Parameters:

  • target_lat (Object)

    the target lat

  • target_lng (Object)

    the target lng



410
411
412
# File 'app/models/address.rb', line 410

def distance_from(target_lat, target_lng)
  Geocoder::Calculations.distance_between([lat, lng], [target_lat, target_lng])
end

#do_not_save_if_warehouseObject

Do not save if warehouse.



951
952
953
954
955
# File 'app/models/address.rb', line 951

def do_not_save_if_warehouse
  return unless is_warehouse? && changed? && !admin_mode.to_b

  errors.add(:is_warehouse, 'Warehouse addresses cannot be modified outside of admin console.')
end

#editing_locked?Boolean

Returns whether the record editing locked.

Returns:

  • (Boolean)

    whether the record editing locked



384
385
386
# File 'app/models/address.rb', line 384

def editing_locked?
  shipping_orders.any?(&:editing_locked?) || support_cases.any?(&:editing_locked?)
end

#external_validation_with_carrier(force = false) ⇒ Object

External validation with carrier.

Parameters:

  • force (Object) (defaults to: false)

    the force



1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
# File 'app/models/address.rb', line 1001

def external_validation_with_carrier(force = false)
  return false if errors.present? # Don't bother hitting the server if we have other errors

  self.require_carrier_validation = require_carrier_validation.to_b # Make sure we interpret all flavors to boolean
  # Not checking for address change to justify this expensive validation causes horrendous performance *any* time this address is verified - CB11/4
  self.verified_for_shipping = false if address_element_changed? || disable_address_correction_changed? || override_all_address_validation_changed?
  return true if override_all_address_validation || (!do_not_accept_legacy_verified && verified) || (disable_address_correction || (!require_carrier_validation && !force))
  return true if verified_for_shipping

  logger.info "External Validation with Carrier called for #{id}. require_carrier_validation(#{require_carrier_validation}), previous verified_for_shipping(#{verified_for_shipping})"

  matches = Address.where(street1: street1)
                   .where(street2: street2)
                   .where(city: city)
                   .where(state_code: state_code)
                   .where(zip: zip)
                   .where(country_iso3: country_iso3)
                   .where(verified_for_shipping: true)
                   .where.not(id: id).limit(1)

  if matches.present?
    logger.info 'Address External Validation with Carrier: Found matching address in system'
    self.verified_for_shipping = true
    return true
  end

  res, message, suggested_address_hashes = carrier_validation
  if res && (suggested_address_hashes&.length == 1) && ((a = suggested_address_hashes.first)[:address].to_s.length > 1) && (a[:address].to_s.length <= MAX_STREET_FIELD_LENGTH)
    # One address, auto fix
    old_address = to_s(include_recipient: false, with_country: false, address_type: true)
    a = suggested_address_hashes.first
    self.street1 = I18n.transliterate(a[:address].to_s).upcase
    self.street2 = a[:address2] || street2 # here nil means use what was entered since we don't validate apartments, suites etc
    self.street3 = a[:address3] || street3 # here nil means use what was entered since we don't validate apartments, suites etc
    self.city = I18n.transliterate(a[:city].to_s).upcase
    self.state_code = a[:state]
    self.zip = a[:zip]
    self.is_residential = a[:is_residential].present? # If null or false it will be false, otherwise true
    self.verified_for_shipping = true
    # self.disable_address_correction = true
    unless changed?
      self.autocorrect_message = "Address was AUTOCORRECTED, FROM: #{old_address}, TO: #{to_s(include_recipient: false, with_country: false,
                                                                                              address_type: true)}."
    end
  elsif suggested_address_hashes&.length&.> 1
    self.suggested_addresses = suggested_address_hashes.map do |a|
      # This will do two things, first remove the address validation attributes that are not understood by address and convert all symbols to strings for merging into the attributes
      new_addr_attr = {}
      new_addr_attr['street1'] = I18n.transliterate(a[:address].to_s).upcase
      new_addr_attr['street2'] = a[:address2] || street2 # here nil means use what was entered since we don't validate apartments, suites etc
      new_addr_attr['street3'] = a[:address3] || street3 # here nil means use what was entered since we don't validate apartments, suites etc
      new_addr_attr['city'] = I18n.transliterate(a[:city].to_s).upcase
      new_addr_attr['state_code'] = a[:state]
      new_addr_attr['zip'] = a[:zip]
      new_addr_attr['is_residential'] = a[:is_residential].present?
      new_addr = dup
      new_addr.attributes = new_addr.attributes.merge(new_addr_attr)
      new_addr.id = id # Because if this is used it will replace it, the dup/cloning doesn't copy this attribute.
      new_addr.verified_for_shipping = true # No need to double validate this address if it gets selected
      # new_addr.disable_address_correction = true
      new_addr.check_for_duplicates = true # will need to verify this is not a duplicate
      new_addr
    end
  end
  self.verified_for_shipping = res
  unless res
    errors.add(:base,
               "We could not validate your address. Please check that your address is correct. If you want to skip validation, please check the 'Disable Address Correction' checkbox when editing the address. #{message}")
  end
  res
end

#first_duplicateObject

First duplicate.



1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
# File 'app/models/address.rb', line 1255

def first_duplicate
  dupe_id = nil
  if party_id
    Address.where(party_id: party_id).where.not(id: id.to_i).find_each do |a|
      logger.info "Address - Comparing #{id} with address id #{a.id}"
      if a == self
        dupe_id = a.id
        break
      end
    end
  end
  dupe_id
end

#format_for_bread_billingObject

Format for bread billing.



671
672
673
674
675
676
677
678
679
680
681
682
# File 'app/models/address.rb', line 671

def format_for_bread_billing
  names = (person_name || company_name).split
  { fullName: person_name || company_name,
    firstName: names.first,
    lastName: names.last,
    address: street1,
    address2: street2,
    city: city,
    state: state_code,
    zip: zip,
    email: party&.email }
end

#format_for_bread_shippingObject

Format for bread shipping.



658
659
660
661
662
663
664
665
666
667
668
# File 'app/models/address.rb', line 658

def format_for_bread_shipping
  names = (person_name || company_name).split
  { fullName: person_name || company_name,
    firstName: names.first,
    lastName: names.last,
    address: street1,
    address2: street2,
    city: city,
    state: state_code,
    zip: zip }
end

#format_for_forte(use_iso = false) ⇒ Object

Format for forte.

Parameters:

  • use_iso (Object) (defaults to: false)

    the use iso



622
623
624
625
626
627
628
629
630
631
632
633
634
635
# File 'app/models/address.rb', line 622

def format_for_forte(use_iso = false)
  names = (person_name || company_name).split
  { name: person_name || company_name,
    first_name: names.first,
    last_name: names.last,
    company_name: company_name || person_name,
    address1: street1,
    address2: street2,
    city: city,
    state: state_code,
    zip: zip,
    country: (use_iso ? country.iso : country.iso3),
    shipping_address_type: is_residential? ? 'residential' : 'commercial' }
end

#format_for_payment_gateway(use_iso = false) ⇒ Object

Format for payment gateway.

Parameters:

  • use_iso (Object) (defaults to: false)

    the use iso



596
597
598
599
600
601
602
603
604
605
# File 'app/models/address.rb', line 596

def format_for_payment_gateway(use_iso = false)
  { name: person_name || company_name,
    address1: street1,
    address2: street2,
    city: city,
    state: state&.name || state_code,
    zip: zip,
    phone: party&.phone,
    country: (use_iso ? country.iso : country.iso3) }
end

#format_for_paypal_api_invoiceObject

Format for paypal api invoice.



648
649
650
651
652
653
654
655
# File 'app/models/address.rb', line 648

def format_for_paypal_api_invoice
  { line1: street1,
    line2: street2,
    city: city,
    state: state_code,
    postal_code: zip,
    country_code: country.iso }
end

#format_for_paypal_invoiceObject

Format for paypal invoice.



638
639
640
641
642
643
644
645
# File 'app/models/address.rb', line 638

def format_for_paypal_invoice
  { line1: street1,
    line2: street2,
    city: city,
    state: state_code,
    postalCode: zip,
    countryCode: country.iso }
end

#format_for_paypal_payment_gateway(use_iso = false) ⇒ Object

Format for paypal payment gateway.

Parameters:

  • use_iso (Object) (defaults to: false)

    the use iso



609
610
611
612
613
614
615
616
617
618
# File 'app/models/address.rb', line 609

def format_for_paypal_payment_gateway(use_iso = false)
  { name: person_name || company_name,
    address1: street1,
    address2: street2,
    city: city,
    state: state_code,
    zip: zip,
    phone: party&.phone,
    country: (use_iso ? country.iso : country.iso3) }
end

#freight_fields_textObject

Returns a string summarizing the freight fields set on the address.
Joins freight field values like 'requires liftgate', 'limited access'
into a comma-separated string. Handles defaults like require signature.



856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
# File 'app/models/address.rb', line 856

def freight_fields_text
  freight_fields_text = nil
  freight_fields_text_arr = []
  freight_fields_text_arr << 'require signature by default' if require_signature_by_default?
  if has_freight_fields_defined?
    freight_fields_text_arr << 'has loading dock' if has_loading_dock?
    freight_fields_text_arr << 'requires inside delivery' if requires_inside_delivery?
    freight_fields_text_arr << 'requires lift-gate' if requires_liftgate?
    freight_fields_text_arr << 'is construction site' if is_construction_site?
    freight_fields_text_arr << 'limited_access' if limited_access?
    freight_fields_text_arr << 'requires appointment' if requires_appointment?
    freight_fields_text = freight_fields_text_arr.join(', ')
  end
  freight_fields_text
end

#freight_fields_validationObject

Freight fields validation.



958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
# File 'app/models/address.rb', line 958

def freight_fields_validation
  return false if errors.present? # Don't bother hitting the server if we have other errors

  logger.info "freight_fields_validation called for #{id}. previous verified_for_shipping (#{verified_for_shipping})"

  # Not checking for address change to justify this expensive validation causes horrendous performance *any* time this address is verified - CB11/4
  self.verified_for_shipping = false if address_element_changed? || override_all_address_validation_changed?
  return true if override_all_address_validation
  return true if verified_for_shipping

  logger.info "freight_fields_validation called for #{id}. verified_for_shipping (#{verified_for_shipping})"

  res = true

  if is_residential && (has_loading_dock || is_trade_show)
    res = false
    errors.add(:is_residential,
               "Residential addresses generally speaking can't have loading docks. If this is *really* the case then please check the 'override all address validation' checkbox.")
  end

  if is_construction_site && requires_inside_delivery
    res = false
    errors.add(:is_construction_site,
               "Construction sites (ie unbuilt yet) generally speaking can't require inside delivery. If this is *really* the case then please check the 'override all address validation' checkbox.")
  end

  if has_loading_dock && requires_liftgate
    res = false
    errors.add(:has_loading_dock,
               "Addresses generally speaking don't usually both have loading docks and require a liftgate. If this is *really* the case then please check the 'override all address validation' checkbox.")
  end

  res
end

#full_address(include_recipient = true, sep = '.', attn_override_flag = nil, hide_country = false, hide_street_2 = false) ⇒ Object

Full address.

Parameters:

  • include_recipient (Object) (defaults to: true)

    the include recipient

  • sep (Object) (defaults to: '.')

    the sep

  • attn_override_flag (Object) (defaults to: nil)

    the attn override flag

  • hide_country (Object) (defaults to: false)

    the hide country

  • hide_street_2 (Object) (defaults to: false)

    the hide street 2



530
531
532
533
534
535
536
537
538
539
540
541
542
543
# File 'app/models/address.rb', line 530

def full_address(include_recipient = true, sep = '.', attn_override_flag = nil, hide_country = false, hide_street_2 = false)
  addr = []
  if include_recipient
    addr << company_name.presence
    addr << (attn_override_flag || person_name.presence)
  end
  addr << street1.presence
  addr << street2.presence unless hide_street_2
  addr << street3.presence
  addr << "#{city}, #{state_code} #{zip}"
  addr << country unless country_iso3 == 'USA' || hide_country
  addr = addr.uniq.compact
  addr.join(sep)
end

#full_address_for_webObject

Full address for web.



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

def full_address_for_web
  full_address(false, '<br>')
end

#geocode_meObject

Geocodes the address by searching using Geocoder
and sets the latitude, longitude and normalized address
attributes on the model. Handles errors from the Geocoder gem.



934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
# File 'app/models/address.rb', line 934

def geocode_me
  results = Geocoder.search(address_for_geocoder)
  geo = results.first
  if geo
    logger.info "Geocoded address #{id} with lat:#{geo.latitude}, lng: #{geo.longitude}"
    self.lat = geo.latitude
    self.lng = geo.longitude
    na = [company_name_override.presence, person_name_override.presence, geo.formatted_address].compact.uniq.join(', ')
    self.normalized_address = na
  else
    logger.info "Geocoding failed on address #{id}, #{address_for_geocoder}"
  end
rescue StandardError => e
  @logger.error " Geocoder error from address(id=#{id}) geocode_me, #{e} skipping"
end

#has_freight_fields_defined?Boolean Also known as: is_valid_for_freight?

Returns whether the record has freight fields defined.

Returns:

  • (Boolean)

    whether the record has freight fields defined



994
995
996
# File 'app/models/address.rb', line 994

def has_freight_fields_defined?
  FREIGHT_FIELDS.all? { |ff| !send(ff).nil? }
end

#has_postal_codes?Boolean

Returns whether the record has postal codes.

Returns:

  • (Boolean)

    whether the record has postal codes



731
732
733
# File 'app/models/address.rb', line 731

def has_postal_codes?
  iso3166_country&.postal_code
end

#has_states_or_provinces?Boolean

Returns whether the record has states or provinces.

Returns:

  • (Boolean)

    whether the record has states or provinces



721
722
723
# File 'app/models/address.rb', line 721

def has_states_or_provinces?
  iso3166_country.subdivisions.present?
end

#in_canada?Boolean

Returns whether the record in canada.

Returns:

  • (Boolean)

    whether the record in canada



443
444
445
# File 'app/models/address.rb', line 443

def in_canada?
  country_iso3 == 'CAN'
end

#in_usa?Boolean

Returns whether the record in usa.

Returns:

  • (Boolean)

    whether the record in usa



448
449
450
# File 'app/models/address.rb', line 448

def in_usa?
  country_iso3 == 'USA'
end

#inherited_company_nameObject

Inherited company name.



1183
1184
1185
1186
1187
1188
1189
# File 'app/models/address.rb', line 1183

def inherited_company_name
  return unless party
  return if party.has_guest_name?
  return party.full_name if party.is_a?(Supplier) || party.is_a?(GroupAssociation) || party&.is_organization?

  party.customer.full_name if party.customer&.is_organization?
end

#inherited_person_nameObject

Inherited person name.



1158
1159
1160
1161
1162
1163
1164
# File 'app/models/address.rb', line 1158

def inherited_person_name
  return unless party
  return if party.has_guest_name?
  return unless party.is_person?

  party.full_name
end

#is_amazon?Boolean

Returns whether the record is amazon.

Returns:

  • (Boolean)

    whether the record is amazon



278
279
280
# File 'app/models/address.rb', line 278

def is_amazon?
  company_name_override&.match?(/amazon/i)
end

#is_for_customer?Boolean

Returns whether the record is for customer.

Returns:

  • (Boolean)

    whether the record is for customer



705
706
707
708
# File 'app/models/address.rb', line 705

def is_for_customer?
  # handle one time shipping address party.nil? is true
  party.nil? || party.instance_of?(Customer) || party.instance_of?(Supplier)
end

#is_for_supplier?Boolean

Returns whether the record is for supplier.

Returns:

  • (Boolean)

    whether the record is for supplier



711
712
713
# File 'app/models/address.rb', line 711

def is_for_supplier?
  party.instance_of?(Supplier)
end

#is_warehouse_and_disabled?Boolean

Returns whether the record is warehouse and disabled.

Returns:

  • (Boolean)

    whether the record is warehouse and disabled



1275
1276
1277
1278
1279
1280
1281
1282
# File 'app/models/address.rb', line 1275

def is_warehouse_and_disabled?
  res = false
  if is_warehouse?
    res = true if WAREHOUSE_PICKUP_DISABLED_US && country_iso3 == 'USA'
    res = true if WAREHOUSE_PICKUP_DISABLED_CA && country_iso3 == 'CAN'
  end
  res
end

#is_warehouse_by_distance?Boolean

Returns whether the record is warehouse by distance.

Returns:

  • (Boolean)

    whether the record is warehouse by distance



1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
# File 'app/models/address.rb', line 1285

def is_warehouse_by_distance?
  res = false
  if is_warehouse?
    res = true # trivial case
  else
    # find distance because there are like 86 instances of addresses that correspond to 300 Granton (aka CA warehouse)
    us_warehouse_address = Store.find(1).warehouse_address
    ca_warehouse_address = Store.find(2).warehouse_address
    distance_us = distance_from(us_warehouse_address.lat, us_warehouse_address.lng)
    distance_ca = distance_from(ca_warehouse_address.lat, ca_warehouse_address.lng)
    res = true if distance_us < 0.1 || distance_ca < 0.1
  end
  res
end

#iso3166_countryObject

Iso3166 country.



479
480
481
# File 'app/models/address.rb', line 479

def iso3166_country
  ISO3166::Country.find_country_by_alpha3(country_iso3)
end

#localized_address_format_array(include_recipient: true, with_country: true, attention_name: nil, attention_prepend: nil, name: nil, locale: I18n.locale) ⇒ Object

Returns an array of address lines formatted according to the address format
for the country. Allows customizing included fields like recipient name,
street, city, etc.

Parameters:

  • include_recipient (Object) (defaults to: true)

    the include recipient

  • with_country (Object) (defaults to: true)

    the with country

  • attention_name (Object) (defaults to: nil)

    the attention name

  • attention_prepend (Object) (defaults to: nil)

    the attention prepend

  • name (String) (defaults to: nil)

    the name

  • locale (Object) (defaults to: I18n.locale)

    the locale



755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
# File 'app/models/address.rb', line 755

def localized_address_format_array(include_recipient: true, with_country: true,
                                   attention_name: nil, attention_prepend: nil,
                                   name: nil,
                                   locale: I18n.locale)
  lang = locale.to_s.first(2).downcase
  address_format = iso3166_country&.address_format&.presence
  # Fallback to US format when iso3166 is not present
  address_format ||= "{{recipient}}\n{{street}}\n{{city}} {{region_short}} {{postalcode}}\n{{country}}"
  liquid_template = Liquid::ParseEnvironment.parse(address_format)

  recipient_names = []
  if include_recipient == :overrides
    recipient_names << company_name_override
    recipient_names << person_name_override
  elsif include_recipient
    if company_name.present?
      name = attention_name || person_name
      name = "#{attention_prepend} #{name}" if name.present? && attention_prepend.present?
      recipient_names << company_name
      recipient_names << name
    else
      name = attention_name || name || person_name
      recipient_names << name
    end
  end

  options = {}
  options[:recipient] = recipient_names.filter_map(&:presence).uniq.join("\n")
  options[:street] = [street1.presence, street2.presence, street3.presence].compact.join("\n")
  options[:city] = city
  options[:region] = region || state_code
  options[:region_short] = state_code
  options[:postalcode] = zip
  options[:country] = with_country && iso3166_country ? iso3166_country.translation(lang) : ''
  address_text = Liquid::Renderer.render(liquid_template, options, "Address(#{id})#localized_address_format_array")
  # Now split on new lines and remove empty lines
  address_text.split("\n").compact_blank
end

#locator_recordLocatorRecord

Locator record.



120
# File 'app/models/address.rb', line 120

has_one    :locator_record, dependent: :destroy, inverse_of: :address

#lookup_timezone(store_timezone = false) ⇒ Object

Looks up the timezone using google api. This requires lat / lng to be set
If the timezone returned is valid (sometime the api returns junk) then
the timezone object is returned. Pass true to store_timezone to record the timezone
upon lookup when missing without a callback

Parameters:

  • store_timezone (Object) (defaults to: false)

    the store timezone



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

def lookup_timezone(store_timezone = false)
  return unless lat && lng

  tz = begin
    Timezone.lookup(lat, lng)
  rescue StandardError
    nil
  end
  return unless PartyRecordTimezone.valid_timezone?(tz)

  update_column(:timezone_name, tz.name) if timezone_name != tz.name && store_timezone
  tz
end

#mail_activitiesActiveRecord::Relation<MailActivity>

Mail activities.

Returns:

See Also:



142
# File 'app/models/address.rb', line 142

has_many   :mail_activities, class_name: 'MailActivity'

#mailing_customerCustomer

Mailing customer.

Returns:

See Also:



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

has_one    :mailing_customer, class_name: 'Customer', dependent: :nullify, foreign_key: :mailing_address_id, inverse_of: :mailing_address

#mailing_storeStore

Mailing store.

Returns:

See Also:



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

has_one    :mailing_store, class_name: 'Store', foreign_key: :mailing_address_id

#name_and_addressObject

Name and address.



473
474
475
476
# File 'app/models/address.rb', line 473

def name_and_address
  [party.full_name, street1, street2, city, "#{state_code} #{zip}",
   iso3166_country.common_name].compact.uniq.join(', ')
end

#north_america?Boolean

Returns whether the record north america.

Returns:

  • (Boolean)

    whether the record north america



736
737
738
# File 'app/models/address.rb', line 736

def north_america?
  %w[USA CAN MEX].include?(country_iso3)
end

#not_a_po_box?(carrier = nil) ⇒ Boolean

Returns whether the record not a po box.

Parameters:

  • carrier (Object) (defaults to: nil)

    the carrier

Returns:

  • (Boolean)

    whether the record not a po box



1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
# File 'app/models/address.rb', line 1075

def not_a_po_box?(carrier = nil)
  Rails.logger.debug { "not_a_po_box? carrier: #{carrier || 'nil'}" }
  carrier_is_not_postal = false
  carrier_is_not_postal = true if carrier && PO_BOX_CARRIERS.exclude?(carrier) && carrier.to_s.downcase.exclude?('post') && carrier.to_s.downcase.exclude?('usps')
  nil_carrier_and_we_do_not_support_postal_carrier = false
  nil_carrier_and_we_do_not_support_postal_carrier = true if carrier.nil? && (SUPPORTED_SHIPPING_CARRIERS[country_iso3.to_sym] || []).exclude?(postal_carrier)
  Rails.logger.debug { "not_a_po_box? carrier_is_not_postal: #{carrier_is_not_postal}" }
  Rails.logger.debug { "not_a_po_box? nil_carrier_and_we_do_not_support_postal_carrier: #{nil_carrier_and_we_do_not_support_postal_carrier}" }
  Rails.logger.debug { "not_a_po_box? po_box?: #{po_box?}" }
  if po_box? && (carrier_is_not_postal || nil_carrier_and_we_do_not_support_postal_carrier)
    errors.add(:base, "We can't ship to PO boxes unless the carrier is one of #{PO_BOX_CARRIERS.join(', ')}")
    false
  else
    true
  end
end

#ok_to_delete?Boolean

Returns whether the record ok to delete.

Returns:

  • (Boolean)

    whether the record ok to delete



685
686
687
# File 'app/models/address.rb', line 685

def ok_to_delete?
  dependents.empty?
end

#one_time_onlyObject

One time only.



423
424
425
# File 'app/models/address.rb', line 423

def one_time_only
  party_id.nil? && !is_warehouse
end

#partyParty?

Returns the party this record belongs to.

Returns:

  • (Party, nil)

    the party this record belongs to



106
# File 'app/models/address.rb', line 106

belongs_to :party, inverse_of: :addresses, optional: true

#person_nameObject

Person name.



1167
1168
1169
# File 'app/models/address.rb', line 1167

def person_name
  person_name_override.presence || inherited_person_name
end

#person_name=(value) ⇒ Object

Sets the person name.

Parameters:

  • value (Object)

    the new person name

  • value (String)

    the value



1174
1175
1176
1177
1178
1179
1180
# File 'app/models/address.rb', line 1174

def person_name=(value)
  self.person_name_override = if value.nil? || (inherited_person_name && value.casecmp?(inherited_person_name))
                                nil
                              else
                                value
                              end
end

#po_box?Boolean

An address is defined as a PO box if all its address components matches the PO box regexp
Ex. if address 1 is 123 main st and address 2 is PO BOX 10 it is not considered a PO box
but if address 1 is PO BOX 10 and there's no other street address then it is considered a PO Box

Returns:

  • (Boolean)


1095
1096
1097
# File 'app/models/address.rb', line 1095

def po_box?
  [street1, street2, street3].filter_map(&:presence).all? { |s| s =~ PO_BOX_REGEXP }
end

#po_box_carriersObject

Po box carriers.



1100
1101
1102
# File 'app/models/address.rb', line 1100

def po_box_carriers
  PO_BOX_CARRIERS_BY_COUNTRY[country_iso3.to_sym]
end

#postal_carrierObject

Postal carrier.



1105
1106
1107
1108
# File 'app/models/address.rb', line 1105

def postal_carrier
  postal_carriers = { USA: 'USPS', CAN: 'Canadapost' }
  postal_carriers[country_iso3.to_sym]
end

#postal_codePostalCode?

Returns the postal code this record belongs to.

Returns:

  • (PostalCode, nil)

    the postal code this record belongs to



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

belongs_to :postal_code, foreign_key: :zip, primary_key: :code, optional: true

#postal_compactObject

Postal compact.



438
439
440
# File 'app/models/address.rb', line 438

def postal_compact
  zip.to_s.scan(/\w/).join.first(6)
end

#recalc_tax_on_open_ordersObject

Recalc tax on open orders.



306
307
308
# File 'app/models/address.rb', line 306

def recalc_tax_on_open_orders
  shipping_orders.so_only.open_for_tax_update.each(&:refresh_tax_rate)
end

#recipientObject

Recipient.



515
516
517
# File 'app/models/address.rb', line 515

def recipient
  company_name.presence || person_name
end

#render_to(options = {}) ⇒ void

This method returns an undefined value.

Renders each line of the address to the given block.

Parameters:

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

    rendering options, forwarded to #to_array

Options Hash (options):

  • include_recipient (Boolean)

    include the recipient name line

  • with_country (Boolean)

    include the country line

  • attention_name (String, nil)

    attention name to prepend

  • attention_prepend (String, nil)

    text prepended to the attention line

  • name (String, nil)

    recipient name override

  • upcase (Boolean)

    render the address upcased

  • phone (String, nil)

    phone number line

  • email (String, nil)

    email line

  • address_type (Boolean)

    include the address type

  • extra_data (String, nil)

    extra data line

  • locale (Symbol)

    locale for the address format



916
917
918
# File 'app/models/address.rb', line 916

def render_to(options = {}, &)
  to_array(**options).each(&)
end

#requires_states_or_provinces?Boolean

Returns whether the record requires states or provinces.

Returns:

  • (Boolean)

    whether the record requires states or provinces



716
717
718
# File 'app/models/address.rb', line 716

def requires_states_or_provinces?
  %w[USA CAN].include?(country_iso3)
end

#returned_rmasActiveRecord::Relation<Rma>

Returned rmas.

Returns:

  • (ActiveRecord::Relation<Rma>)

See Also:



146
# File 'app/models/address.rb', line 146

has_many   :returned_rmas, class_name: 'Rma', foreign_key: :return_shipping_address_id

#same_as(address) ⇒ Object

Same as.

Parameters:

  • address (Object)

    the address



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

def same_as(address)
  # Possible you're just giving me the same address so i'll just match on id
  if address.persisted? && persisted?
    address.id == id
  # If our address was normalized by geocoder, we will use that form to compare
  elsif address.normalized_address.present? && normalized_address.present?
    address.normalized_address == normalized_address
  else # Use native fields
    address_for_comparison == address.address_for_comparison
  end
end

#search_textObject

Search text.



1270
1271
1272
# File 'app/models/address.rb', line 1270

def search_text
  to_array.join(' ')
end

#service_jobsActiveRecord::Relation<ServiceJob>

Service jobs.

Returns:

See Also:



148
# File 'app/models/address.rb', line 148

has_many   :service_jobs, -> { order(:created_at) }, inverse_of: :address

#set_freight_field_defaultsObject

Set freight field defaults.



880
881
882
883
884
885
886
887
# File 'app/models/address.rb', line 880

def set_freight_field_defaults
  self.has_loading_dock ||= false
  self.requires_liftgate ||= true
  self.requires_inside_delivery ||= false
  self.is_construction_site ||= false
  self.limited_access ||= false
  self.requires_appointment ||= true
end

#shipping_account_numbersActiveRecord::Relation<ShippingAccountNumber>

Shipping account numbers.

Returns:

See Also:



144
# File 'app/models/address.rb', line 144

has_many   :shipping_account_numbers

#shipping_credit_memosActiveRecord::Relation<CreditMemo>

Shipping credit memos.

Returns:

See Also:



136
# File 'app/models/address.rb', line 136

has_many   :shipping_credit_memos, class_name: 'CreditMemo', foreign_key: :shipping_address_id

#shipping_customerCustomer

Shipping customer.

Returns:

See Also:



116
# File 'app/models/address.rb', line 116

has_one    :shipping_customer, class_name: 'Customer', dependent: :nullify, foreign_key: :shipping_address_id, inverse_of: :shipping_address

#shipping_invoicesActiveRecord::Relation<Invoice>

Shipping invoices.

Returns:

  • (ActiveRecord::Relation<Invoice>)

See Also:



132
# File 'app/models/address.rb', line 132

has_many   :shipping_invoices, class_name: 'Invoice', foreign_key: :shipping_address_id

#shipping_ordersActiveRecord::Relation<Order>

Shipping orders.

Returns:

  • (ActiveRecord::Relation<Order>)

See Also:



128
# File 'app/models/address.rb', line 128

has_many   :shipping_orders, class_name: 'Order', foreign_key: :shipping_address_id

#shipping_quotesActiveRecord::Relation<Quote>

Shipping quotes.

Returns:

  • (ActiveRecord::Relation<Quote>)

See Also:



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

has_many   :shipping_quotes, class_name: 'Quote', foreign_key: :shipping_address_id

#stateState?

Returns the state this record belongs to.

Returns:

  • (State, nil)

    the state this record belongs to



110
# File 'app/models/address.rb', line 110

belongs_to :state, foreign_key: 'state_code', primary_key: 'code', optional: true

#state_codes_listObject

State codes list.



458
459
460
# File 'app/models/address.rb', line 458

def state_codes_list
  iso3166_country.subdivisions.keys
end

#state_present_and_has_states_or_provinces?Boolean

Returns whether the record state present and has states or provinces.

Returns:

  • (Boolean)

    whether the record state present and has states or provinces



726
727
728
# File 'app/models/address.rb', line 726

def state_present_and_has_states_or_provinces?
  state_code.present? && iso3166_country && has_states_or_provinces?
end

#states_for_selectObject

States for select.



453
454
455
# File 'app/models/address.rb', line 453

def states_for_select
  State.states_for_select(countries_iso: country_iso3)
end

#support_casesActiveRecord::Relation<SupportCase>

Support cases.

Returns:

See Also:



140
# File 'app/models/address.rb', line 140

has_many   :support_cases, class_name: 'SupportCase', foreign_key: :service_address_id

#temporary?Boolean

Returns whether the record temporary.

Returns:

  • (Boolean)

    whether the record temporary



353
354
355
# File 'app/models/address.rb', line 353

def temporary?
  street1 == 'Unknown'
end

#text_block(with_recipient = false, with_country = false, phone = nil) ⇒ Object

Text block.

Parameters:

  • with_recipient (Object) (defaults to: false)

    the with recipient

  • with_country (Object) (defaults to: false)

    the with country

  • phone (Object) (defaults to: nil)

    the phone



893
894
895
896
897
898
899
# File 'app/models/address.rb', line 893

def text_block(with_recipient = false, with_country = false, phone = nil)
  options = {}
  options[:include_recipient] = with_recipient
  options[:with_country] = with_country
  options[:phone] = phone
  to_array(options).join("\n")
end

#timezoneObject

Returns a timezone object or look it up and store



363
364
365
366
367
368
369
370
# File 'app/models/address.rb', line 363

def timezone
  tz = begin
    ActiveSupport::TimeZone.find_tzinfo(timezone_name)
  rescue TZInfo::InvalidTimezoneIdentifier
    ErrorReporting.warning("Invalid timezone #{timezone_name} for address id #{id}")
  end
  tz || lookup_timezone(:store)
end

#timezones_for_selectObject

Timezones for select.



358
359
360
# File 'app/models/address.rb', line 358

def timezones_for_select
  iso3166_country.timezones.zone_identifiers
end

#to_array(include_recipient: true, with_country: true, attention_name: nil, attention_prepend: nil, name: nil, upcase: false, phone: nil, email: nil, address_type: false, extra_data: nil, locale: I18n.locale) ⇒ Object

Returns an array of address lines formatted according to the options passed in.

Allows customizing included fields like recipient name, street, city, etc.
Also includes phone, email, address type, and extra data if specified.
Can upcase the fields.

locale - The locale to use for translations (default: I18n.locale)
include_recipient - Whether to include the recipient name
with_country - Whether to include the country
attention_name - Custom attention name to use
attention_prepend - String to prepend to attention name
name - Custom recipient name to use
upcase - Whether to upcase the fields
phone - Phone number to include
email - Email to include
address_type - Whether to include the address type
extra_data - Extra data to append

Returns an array of address lines, based on the country formatting rules.

Parameters:

  • include_recipient (Object) (defaults to: true)

    the include recipient

  • with_country (Object) (defaults to: true)

    the with country

  • attention_name (Object) (defaults to: nil)

    the attention name

  • attention_prepend (Object) (defaults to: nil)

    the attention prepend

  • name (String) (defaults to: nil)

    the name

  • upcase (Object) (defaults to: false)

    the upcase

  • phone (Object) (defaults to: nil)

    the phone

  • email (Object) (defaults to: nil)

    the email

  • address_type (Object) (defaults to: false)

    the address type

  • extra_data (Object) (defaults to: nil)

    the extra data

  • locale (Object) (defaults to: I18n.locale)

    the locale



824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
# File 'app/models/address.rb', line 824

def to_array(include_recipient: true,
             with_country: true,
             attention_name: nil,
             attention_prepend: nil,
             name: nil,
             upcase: false,
             phone: nil,
             email: nil,
             address_type: false,
             extra_data: nil,
             locale: I18n.locale)
  a = localized_address_format_array(include_recipient:,
                                     with_country:,
                                     attention_name:,
                                     attention_prepend:,
                                     name:,
                                     locale:)
  a << "Phone: #{phone}" if phone.present?
  a << "E-mail: #{email}" if email.present?
  if address_type
    a << (is_residential? ? "(Residential #{freight_fields_text})" : "(Commercial #{freight_fields_text})")
    a << "Freight Ready?: #{is_valid_for_freight? ? '✔ Yes' : 'X No'}"
  end
  a << extra_data
  res = a.map(&:to_s).map(&:squish).map(&:presence).uniq.compact
  res = res.map(&:upcase) if upcase.to_b
  res.uniq
end

#to_s(params = {}) ⇒ String

Returns string representation of the record.

Parameters:

  • params (Object) (defaults to: {})

    the params

Returns:

  • (String)

    string representation of the record



742
743
744
# File 'app/models/address.rb', line 742

def to_s(params = {})
  to_array(**params).join(', ')
end

Unlink.



1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
# File 'app/models/address.rb', line 1217

def unlink
  party_id
  return unless party

  Address.transaction do
    party.update_attribute!(:shipping_address_id, nil) if self == party.shipping_address
    party.update_attribute!(:mailing_address_id, nil) if self == party.mailing_address
    raise 'Cannot remove address from customer as it is used as the main billing address.' if self == party.billing_address

    update_attribute(:party_id, nil)
  end
end

#update_party_timezoneObject

Update party timezone.



311
312
313
314
315
# File 'app/models/address.rb', line 311

def update_party_timezone
  return unless party && saved_change_to_timezone_name?

  PartyRecordTimezone.new.process_party_and_associations(party)
end

#via_jsonObject

Via json.



1231
1232
1233
# File 'app/models/address.rb', line 1231

def via_json
  to_json
end

#via_json=(x) ⇒ Object

Sets the via json.

Parameters:

  • x (Object)

    the x

  • value (Object)

    the new via json



1238
1239
1240
# File 'app/models/address.rb', line 1238

def via_json=(x)
  from_json x
end

#via_xmlObject

Via xml.



1243
1244
1245
# File 'app/models/address.rb', line 1243

def via_xml
  to_xml
end

#via_xml=(x) ⇒ Object

Sets the via xml.

Parameters:

  • value (Object)

    the new via xml

  • x (Object)

    the x



1250
1251
1252
# File 'app/models/address.rb', line 1250

def via_xml=(x)
  from_xml x
end

#warehouse_storeStore

Warehouse store.

Returns:

See Also:



122
# File 'app/models/address.rb', line 122

has_one    :warehouse_store, class_name: 'Store', foreign_key: :warehouse_address_id

#zip_5Object

Zip 5.



428
429
430
# File 'app/models/address.rb', line 428

def zip_5
  zip.to_s.first(5)
end

#zip_compactObject

Zip compact.



433
434
435
# File 'app/models/address.rb', line 433

def zip_compact
  in_usa? ? zip_5 : postal_compact
end

#zip_state_consistent?Boolean?

Cheap, offline check that the ZIP and state agree, using the
PostalCode cache (self-healing) for the US and the FSA first-letter map
for Canada. This is a signal, not an authority: only full-address
verification (AddressValidation) can say which field is wrong. Used to
decide whether to spend a verification call (e.g. before a TaxJar submit —
AppSignal #6094, a VT ZIP saved with state FL).

Returns:

  • (Boolean, nil)

    true/false when known; nil when the ZIP is unknown
    or the country has no ZIP↔state mapping (don't block on unknowns).



241
242
243
244
245
246
247
248
249
# File 'app/models/address.rb', line 241

def zip_state_consistent?
  return nil if zip.blank? || state_code.blank?
  return nil unless %w[USA CAN].include?(country_iso3)

  canonical = PostalCode.state_for(zip, country_iso3: country_iso3)
  return nil if canonical.blank?

  canonical.casecmp?(state_code.to_s)
end