Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep backorders when splitting part of variant to new shipment with same SL #5670

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
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
Loading