Class: Spree::Shipment

Inherits:
Base
  • Object
show all
Extended by:
DisplayMoney
Defined in:
app/models/spree/shipment.rb

Overview

An order's planned shipments including tracking and cost.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from DisplayMoney

money_methods

Methods inherited from Base

display_includes, #initialize_preference_defaults, page, preference

Methods included from Preferences::Preferable

#admin_form_preference_names, #default_preferences, #defined_preferences, #get_preference, #has_preference!, #has_preference?, #preference_default, #preference_type, #set_preference

Instance Attribute Details

#special_instructionsObject

TODO: remove the suppress_mailer temporary variable once we are calling 'ship' from outside of the state machine and can actually pass variables through.



24
25
26
# File 'app/models/spree/shipment.rb', line 24

def special_instructions
  @special_instructions
end

#suppress_mailerObject

TODO: remove the suppress_mailer temporary variable once we are calling 'ship' from outside of the state machine and can actually pass variables through.



24
25
26
# File 'app/models/spree/shipment.rb', line 24

def suppress_mailer
  @suppress_mailer
end

Instance Method Details

#add_shipping_method(shipping_method, selected = false) ⇒ Object



106
107
108
# File 'app/models/spree/shipment.rb', line 106

def add_shipping_method(shipping_method, selected = false)
  shipping_rates.create(shipping_method: shipping_method, selected: selected, cost: cost)
end

#addressObject



389
390
391
392
# File 'app/models/spree/shipment.rb', line 389

def address
  Spree::Deprecation.warn("Calling Shipment#address is deprecated. Use Order#ship_address instead", caller)
  order.ship_address if order
end

#after_cancelObject



111
112
113
# File 'app/models/spree/shipment.rb', line 111

def after_cancel
  manifest.each { |item| manifest_restock(item) }
end

#after_resumeObject



115
116
117
# File 'app/models/spree/shipment.rb', line 115

def after_resume
  manifest.each { |item| manifest_unstock(item) }
end

#backordered?Boolean

Returns:

  • (Boolean)


119
120
121
# File 'app/models/spree/shipment.rb', line 119

def backordered?
  inventory_units.any?(&:backordered?)
end

#can_transition_from_canceled_to_ready?Boolean

Returns:

  • (Boolean)


93
94
95
# File 'app/models/spree/shipment.rb', line 93

def can_transition_from_canceled_to_ready?
  can_transition_from_pending_to_ready?
end

#can_transition_from_pending_to_ready?Boolean

Returns:

  • (Boolean)


87
88
89
90
91
# File 'app/models/spree/shipment.rb', line 87

def can_transition_from_pending_to_ready?
  order.can_ship? &&
    inventory_units.all? { |iu| iu.shipped? || iu.allow_ship? || iu.canceled? } &&
    (order.paid? || !Spree::Config[:require_payment_to_ship])
end

#can_transition_from_pending_to_shipped?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'app/models/spree/shipment.rb', line 83

def can_transition_from_pending_to_shipped?
  !requires_shipment?
end

#currencyObject



123
124
125
# File 'app/models/spree/shipment.rb', line 123

def currency
  order ? order.currency : Spree::Config[:currency]
end

#determine_state(order) ⇒ Object

Determines the appropriate state according to the following logic:

canceled if order is canceled pending unless order is complete and order.payment_state is paid shipped if already shipped (ie. does not change the state) ready all other cases



261
262
263
264
265
266
267
268
269
270
# File 'app/models/spree/shipment.rb', line 261

def determine_state(order)
  return 'canceled' if order.canceled?
  return 'shipped' if shipped?
  return 'pending' unless order.can_ship?
  if can_transition_from_pending_to_ready?
    'ready'
  else
    'pending'
  end
end

#discounted_costObject Also known as: discounted_amount



127
128
129
# File 'app/models/spree/shipment.rb', line 127

def discounted_cost
  cost + promo_total
end

#editable_by?(_user) ⇒ Boolean

Returns:

  • (Boolean)


162
163
164
# File 'app/models/spree/shipment.rb', line 162

def editable_by?(_user)
  !shipped?
end

#finalize!Object

Decrement the stock counts for all pending inventory units in this shipment and mark. Any previous non-pending inventory units are skipped as their stock had already been allocated.



170
171
172
# File 'app/models/spree/shipment.rb', line 170

def finalize!
  finalize_pending_inventory_units
end

#include?(variant) ⇒ Boolean

Returns:

  • (Boolean)


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

def include?(variant)
  inventory_units_for(variant).present?
end

#inventory_units_for(variant) ⇒ Object



178
179
180
# File 'app/models/spree/shipment.rb', line 178

def inventory_units_for(variant)
  inventory_units.where(variant_id: variant.id)
end

#inventory_units_for_item(line_item, variant = nil) ⇒ Object



182
183
184
# File 'app/models/spree/shipment.rb', line 182

def inventory_units_for_item(line_item, variant = nil)
  inventory_units.where(line_item_id: line_item.id, variant_id: line_item.variant.id || variant.id)
end

#item_costObject



186
187
188
# File 'app/models/spree/shipment.rb', line 186

def item_cost
  line_items.map(&:total).sum
end

#manifestObject



231
232
233
# File 'app/models/spree/shipment.rb', line 231

def manifest
  @manifest ||= Spree::ShippingManifest.new(inventory_units: inventory_units).items
end

#ready_or_pending?Boolean

Returns:

  • (Boolean)


190
191
192
# File 'app/models/spree/shipment.rb', line 190

def ready_or_pending?
  ready? || pending?
end

#refresh_ratesObject



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'app/models/spree/shipment.rb', line 194

def refresh_rates
  return shipping_rates if shipped?
  return [] unless can_get_rates?

  # StockEstimator.new assigment below will replace the current shipping_method
  original_shipping_method_id = shipping_method.try!(:id)

  new_rates = Spree::Config.stock.estimator_class.new.shipping_rates(to_package)

  # If one of the new rates matches the previously selected shipping
  # method, select that instead of the default provided by the estimator.
  # Otherwise, keep the default.
  selected_rate = new_rates.detect{ |rate| rate.shipping_method_id == original_shipping_method_id }
  if selected_rate
    new_rates.each do |rate|
      rate.selected = (rate == selected_rate)
    end
  end

  self.shipping_rates = new_rates
  save!

  shipping_rates
end

#requires_shipment?Boolean

Returns:

  • (Boolean)


385
386
387
# File 'app/models/spree/shipment.rb', line 385

def requires_shipment?
  !stock_location || stock_location.fulfillable?
end

#select_shipping_method(shipping_method) ⇒ Object



219
220
221
222
223
224
225
# File 'app/models/spree/shipment.rb', line 219

def select_shipping_method(shipping_method)
  estimator = Spree::Config.stock.estimator_class.new
  rates = estimator.shipping_rates(to_package, false)
  rate = rates.detect { |r| r.shipping_method_id == shipping_method.id }
  rate.selected = true
  self.shipping_rates = [rate]
end

#selected_shipping_rateObject



227
228
229
# File 'app/models/spree/shipment.rb', line 227

def selected_shipping_rate
  shipping_rates.detect(&:selected?)
end

#selected_shipping_rate_idObject



235
236
237
# File 'app/models/spree/shipment.rb', line 235

def selected_shipping_rate_id
  selected_shipping_rate.try(:id)
end

#selected_shipping_rate_id=(id) ⇒ Object



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'app/models/spree/shipment.rb', line 239

def selected_shipping_rate_id=(id)
  return if selected_shipping_rate_id == id
  new_rate = shipping_rates.detect { |rate| rate.id == id.to_i }
  unless new_rate
    fail(
      ArgumentError,
      "Could not find shipping rate id #{id} for shipment #{number}"
    )
  end

  transaction do
    selected_shipping_rate.update!(selected: false) if selected_shipping_rate
    new_rate.update!(selected: true)
  end
end

#set_up_inventory(state, variant, _order, line_item) ⇒ Object



272
273
274
275
276
277
278
# File 'app/models/spree/shipment.rb', line 272

def set_up_inventory(state, variant, _order, line_item)
  inventory_units.create(
    state: state,
    variant_id: variant.id,
    line_item_id: line_item.id
  )
end

#shipped=(value) ⇒ Object



280
281
282
283
# File 'app/models/spree/shipment.rb', line 280

def shipped=(value)
  return unless value == '1' && shipped_at.nil?
  self.shipped_at = Time.current
end

#shipping_methodObject



285
286
287
# File 'app/models/spree/shipment.rb', line 285

def shipping_method
  selected_shipping_rate.try(:shipping_method)
end

#tax_totalObject

Only one of either included_tax_total or additional_tax_total is set This method returns the total of the two. Saves having to check if tax is included or additional.



292
293
294
# File 'app/models/spree/shipment.rb', line 292

def tax_total
  included_tax_total + additional_tax_total
end

#to_packageObject



296
297
298
299
300
301
302
303
# File 'app/models/spree/shipment.rb', line 296

def to_package
  package = Stock::Package.new(stock_location)
  package.shipment = self
  inventory_units.includes(:variant).joins(:variant).group_by(&:state).each do |state, state_inventory_units|
    package.add_multiple state_inventory_units, state.to_sym
  end
  package
end

#to_paramObject



305
306
307
# File 'app/models/spree/shipment.rb', line 305

def to_param
  number
end

#totalBigDecimal Also known as: final_price

Returns the amount of this shipment, taking into consideration all its adjustments.

Returns:

  • (BigDecimal)

    the amount of this shipment, taking into consideration all its adjustments.



136
137
138
# File 'app/models/spree/shipment.rb', line 136

def total
  cost + adjustment_total
end

#total_before_taxBigDecimal

Returns the amount of this item, taking into consideration all non-tax adjustments.

Returns:

  • (BigDecimal)

    the amount of this item, taking into consideration all non-tax adjustments.



144
145
146
# File 'app/models/spree/shipment.rb', line 144

def total_before_tax
  amount + adjustments.select { |a| !a.tax? && a.eligible? }.sum(&:amount)
end

#total_excluding_vatBigDecimal Also known as: pre_tax_amount

Note:

just like `cost`, this does not include any additional tax

Returns the amount of this shipment before VAT tax

Returns:

  • (BigDecimal)

    the amount of this shipment before VAT tax



150
151
152
# File 'app/models/spree/shipment.rb', line 150

def total_excluding_vat
  total_before_tax - included_tax_total
end

#total_with_itemsObject Also known as: final_price_with_items



156
157
158
# File 'app/models/spree/shipment.rb', line 156

def total_with_items
  total + item_cost
end

#tracking_urlObject



309
310
311
312
313
# File 'app/models/spree/shipment.rb', line 309

def tracking_url
  return nil unless tracking && shipping_method

  @tracking_url ||= shipping_method.build_tracking_url(tracking)
end

#transfer_to_location(variant, quantity, stock_location) ⇒ Object



369
370
371
372
373
# File 'app/models/spree/shipment.rb', line 369

def transfer_to_location(variant, quantity, stock_location)
  Spree::Deprecation.warn("Please use the Spree::FulfilmentChanger class instead of Spree::Shipment#transfer_to_location", caller)
  new_shipment = order.shipments.create!(stock_location: stock_location)
  transfer_to_shipment(variant, quantity, new_shipment)
end

#transfer_to_shipment(variant, quantity, shipment_to_transfer_to) ⇒ Object



375
376
377
378
379
380
381
382
383
# File 'app/models/spree/shipment.rb', line 375

def transfer_to_shipment(variant, quantity, shipment_to_transfer_to)
  Spree::Deprecation.warn("Please use the Spree::FulfilmentChanger class instead of Spree::Shipment#transfer_to_location", caller)
  Spree::FulfilmentChanger.new(
    current_shipment: self,
    desired_shipment: shipment_to_transfer_to,
    variant: variant,
    quantity: quantity
  ).run!
end

#update!(order_or_attrs) ⇒ Object



357
358
359
360
361
362
363
364
365
366
367
# File 'app/models/spree/shipment.rb', line 357

def update!(order_or_attrs)
  if order_or_attrs.is_a?(Spree::Order)
    Spree::Deprecation.warn "Calling Shipment#update! with an order to update the shipments state is deprecated. Please use Shipment#update_state instead."
    if order_or_attrs.object_id != order.object_id
      Spree::Deprecation.warn "Additionally, update! is being passed an instance of order which isn't the same object as the shipment's order association"
    end
    update_state
  else
    super
  end
end

#update_amountsObject



315
316
317
318
319
320
321
322
323
324
325
# File 'app/models/spree/shipment.rb', line 315

def update_amounts
  if selected_shipping_rate
    self.cost = selected_shipping_rate.cost
    if changed?
      update_columns(
        cost: cost,
        updated_at: Time.current
      )
    end
  end
end

#update_attributes_and_order(params = {}) ⇒ Object

Update Shipment and make sure Order states follow the shipment changes



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

def update_attributes_and_order(params = {})
  if update_attributes params
    if params.key? :selected_shipping_rate_id
      # Changing the selected Shipping Rate won't update the cost (for now)
      # so we persist the Shipment#cost before running `order.recalculate`
      update_amounts
      order.recalculate
    end

    true
  end
end

#update_stateObject

Updates the state of the Shipment bypassing any callbacks.

If this moves the shipmnent to the 'shipped' state, after_ship will be called.



345
346
347
348
349
350
351
352
353
354
355
# File 'app/models/spree/shipment.rb', line 345

def update_state
  old_state = state
  new_state = determine_state(order)
  if new_state != old_state
    update_columns(
      state: new_state,
      updated_at: Time.current
    )
    after_ship if new_state == 'shipped'
  end
end