Skip to content
41 changes: 31 additions & 10 deletions core/app/models/spree/fulfilment_changer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ def run!
# we can take from the desired location, we could end up with some items being backordered.
def run_tracking_inventory
# 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

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

ActiveRecord::Base.transaction do
Expand All @@ -105,19 +105,21 @@ def run_tracking_inventory
# 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'`.
# We start to move the new actual backordered quantity, so the remaining
# quantity can be set to on_hand state.
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)
limit(backordered_quantity).
update_all(shipment_id: desired_shipment.id, state: :backordered)

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)
limit(quantity - backordered_quantity).
update_all(shipment_id: desired_shipment.id, state: :on_hand)
end
end

Expand All @@ -141,11 +143,22 @@ def handle_stock_counts?
current_shipment.order.completed? && current_stock_location != desired_stock_location
end

def default_on_hand_quantity
def get_available_quantity
if current_stock_location != desired_stock_location
desired_location_quantifier.positive_stock
else
sl_availability = current_location_quantifier.positive_stock
shipment_availability = current_shipment.inventory_units.where(variant: variant).on_hand.count
sl_availability + shipment_availability
end
end

def get_backordered_quantity(available_quantity, new_on_hand_quantity)
if current_stock_location != desired_stock_location
0
quantity - new_on_hand_quantity
else
current_shipment.inventory_units.where(variant: variant).on_hand.count
shipment_quantity = current_shipment.inventory_units.where(variant: variant).size
shipment_quantity - available_quantity
end
end

Expand All @@ -156,11 +169,19 @@ def current_shipment_not_already_shipped
end

def enough_stock_at_desired_location
unless Spree::Stock::Quantifier.new(variant, desired_stock_location).can_supply?(quantity)
unless desired_location_quantifier.can_supply?(quantity)
errors.add(:desired_shipment, :not_enough_stock_at_desired_location)
end
end

def desired_location_quantifier
@desired_location_quantifier ||= Spree::Stock::Quantifier.new(variant, desired_stock_location)
end

def current_location_quantifier
@current_location_quantifier ||= Spree::Stock::Quantifier.new(variant, current_stock_location)
end

def desired_shipment_different_from_current
if desired_shipment.id == current_shipment.id
errors.add(:desired_shipment, :can_not_transfer_within_same_shipment)
Expand Down
42 changes: 33 additions & 9 deletions core/app/models/spree/stock/quantifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,16 @@ class Quantifier
# If unspecified it will check inventory in all available StockLocations
def initialize(variant, stock_location_or_id = nil)
@variant = variant
@stock_items = variant.stock_items.select do |stock_item|
if stock_location_or_id
stock_item.stock_location == stock_location_or_id ||
stock_item.stock_location_id == stock_location_or_id
else
stock_item.stock_location.active?
end
end
@stock_location_or_id = stock_location_or_id
@stock_items = variant_stock_items
end

# Returns the total number of inventory units on hand for the variant.
#
# @return [Fixnum] number of inventory units on hand, or infinity if
# inventory is not tracked on the variant.
def total_on_hand
if @variant.should_track_inventory?
if variant.should_track_inventory?
stock_items.sum(&:count_on_hand)
else
Float::INFINITY
Expand All @@ -48,6 +42,36 @@ def backorderable?
def can_supply?(required)
total_on_hand >= required || backorderable?
end

def positive_stock
return unless stock_location

on_hand = stock_location.count_on_hand(variant)
on_hand.positive? ? on_hand : 0
end

private

attr_reader :variant, :stock_location_or_id

def stock_location
@stock_location ||= if stock_location_or_id.is_a?(Spree::StockLocation)
stock_location_or_id
else
Spree::StockLocation.find_by(id: stock_location_or_id)
end
end

def variant_stock_items
variant.stock_items.select do |stock_item|
if stock_location_or_id
stock_item.stock_location == stock_location_or_id ||
stock_item.stock_location_id == stock_location_or_id
else
stock_item.stock_location.active?
end
end
end
end
end
end
Loading