Skip to content
Draft
5 changes: 4 additions & 1 deletion core/app/models/spree/exchange.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ def display_amount

def perform!
begin
shipments = Spree::Config.stock.coordinator_class.new(@order, @reimbursement_objects.map(&:build_exchange_inventory_unit)).shipments
shipments = Spree::Config.stock.coordinator_class.new(
@order,
inventory_units: @reimbursement_objects.map(&:build_exchange_inventory_unit)
).shipments
rescue Spree::Order::InsufficientStock
raise UnableToCreateShipments.new("Could not generate shipments for all items. Out of stock?")
end
Expand Down
4 changes: 2 additions & 2 deletions core/app/models/spree/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -513,9 +513,9 @@ def create_proposed_shipments
end

def create_shipments_for_line_item(line_item)
units = Spree::Config.stock.inventory_unit_builder_class.new(self).missing_units_for_line_item(line_item)
inventory_units = Spree::Config.stock.inventory_unit_builder_class.new(self).missing_units_for_line_item(line_item)

Spree::Config.stock.coordinator_class.new(self, units).shipments.each do |shipment|
Spree::Config.stock.coordinator_class.new(self, inventory_units:).shipments.each do |shipment|
shipments << shipment
end
end
Expand Down
33 changes: 33 additions & 0 deletions core/app/models/spree/stock/coordinator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module Spree
module Stock
class Coordinator
def initialize(order, inventory_units: nil)
@context = {order:, inventory_units:}
@order = order

Middleware::InventoryUnit.new.call(@context)
Middleware::InventoryUnitGroup.new.call(@context)
Middleware::StockLocation.new.call(@context)
Middleware::Desired.new.call(@context)
Middleware::Availability.new.call(@context)
end

def shipments
@shipments ||= begin
Middleware::Allocate.new.call(@context)
Middleware::Package.new.call(@context)
Middleware::Shipment.new.call(@context)

shipments = @context[:shipments]

# Make sure we don't add the proposed shipments to the order
@order.shipments = @order.shipments - shipments

shipments
end

Check failure on line 29 in core/app/models/spree/stock/coordinator.rb

View workflow job for this annotation

GitHub Actions / Check Ruby

Layout/BeginEndAlignment: `end` at 29, 23 is not aligned with `@shipments ||= begin` at 18, 8.
end
end
end
end
17 changes: 17 additions & 0 deletions core/app/models/spree/stock/middleware/allocate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Spree
module Stock
module Middleware
class Allocate
def call(context)
allocator = Spree::Config.stock.allocator_class.new(context[:availability])
on_hand_packages, backordered_packages, leftover = allocator.allocate_inventory(context[:desired])

raise Spree::Order::InsufficientStock.new(items: leftover.quantities) unless leftover.empty?

context[:on_hand_packages] = on_hand_packages
context[:backordered_packages] = backordered_packages
end
end
end
end
end
14 changes: 14 additions & 0 deletions core/app/models/spree/stock/middleware/availability.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Spree
module Stock
module Middleware
class Availability
def call(context)
context[:availability] = Spree::Stock::Availability.new(
variants: context[:desired].variants,
stock_locations: context[:stock_locations]
)
Comment on lines +6 to +9
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely an example of how overusing this approach makes the code worse. This has seriously obfuscated the dependency chain of the availability and desired variants objects. If we have some dependent computed stuff like this, maybe we just make this methods on the context object or something? It's worth considering this further.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! I didn't put too much thought into how things shook out entirely at this point in the draft. Will definitely revisit before opening for review.

end
end
end
end
end
11 changes: 11 additions & 0 deletions core/app/models/spree/stock/middleware/desired.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Spree
module Stock
module Middleware
class Desired
def call(context)
context[:desired] = Spree::StockQuantities.new(context[:inventory_unit_groups].transform_values(&:count))
end
end
end
end
end
15 changes: 15 additions & 0 deletions core/app/models/spree/stock/middleware/inventory_unit.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Spree
module Stock
module Middleware
class InventoryUnit
def call(context)
order = context[:order]

context[:inventory_units] = context[:inventory_units] ||
Spree::Config.stock.inventory_unit_builder_class.new(order).units
end
end
end
end
end

Check failure on line 15 in core/app/models/spree/stock/middleware/inventory_unit.rb

View workflow job for this annotation

GitHub Actions / Check Ruby

Layout/TrailingEmptyLines: 1 trailing blank lines detected.
11 changes: 11 additions & 0 deletions core/app/models/spree/stock/middleware/inventory_unit_group.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Spree
module Stock
module Middleware
class InventoryUnitGroup
def call(context)
context[:inventory_unit_groups] = context[:inventory_units].group_by(&:variant)
end
end
end
end
end
41 changes: 41 additions & 0 deletions core/app/models/spree/stock/middleware/package.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module Spree
module Stock
module Middleware
class Package
def call(context)
packages = context[:stock_locations].map do |stock_location|
on_hand = context[:on_hand_packages][stock_location.id] || Spree::StockQuantities.new
backordered = context[:backordered_packages][stock_location.id] || Spree::StockQuantities.new

next if on_hand.empty? && backordered.empty?

package = Spree::Stock::Package.new(stock_location)
package.add_multiple(get_units(context, on_hand), :on_hand)
package.add_multiple(get_units(context, backordered), :backordered)

package
end.compact

context[:packages] = split_packages(packages)
end

private

def get_units(context, quantities)
quantities.flat_map do |variant, quantity|
context[:inventory_unit_groups][variant].shift(quantity)
end
end

def split_packages(initial_packages)
splitters = Spree::Config.environment.stock_splitters

initial_packages.flat_map do |initial_package|
stock_location = initial_package.stock_location
Spree::Stock::SplitterChain.new(stock_location, splitters).split([initial_package])
end
end
end
end
end
end
15 changes: 15 additions & 0 deletions core/app/models/spree/stock/middleware/shipment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Spree
module Stock
module Middleware
class Shipment
def call(context)
context[:shipments] = context[:packages].map do |package|
shipment = package.shipment = package.to_shipment
shipment.shipping_rates = Spree::Config.stock.estimator_class.new.shipping_rates(package)
shipment
end
end
end
end
end
end
24 changes: 24 additions & 0 deletions core/app/models/spree/stock/middleware/stock_location.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Spree
module Stock
module Middleware
class StockLocation
def call(context)
filtered_stock_locations = Spree::Config.stock.location_filter_class.new(
load_stock_locations, context[:order]
).filter
sorted_stock_locations = Spree::Config.stock.location_sorter_class.new(
filtered_stock_locations
).sort

context[:stock_locations] = sorted_stock_locations
end

private

def load_stock_locations
Spree::StockLocation.all
end
end
end
end
end
9 changes: 8 additions & 1 deletion core/app/models/spree/stock/simple_coordinator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ class SimpleCoordinator
:filtered_stock_locations, :inventory_units_by_variant, :desired,
:availability, :allocator, :packages

def initialize(order, inventory_units = nil)
def initialize(order, inventory_units_deprecated = nil, inventory_units: nil)
if inventory_units_deprecated
Spree.deprecator.warn "Using the `inventory_units` positional " \
"argument is deprecated in favor of using the keyword argument. "

inventory_units ||= inventory_units_deprecated
end

@order = order
@inventory_units =
inventory_units || Spree::Config.stock.inventory_unit_builder_class.new(order).units
Expand Down
Loading
Loading