Class: ShippingOption

Inherits:
ApplicationRecord show all
Defined in:
app/models/shipping_option.rb

Overview

== Schema Information

Table name: shipping_options
Database name: primary

id :integer not null, primary key
carrier :string(255)
country :string(255)
days_commitment :float default(3.5), not null
delivery_commitment :text
description :string(255)
information :text
is_beta :boolean default(FALSE), not null
is_freight :boolean default(FALSE), not null
is_inactive :boolean default(FALSE), not null
is_third_party_only :boolean default(FALSE), not null
jde_shipping_stop_code :string(255)
name :string(255)
service_code :string(255)
service_level :string(255)
supported_for_st :boolean default(FALSE), not null
tax_class :string(3) not null
created_at :datetime
updated_at :datetime
item_id :integer

Indexes

index_shipping_options_on_service_level (service_level)

Constant Summary collapse

SERVICE_LEVELS =

Service levels.

{ standard: 3, expedited: 2, express: 1 }.freeze
OPTIONS_FOR_RMAS =

Options for rmas.

[1, 139, 207, 4, 30, 63, 146].freeze
PAPERLESS_RETURN_OPTION_LABEL =

Human-readable tag appended to a paperless-eligible RMA shipping option.
Shared with the picker view (rmas/calculate_shipping_costs) which detects
this exact substring to render a highlighted badge.

'USPS Label Broker Paperless QR return'
SHIPENGINE_LTL_CARRIERS =
%w[
  ShipengineRlCarriers ShipengineFedExFreight ShipengineFedExFreightEconomy
  ShipengineSaia ShipengineRoadrunner ShipengineXpo
].freeze
MANUAL_PICKUP_CARRIERS =
{
  'ShipengineRoadrunner' => {
    name: 'Roadrunner Freight',
    phone: '(630) 679-2800',
    email: 'CustomerService@RoadrunnerLTL.com',
    url: 'https://tools.rrts.com/QuickPickup',
    location: 'Chicago/Bolingbrook, IL'
  }
}.freeze

Constants included from Schedulable

Schedulable::SIMPLE_FORM_OPTIONS

Has many collapse

Belongs to collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Class Method Details

.:CANActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are :CAN. Active Record Scope

Returns:

See Also:



47
# File 'app/models/shipping_option.rb', line 47

scope :CAN, -> { for_country_iso('CA') }

.:NLDActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are :NLD. Active Record Scope

Returns:

See Also:



48
# File 'app/models/shipping_option.rb', line 48

scope :NLD, -> { for_country_iso('NL') }

.:USAActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are :USA. Active Record Scope

Returns:

See Also:



46
# File 'app/models/shipping_option.rb', line 46

scope :USA, -> { for_country_iso('US') }

.activeActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are active. Active Record Scope

Returns:

See Also:



42
# File 'app/models/shipping_option.rb', line 42

scope :active, -> { where('shipping_options.is_inactive IS NOT TRUE') }

.by_days_commitmentActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are by days commitment. Active Record Scope

Returns:

See Also:



45
# File 'app/models/shipping_option.rb', line 45

scope :by_days_commitment, -> { order('shipping_options.days_commitment DESC') }

.carrier_options_for_select(country_iso3 = nil) ⇒ Object



94
95
96
97
98
# File 'app/models/shipping_option.rb', line 94

def self.carrier_options_for_select(country_iso3 = nil)
  opts = ShippingOption.active.where.not(carrier: nil).order(:carrier)
  opts = opts.where(country: country_iso3[0..1]) if country_iso3.present?
  opts.pluck(:carrier).uniq
end

.for_country_isoActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are for country iso. Active Record Scope

Returns:

See Also:



49
# File 'app/models/shipping_option.rb', line 49

scope :for_country_iso, ->(country_iso) { where(country: country_iso).by_days_commitment }

.for_country_iso3ActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are for country iso3. Active Record Scope

Returns:

See Also:



50
# File 'app/models/shipping_option.rb', line 50

scope :for_country_iso3, ->(country_iso3) { for_country_iso(Country.iso3_to_iso_map[country_iso3]) }

.for_rmasActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are for rmas. Active Record Scope

Returns:

See Also:



54
55
56
57
# File 'app/models/shipping_option.rb', line 54

scope :for_rmas, lambda {
  where(shipping_options: { id: OPTIONS_FOR_RMAS })
    .or(where(shipping_options: { paperless_eligible: true }))
}

.freightActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are freight. Active Record Scope

Returns:

See Also:



58
# File 'app/models/shipping_option.rb', line 58

scope :freight, -> { where("shipping_options.carrier = 'UPSFreight'") }

.get_appropriate(country_code, _is_residential, purolator_ltl = nil) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'app/models/shipping_option.rb', line 61

def self.get_appropriate(country_code, _is_residential, purolator_ltl = nil)
  opts = []
  if country_code.in?(Country::EU_COUNTRIES_ISO)
    opts = ShippingOption.active.NLD.to_a # This should be more generic and add any european country?
  elsif country_code == 'CA'
    # puts "ShippingOption.get_appropriate: country_code: CA, purolator_ltl: #{purolator_ltl}"
    opts = ShippingOption.active.CAN.to_a
    unless purolator_ltl
      # puts "ShippingOption.get_appropriate: purolator_ltl IS NOT TRUE"
      purolator_freight_sos = ShippingOption.where(carrier: 'PurolatorFreight')
      purolator_freight_sos.each do |purolator_freight_so|
        opts.delete(purolator_freight_so)
      end
    end
  else
    opts = ShippingOption.active.USA.to_a
  end
  opts
end

.non_freightActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are non freight. Active Record Scope

Returns:

See Also:



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

scope :non_freight, -> { where("shipping_options.carrier <> 'UPSFreight'") }

.non_overrideActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are non override. Active Record Scope

Returns:

See Also:



44
# File 'app/models/shipping_option.rb', line 44

scope :non_override, -> { where.not(shipping_options: { carrier: nil }) }

.options_for_preferred_shipping_method_select(country = 'USA') ⇒ Object



89
90
91
92
# File 'app/models/shipping_option.rb', line 89

def self.options_for_preferred_shipping_method_select(country = 'USA')
  # must only use supported shipping options here or free shipping becomes almost default
  ShippingOption.active.supported.for_country_iso3(country).where.not(carrier: nil).order(days_commitment: :desc).map { |so| ["#{so.description} #{so.country}, code: #{so.service_code}", so.name] }
end

.select_options(country_iso3 = nil, supported = nil, include_override = false) ⇒ Object



81
82
83
84
85
86
87
# File 'app/models/shipping_option.rb', line 81

def self.select_options(country_iso3 = nil, supported = nil, include_override = false)
  opts = ShippingOption.active
  opts = opts.non_override unless include_override == true
  opts = opts.supported if supported == true
  opts = opts.send(country_iso3.to_sym) if country_iso3.present?
  opts.order(:carrier, days_commitment: :desc, description: :asc, country: :desc).map { |so| ["#{so.carrier}: #{so.description} (#{so.country}), code: #{so.service_code}", so.id] }
end

.supportedActiveRecord::Relation<ShippingOption>

A relation of ShippingOptions that are supported. Active Record Scope

Returns:

See Also:



43
# File 'app/models/shipping_option.rb', line 43

scope :supported, -> { where('shipping_options.is_third_party_only IS NOT TRUE and shipping_options.carrier IS NOT NULL') }

.supported_carrier_options_for_rmas(country = 'US') ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'app/models/shipping_option.rb', line 117

def self.supported_carrier_options_for_rmas(country = 'US')
  supported_carriers = supported_carrier_options_for_select
  opts = ShippingOption.active.for_rmas.where(carrier: supported_carriers).where(country: country)
  # `paperless_eligible: :desc` sorts the paperless variant ahead of its
  # standard sibling within the same carrier (true > false).
  opts.order(:carrier, paperless_eligible: :desc, days_commitment: :desc, description: :asc, country: :desc).map do |so|
    # Suffix is purely informational so staff can tell a customer "this
    # method supports a QR code at the counter" while picking. Does not
    # gate the API request — see `Shipping::ShipengineBase#get_label`.
    label = "#{so.carrier}: #{so.description} (#{so.country}), code: #{so.service_code}"
    label += "#{PAPERLESS_RETURN_OPTION_LABEL}" if so.paperless_eligible?
    [label, so.id]
  end
end

.supported_carrier_options_for_select(country = nil) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
# File 'app/models/shipping_option.rb', line 100

def self.supported_carrier_options_for_select(country = nil)
  if country
    SUPPORTED_SHIPPING_CARRIERS[country.to_sym] || []
  else
    carrier_options = []
    SUPPORTED_SHIPPING_CARRIERS.each do |_country, carrier_options_arr|
      carrier_options.concat(carrier_options_arr)
    end
    carrier_options.uniq
  end
end

.with_mirrored_paperless_costs(option_ids, costs_by_option_id) ⇒ Hash{Integer=>ShippingCost}

A paperless option is a separate ShippingOption row sharing a carrier +
service_code with a standard option but with its own name, so the rate
engine (which keys quotes by option name) never produces a cost row for it.
Returns a copy of costs_by_option_id with each paperless option's cost
mirrored from its standard sibling (same carrier + service_code) — same
postage, just a different label format. Paperless options whose sibling has
no quote stay absent (no quote shown).

Parameters:

  • option_ids (Array<Integer>)

    shipping_option ids in play

  • costs_by_option_id (Hash{Integer=>ShippingCost})

    real costs by option id

Returns:



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'app/models/shipping_option.rb', line 143

def self.with_mirrored_paperless_costs(option_ids, costs_by_option_id)
  options = where(id: option_ids).index_by(&:id)
  result = costs_by_option_id.dup
  options.each_value do |opt|
    next unless opt.paperless_eligible?
    next if result[opt.id]

    sibling_cost = costs_by_option_id.values.find do |sc|
      sibling = options[sc.shipping_option_id]
      sibling && !sibling.paperless_eligible? &&
        sibling.carrier == opt.carrier && sibling.service_code == opt.service_code
    end
    result[opt.id] = sibling_cost if sibling_cost
  end
  result
end

Instance Method Details

#country_iso3Object



209
210
211
# File 'app/models/shipping_option.rb', line 209

def country_iso3
  Country.find_by(iso: country).iso3
end

#is_economy?Boolean

Returns:

  • (Boolean)


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

def is_economy?
  %w[ground standard fedex_ground fedex_ground_residential usps_media_mail usps_standard_post usps_parcel_select usps_priority_mail canada_post_regular_parcel canpar_ground purolator_ground
     purolator_ground_evening].include? name
end

#is_fedex_ca?Boolean

Returns:

  • (Boolean)


169
170
171
172
# File 'app/models/shipping_option.rb', line 169

def is_fedex_ca?
  nm = name.to_s.downcase
  nm.index('fedex_').present? && nm.index('_ca').present?
end

#is_freightquote?Boolean

Returns:

  • (Boolean)


174
175
176
# File 'app/models/shipping_option.rb', line 174

def is_freightquote?
  carrier.to_s.downcase.index('freightquote').present?
end

#is_override?Boolean

Returns:

  • (Boolean)


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

def is_override?
  name.to_s.downcase == 'override'
end

#is_postal?Boolean

Returns:

  • (Boolean)


165
166
167
# File 'app/models/shipping_option.rb', line 165

def is_postal?
  POSTAL_CARRIERS.include? carrier
end

#is_shipengine_ltl?Boolean

Returns:

  • (Boolean)


183
184
185
# File 'app/models/shipping_option.rb', line 183

def is_shipengine_ltl?
  SHIPENGINE_LTL_CARRIERS.include?(carrier)
end

#itemItem

Returns:

See Also:



35
# File 'app/models/shipping_option.rb', line 35

belongs_to :item, optional: true

#manual_pickup_contactObject



201
202
203
# File 'app/models/shipping_option.rb', line 201

def manual_pickup_contact
  MANUAL_PICKUP_CARRIERS[carrier]
end

#requires_manual_pickup?Boolean

Returns:

  • (Boolean)


197
198
199
# File 'app/models/shipping_option.rb', line 197

def requires_manual_pickup?
  MANUAL_PICKUP_CARRIERS.key?(carrier)
end

#roomsActiveRecord::Relation<Room>

Returns:

  • (ActiveRecord::Relation<Room>)

See Also:



34
# File 'app/models/shipping_option.rb', line 34

has_many :rooms, through: :shipping_costs

#shipping_costsActiveRecord::Relation<ShippingCost>

Returns:

See Also:



33
# File 'app/models/shipping_option.rb', line 33

has_many :shipping_costs