Module: Models::CustomerShipping

Extended by:
ActiveSupport::Concern
Included in:
Customer
Defined in:
app/concerns/models/customer_shipping.rb

Overview

Shipping stats, carriers, Canadian Tire helpers, shipment methods.

See Also:

Instance Method Summary collapse

Instance Method Details

#bill_shipping_to_account_number_options_for_select(carrier, origin_address = nil) ⇒ Array<Array(String, Integer)>

Label/value pairs for a "bill shipping to account" select dropdown.

Parameters:

  • carrier (String)

    carrier code to filter by; accounts with a nil carrier match any

  • origin_address (Address, nil) (defaults to: nil)

    origin address; must support third-party
    shipping or an empty array is returned

Returns:

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

    [label, shipping_account_number id] pairs



160
161
162
163
164
165
166
# File 'app/concerns/models/customer_shipping.rb', line 160

def (carrier, origin_address = nil)
  return [] unless origin_address.present? && origin_address.supports_third_party_shipping

  all_acct_numbers = (origin_address.country_iso3)
  shipping_acct_numbers = all_acct_numbers.select { |sn| sn.carrier == carrier || sn.carrier.nil? }
  shipping_acct_numbers.uniq.map { |sn| ["#{sn.customer.name}: #{sn.carrier} #{sn.}", sn.id] }
end

#cached_active_shipping_account_numbers(country_iso3) ⇒ Array<ShippingAccountNumber>

Active shipping account numbers for a country, memoized per country.

Includes the customer's own accounts plus the parent organization's.

Parameters:

  • country_iso3 (String)

    ISO 3166-1 alpha-3 country code

Returns:



174
175
176
177
178
179
180
181
# File 'app/concerns/models/customer_shipping.rb', line 174

def (country_iso3)
  @cached_shipping_account_numbers ||= {}
  @cached_shipping_account_numbers[country_iso3] ||= begin
    acct_numbers = .active.includes(:customer).where(country_iso3: country_iso3).to_a
    acct_numbers += parent_organization..active.includes(:customer).where(country_iso3: country_iso3).to_a if parent_organization
    acct_numbers
  end
end

#default_shipping_option_nameString

Name of the default shipping method for this customer.

Falls back to the parent organization's preference, then to the
country-specific default for the store's country.

Returns:

  • (String)

    the preferred or default shipping method name



103
104
105
106
107
108
# File 'app/concerns/models/customer_shipping.rb', line 103

def default_shipping_option_name
  res = preferred_shipping_method
  res = parent_organization.preferred_shipping_method if res.blank? && parent_organization.present?
  res = DEFAULT_SHIPPING_METHOD_NAME[store.country.iso3.to_sym] if res.blank?
  res
end

#default_shipping_options(shipping_address_to_use = nil) ⇒ Array<ShippingOption>

Active, non-legacy, non-third-party shipping options for the store's country,
sorted by description, with the non-matching FedEx Ground residential/commercial
variant removed for the destination address.

Parameters:

  • shipping_address_to_use (Address, nil) (defaults to: nil)

    destination address used for
    residential/commercial filtering; nil skips the filtering

Returns:

  • (Array<ShippingOption>)

    eligible shipping options sorted by description



87
88
89
90
91
92
93
94
95
# File 'app/concerns/models/customer_shipping.rb', line 87

def default_shipping_options(shipping_address_to_use = nil)
  opts = ShippingOption.active
                       .where(country: store.country.iso)
                       .where.not(name: %w[legacy override])
                       .where(is_third_party_only: [false, nil])
                       .sort_by { |so| [-1.0 * so.days_commitment, so.description] }.to_a
  filter_shipping_options_for_residential!(opts, shipping_address_to_use)
  opts.sort_by { |opt| opt[:description] }
end

#distance_from_us_warehouseInteger?

Distance in miles from the US warehouse to the customer's billing address.

Returns:

  • (Integer, nil)

    rounded distance in miles (minimum 1), or nil when
    the billing address is missing or the distance cannot be computed



13
14
15
16
17
18
19
20
21
# File 'app/concerns/models/customer_shipping.rb', line 13

def distance_from_us_warehouse
  return nil if billing_address.nil?

  us_warehouse_address = Store.find(1).warehouse_address
  distance = billing_address.distance_from(us_warehouse_address.lat, us_warehouse_address.lng)
  return nil if distance.nil?

  distance < 1 ? 1 : distance.round
end

#find_or_create_canadian_tire_address_from_store_number(store_number) ⇒ Address?

Finds or builds a Canadian Tire store address from a store number.

Parameters:

  • store_number (String)

    Canadian Tire store number (e.g. "1234")

Returns:

  • (Address, nil)

    existing or newly built address, or nil unless the
    customer is Canadian Tire



188
189
190
191
192
# File 'app/concerns/models/customer_shipping.rb', line 188

def find_or_create_canadian_tire_address_from_store_number(store_number)
  return nil unless canadian_tire?

  find_existing_canadian_tire_address(store_number) || build_canadian_tire_address_from_store(store_number)
end

#jde_abnString, ...

JDE address book number of the shipping address.

Returns:

  • (String, Integer, nil)

    the shipping address's JDE number, or nil when absent



62
63
64
# File 'app/concerns/models/customer_shipping.rb', line 62

def jde_abn
  shipping_address&.jde_number
end

#jde_b_abString, ...

JDE address book number of the billing address (EDI "bill to" party).

Returns:

  • (String, Integer, nil)

    the billing address's JDE number, or nil when absent



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

def jde_b_ab
  billing_address&.jde_number
end

#jde_s_abString, ...

JDE address book number of the shipping address (EDI "ship to" party).

Returns:

  • (String, Integer, nil)

    the shipping address's JDE number, or nil when absent



69
70
71
# File 'app/concerns/models/customer_shipping.rb', line 69

def jde_s_ab
  shipping_address&.jde_number
end

#largest_shipping_cost_in_last_yearBigDecimal, Integer

Largest single-invoice shipping cost in the trailing year.

Returns:

  • (BigDecimal, Integer)

    maximum shipping cost over the last year, or 0 when no invoices



48
49
50
# File 'app/concerns/models/customer_shipping.rb', line 48

def largest_shipping_cost_in_last_year
  invoices.where(shipped_date: (Date.current - 1.year)..).maximum(:shipping_cost) || 0
end

#last_invoice_shipping_costBigDecimal, Integer

Shipping cost on the customer's most recently shipped invoice.

Returns:

  • (BigDecimal, Integer)

    shipping cost of the latest shipped invoice, or 0 when none exists



40
41
42
43
# File 'app/concerns/models/customer_shipping.rb', line 40

def last_invoice_shipping_cost
  row = invoices.order(shipped_date: :desc).pick(:shipping_cost)
  row.nil? ? 0 : row
end

#shipping_account_number(carrier, billing_address = nil, destination_address = nil) ⇒ ShippingAccountNumber?

Default shipping account number for a carrier.

Parameters:

  • carrier (String)

    carrier code to look up (e.g. 'fedex', 'ups')

  • billing_address (Address, nil) (defaults to: nil)

    billing address whose owning party may
    also provide an account number

  • destination_address (Address, nil) (defaults to: nil)

    destination address; restricts results
    to its country when present

Returns:



119
120
121
# File 'app/concerns/models/customer_shipping.rb', line 119

def (carrier, billing_address = nil, destination_address = nil)
  (carrier, billing_address, destination_address).first
end

#shipping_account_numbers_for_carrier(carrier, billing_address = nil, destination_address = nil) ⇒ ActiveRecord::Relation<ShippingAccountNumber>

Shipping account numbers available to this customer for a carrier, ordered
by selection priority (own account first, then parent org's, then others,
newest first within each tier).

Parameters:

  • carrier (String)

    carrier code to look up (e.g. 'fedex', 'ups')

  • billing_address (Address, nil) (defaults to: nil)

    billing address whose owning party may
    also provide account numbers

  • destination_address (Address, nil) (defaults to: nil)

    destination address; restricts results
    to its country when present

Returns:



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'app/concerns/models/customer_shipping.rb', line 134

def (carrier, billing_address = nil, destination_address = nil)
  ids = (billing_address)
  res = ShippingAccountNumber.active.where(customer_id: ids).where(carrier:)
  res = res.where(country_iso3: destination_address.country_iso3) if destination_address.present?
  # Default-selection order for #shipping_account_number(...).first:
  #   1. THIS customer's own account (newest first), then
  #   2. the parent org's / HQ's account (newest first), then
  #   3. any other candidate (e.g. billing-address owner), newest first.
  # So a subsidiary that has its own SAN bills its own most-recent account
  # by default, and one with none falls back to HQ's most-recent. The old
  # "oldest across everyone" made every branch default to HQ's master
  # account (e.g. Ferguson HQ 754922). CSR can still override per option
  # via the dropdown in _shipping_services.html.erb.
  priority = ShippingAccountNumber.sanitize_sql_array(
    ['CASE WHEN shipping_account_numbers.customer_id = ? THEN 0 ' \
     'WHEN shipping_account_numbers.customer_id = ? THEN 1 ELSE 2 END', id, parent_id]
  )
  res.order(Arel.sql(priority), created_at: :desc, id: :desc)
end

#shipping_amount_last_30_daysBigDecimal, Integer

Total shipping cost invoiced over the trailing 30 days.

Returns:

  • (BigDecimal, Integer)

    sum of shipping costs for invoices shipped in the last 30 days



33
34
35
# File 'app/concerns/models/customer_shipping.rb', line 33

def shipping_amount_last_30_days
  invoices.where('shipped_date between ? and ?', Date.current - 30, Date.current).sum(:shipping_cost)
end

#shipping_amount_last_monthBigDecimal, Integer

Total shipping cost invoiced during the previous calendar month.

Returns:

  • (BigDecimal, Integer)

    sum of shipping costs for invoices shipped last month



26
27
28
# File 'app/concerns/models/customer_shipping.rb', line 26

def shipping_amount_last_month
  invoices.where('shipped_date between ? and ?', Date.current.last_month.beginning_of_month, Date.current.last_month.end_of_month).sum(:shipping_cost)
end

#spent_20_on_shipping_in_last_year?Boolean

Whether any invoice in the trailing year shipped with a cost of $20 or more.

Returns:

  • (Boolean)

    true when the largest shipping cost in the last year is at least 20



55
56
57
# File 'app/concerns/models/customer_shipping.rb', line 55

def spent_20_on_shipping_in_last_year?
  largest_shipping_cost_in_last_year >= 20
end