Class: Spree::FulfilmentChanger

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Validations
Defined in:
app/models/spree/fulfilment_changer.rb

Overview

Service class to change fulfilment of inventory units of a particular variant to another shipment. The other shipment would typically have a different shipping method, stock location or delivery date, such that we actually change the planned fulfilment for the items in question.

Can be used to merge shipments by moving all items to another shipment, because this class will delete any empty original shipment.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(current_shipment:, desired_shipment:, variant:, quantity:) ⇒ FulfilmentChanger

Returns a new instance of FulfilmentChanger.



25
26
27
28
29
30
31
32
# File 'app/models/spree/fulfilment_changer.rb', line 25

def initialize(current_shipment:, desired_shipment:, variant:, quantity:)
  @current_shipment = current_shipment
  @desired_shipment = desired_shipment
  @current_stock_location = current_shipment.stock_location
  @desired_stock_location = desired_shipment.stock_location
  @variant = variant
  @quantity = quantity
end

Instance Attribute Details

#current_shipmentSpree::Shipment

The shipment we transfer units from

Returns:



19
20
21
# File 'app/models/spree/fulfilment_changer.rb', line 19

def current_shipment
  @current_shipment
end

#current_stock_locationSpree::StockLocation

The stock location of the current shipment

Returns:



19
20
21
# File 'app/models/spree/fulfilment_changer.rb', line 19

def current_stock_location
  @current_stock_location
end

#desired_shipmentSpree::Shipment

The shipment we want to move units onto

Returns:



19
20
21
# File 'app/models/spree/fulfilment_changer.rb', line 19

def desired_shipment
  @desired_shipment
end

#desired_stock_locationSpree::StockLocation

The stock location of the desired shipment

Returns:



19
20
21
# File 'app/models/spree/fulfilment_changer.rb', line 19

def desired_stock_location
  @desired_stock_location
end

#quantityInteger

How many units we want to move

Returns:

  • (Integer)

    the current value of quantity



19
20
21
# File 'app/models/spree/fulfilment_changer.rb', line 19

def quantity
  @quantity
end

#variantSpree::Variant

We only move units that represent this variant

Returns:



19
20
21
# File 'app/models/spree/fulfilment_changer.rb', line 19

def variant
  @variant
end

Instance Method Details

#run!true, false

Performs the change of fulfilment

Returns:

  • (true, false)

    Whether the requested fulfilment change was successful



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'app/models/spree/fulfilment_changer.rb', line 42

def run!
  # Validations here are intended to catch all necessary prerequisites.
  # We return early so all checks have happened already.
  return false if invalid?
  desired_shipment.save! if desired_shipment.new_record?

  # Retrieve how many on hand items we can take from desired stock location
  available_quantity = [desired_shipment.stock_location.count_on_hand(variant), default_on_hand_quantity].max

  new_on_hand_quantity = [available_quantity, quantity].min
  unstock_quantity = desired_shipment.stock_location.backorderable?(variant) ? quantity : new_on_hand_quantity

  ActiveRecord::Base.transaction do
    if handle_stock_counts?
      # We only run this query if we need it.
      current_on_hand_quantity = [current_shipment.inventory_units.pre_shipment.size, quantity].min

      # Restock things we will not fulfil from the current shipment anymore
      current_stock_location.restock(variant, current_on_hand_quantity, current_shipment)
      # Unstock what we will fulfil with the new shipment
      desired_stock_location.unstock(variant, unstock_quantity, desired_shipment)
    end

    # These two statements are the heart of this class. We change the number
    # of inventory units requested from one shipment to the other.
    # We order by state, because `'backordered' < 'on_hand'`.
    current_shipment.
      inventory_units.
      where(variant: variant).
      order(state: :asc).
      limit(new_on_hand_quantity).
      update_all(shipment_id: desired_shipment.id, state: :on_hand)

    current_shipment.
      inventory_units.
      where(variant: variant).
      order(state: :asc).
      limit(quantity - new_on_hand_quantity).
      update_all(shipment_id: desired_shipment.id, state: :backordered)
  end

  # We modified the inventory units at the database level for speed reasons.
  # The downside of that is that we need to reload the associations.
  current_shipment.inventory_units.reload
  desired_shipment.inventory_units.reload

  # If the current shipment now has no inventory units left, we won't need it any longer.
  if current_shipment.inventory_units.length.zero?
    current_shipment.destroy!
  else
    # The current shipment has changed, so we need to make sure that shipping rates
    # have the correct amount.
    current_shipment.refresh_rates
  end

  # The desired shipment has also change, so we need to make sure shipping rates
  # are up-to-date, too.
  desired_shipment.refresh_rates

  # In order to reflect the changes in the order totals
  desired_shipment.order.reload
  desired_shipment.order.recalculate

  true
end