Module: Models::PartyAddresses

Extended by:
ActiveSupport::Concern
Included in:
Party
Defined in:
app/concerns/models/party_addresses.rb

Overview

Address delegation, catalog/store helpers, and visit-derived location fields.

== Country / locale / timezone resolution chain

These three derived attributes have multi-level overrides that depend on the
concrete Party subclass and which underlying store/customer is reachable:

  • country Party (first address) -> Customer (+ catalog store fallback) ->
    Contact (+ customer catalog fallback)
  • locale Party (I18n.locale) -> Customer (catalog-derived) ->
    Contact (customer.locale)
  • timezone Party (timezone_name column) -> Contact (+ customer.timezone fallback)

Customer overrides are in Models::CustomerDisplay; Contact overrides are in
Models::ContactDisplay. The Party-level methods here serve as the bottom of the chain
and the default for any Party that is neither a Customer nor a Contact.

See Also:

Instance Method Summary collapse

Instance Method Details

#addresses_options_for_selectArray<Array(String, Integer)>

Builds [label, value] pairs of full address and id for select helpers.

Returns:

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

    options for a select tag



58
59
60
# File 'app/concerns/models/party_addresses.rb', line 58

def addresses_options_for_select
  addresses.map { |add| [add.full_address(true, ', '), add.id] }
end

#can_change_country?Boolean

Whether the party's country may be changed: bots without an id,
guests, and lead-qualification parties without a main address.

Returns:

  • (Boolean)

    true when the country can be changed



202
203
204
# File 'app/concerns/models/party_addresses.rb', line 202

def can_change_country?
  bot_id.blank? || state == 'guest' || (state == 'lead_qualify' && main_address.nil?)
end

#catalog_countryCountry?

Country of this party's catalog (or the associated customer's
catalog).

Returns:

  • (Country, nil)

    the catalog's country



125
126
127
128
# File 'app/concerns/models/party_addresses.rb', line 125

def catalog_country
  cat = catalog || try(:customer)&.catalog
  cat&.country
end

#catalog_country_isoString?

Returns ISO code of the catalog's country.

Returns:

  • (String, nil)

    ISO code of the catalog's country



136
137
138
# File 'app/concerns/models/party_addresses.rb', line 136

def catalog_country_iso
  catalog_country&.iso
end

#catalog_country_iso3String?

Returns ISO3 code of the catalog's country.

Returns:

  • (String, nil)

    ISO3 code of the catalog's country



131
132
133
# File 'app/concerns/models/party_addresses.rb', line 131

def catalog_country_iso3
  catalog_country&.iso3
end

#cityString?

City from the first address, falling back to the most recent visit's
geolocated city.

Returns:

  • (String, nil)

    the city name



76
77
78
# File 'app/concerns/models/party_addresses.rb', line 76

def city
  first_address&.city || visit_data(:city)
end

#countryCountry?

Country of the first address. Bottom of the country resolution chain
described in the module header.

Returns:



107
108
109
# File 'app/concerns/models/party_addresses.rb', line 107

def country
  first_address&.country
end

#country_isoString?

Returns ISO code of the party's country.

Returns:

  • (String, nil)

    ISO code of the party's country



117
118
119
# File 'app/concerns/models/party_addresses.rb', line 117

def country_iso
  country&.iso
end

#country_iso3String?

Returns ISO3 code of the party's country.

Returns:

  • (String, nil)

    ISO3 code of the party's country



112
113
114
# File 'app/concerns/models/party_addresses.rb', line 112

def country_iso3
  country&.iso3
end

#first_addressAddress?

Returns the party's first address, or nil when the addresses
association is unreachable.

Returns:

  • (Address, nil)

    the first address



66
67
68
69
70
# File 'app/concerns/models/party_addresses.rb', line 66

def first_address
  addresses.first
rescue StandardError
  nil
end

#flagString?

Flag code ('us' or 'ca') for this party's catalog, falling back to
the associated customer's catalog and then the US catalog.

Returns:

  • (String, nil)

    the flag code



183
184
185
186
# File 'app/concerns/models/party_addresses.rb', line 183

def flag
  cat = catalog || try(:customer)&.catalog || Catalog.us_catalog
  { 1 => 'us', 2 => 'ca' }[cat.store_id]
end

#location_nameString?

"City, ST" summary of the main address, when one exists.

Returns:

  • (String, nil)

    the location summary



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

def location_name
  (addr = main_address) ? "#{addr.city}, #{addr.state_code}" : nil
end

#most_recent_visitVisit?

The party's most recent visit, memoized.

Returns:

  • (Visit, nil)

    the most recent visit by start time



167
168
169
# File 'app/concerns/models/party_addresses.rb', line 167

def most_recent_visit
  @most_recent_visit ||= visits.order(started_at: :desc).first
end

#new_addressAddress

Builds a new unsaved address for this party, defaulting country to the
store's country, copying defaults from the first existing address, and
setting the residential flag from the homeowner status.

Returns:

  • (Address)

    the new unsaved address



29
30
31
32
33
34
35
36
# File 'app/concerns/models/party_addresses.rb', line 29

def new_address
  addr = Address.new
  addr.party = self
  addr.country = store.country
  copy_first_address_defaults!(addr)
  addr.is_residential = is_homeowner?
  addr
end

#storeStore?

Store backing this party's catalog, or nil when the catalog is
unreachable.

Returns:

  • (Store, nil)

    the catalog's store



192
193
194
195
196
# File 'app/concerns/models/party_addresses.rb', line 192

def store
  catalog.store
rescue StandardError
  nil
end

#street1String?

Returns first address street line 1.

Returns:

  • (String, nil)

    first address street line 1



89
90
91
# File 'app/concerns/models/party_addresses.rb', line 89

def street1
  first_address&.street1
end

#street2String?

Returns first address street line 2.

Returns:

  • (String, nil)

    first address street line 2



94
95
96
# File 'app/concerns/models/party_addresses.rb', line 94

def street2
  first_address&.street2
end

#street3String?

Returns first address street line 3.

Returns:

  • (String, nil)

    first address street line 3



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

def street3
  first_address&.street3
end

#timezoneActiveSupport::TimeZone?

Timezone from the party's timezone_name column. Reports a warning to
AppSignal when the name is not a valid TZInfo identifier.

Returns:

  • (ActiveSupport::TimeZone, nil)

    the resolved timezone



144
145
146
147
148
# File 'app/concerns/models/party_addresses.rb', line 144

def timezone
  ActiveSupport::TimeZone.find_tzinfo(timezone_name)
rescue TZInfo::InvalidTimezoneIdentifier
  ErrorReporting.warning("Invalid timezone #{timezone_name} for party id #{id}")
end

#unique_addresses(include_address_id = nil) ⇒ Array<Address>

Returns the party's addresses deduplicated by full address value,
ordered by street1.

Parameters:

  • include_address_id (Integer, nil) (defaults to: nil)

    id of an address to always
    include even when a duplicate of it is already in the result

Returns:

  • (Array<Address>)

    the deduplicated addresses



44
45
46
47
48
49
50
51
52
53
# File 'app/concerns/models/party_addresses.rb', line 44

def unique_addresses(include_address_id = nil)
  all_addresses = addresses.order(:street1)
  res = []
  all_addresses.each do |address|
    (res << address) && next if address.id == include_address_id

    res << address unless res.any?(address)
  end
  res
end

#visit_data(attribute) ⇒ Object?

Reads an attribute off the most recent visit.

Parameters:

  • attribute (String, Symbol)

    attribute to read from the visit

Returns:

  • (Object, nil)

    the attribute value, or nil without a visit



175
176
177
# File 'app/concerns/models/party_addresses.rb', line 175

def visit_data(attribute)
  most_recent_visit.try(attribute.to_sym)
end

#visit_locationString?

Location summary geolocated from the most recent visit.

Returns:

  • (String, nil)

    the visit's location summary



160
161
162
# File 'app/concerns/models/party_addresses.rb', line 160

def visit_location
  most_recent_visit&.location_summary
end

#zipString?

Zip/postal code from the first address, falling back to the most
recent visit's geolocated postal code.

Returns:

  • (String, nil)

    the zip or postal code



84
85
86
# File 'app/concerns/models/party_addresses.rb', line 84

def zip
  first_address&.zip_compact || visit_data(:postal_code)
end