Skip to content

Commit

Permalink
Make the inventory unit builder configurable
Browse files Browse the repository at this point in the history
This adds the inventory unit builder (Spree::Stock::InventoryUnitBuilder
by default) to the Spree::Config.stock configuration, so that it can be
overridden.

This is desirable for any store that doesn't have a one-to-one mapping
between purchased variants and the fulfilled inventory units. The
extension solidus_product_assembly heavily overrides this class to
accomplish this, so with this change it will be able to stop decorating
Spree::Stock::InventoryUnitBuilder and instead provide its own.

I'll admit that I'm not a huge fan of how I tested the functionality in
the order class, but there's a reason for stubbing the preference like
that. Spree::Config.stock isn't a normal config object, so it doesn't
support our test helpers for stubbing preferences within the context of
only a single test.
  • Loading branch information
jarednorman committed May 30, 2022
1 parent 3c72462 commit 2483b46
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 2 deletions.
3 changes: 2 additions & 1 deletion core/app/models/spree/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,8 @@ def create_proposed_shipments
end

def create_shipments_for_line_item(line_item)
units = Spree::Stock::InventoryUnitBuilder.new(self).missing_units_for_line_item(line_item)
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|
shipments << shipment
end
Expand Down
3 changes: 2 additions & 1 deletion core/app/models/spree/stock/simple_coordinator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class SimpleCoordinator

def initialize(order, inventory_units = nil)
@order = order
@inventory_units = inventory_units || InventoryUnitBuilder.new(order).units
@inventory_units =
inventory_units || Spree::Config.stock.inventory_unit_builder_class.new(order).units
@splitters = Spree::Config.environment.stock_splitters

filtered_stock_locations = Spree::Config.stock.location_filter_class.new(Spree::StockLocation.all, @order).filter
Expand Down
6 changes: 6 additions & 0 deletions core/lib/spree/core/stock_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class StockConfiguration
attr_writer :location_filter_class
attr_writer :location_sorter_class
attr_writer :allocator_class
attr_writer :inventory_unit_builder_class

def coordinator_class
@coordinator_class ||= '::Spree::Stock::SimpleCoordinator'
Expand All @@ -33,6 +34,11 @@ def allocator_class
@allocator_class ||= '::Spree::Stock::Allocator::OnHandFirst'
@allocator_class.constantize
end

def inventory_unit_builder_class
@inventory_unit_builder_class ||= '::Spree::Stock::InventoryUnitBuilder'
@inventory_unit_builder_class.constantize
end
end
end
end
22 changes: 22 additions & 0 deletions core/spec/lib/spree/core/stock_configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,26 @@
end
end
end

describe '#inventory_unit_builder_class' do
subject { stock_configuration.inventory_unit_builder_class }

let(:stock_configuration) { described_class.new }

it "returns Spree::Stock::InventoryUnitBuilder" do
is_expected.to be ::Spree::Stock::InventoryUnitBuilder
end

context "with another constant name assigned" do
MyInventoryUnitBuilder = Class.new

before do
stock_configuration.inventory_unit_builder_class = MyInventoryUnitBuilder.to_s
end

it "returns the constant" do
expect(subject).to be MyInventoryUnitBuilder
end
end
end
end
36 changes: 36 additions & 0 deletions core/spec/models/spree/order_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,42 @@ def generate
it 'creates at least one new shipment for the order' do
expect { subject }.to change { order.shipments.count }.by 1
end

context "with a custom inventory unit builder" do
before do
class TestInventoryUnitBuilder
def initialize(order)
end

def missing_units_for_line_item(line_item)
[]
end
end

# This return value is stubbed this way, so that it can access the
# arbitrary_inventory_unit let variable.
allow_any_instance_of(TestInventoryUnitBuilder)
.to receive(:missing_units_for_line_item)
.and_return([arbitrary_inventory_unit])

# This is stubbed out rather than set, so that it doesn't leak out into
# other tests.
allow(Spree::Config.stock)
.to receive(:inventory_unit_builder_class)
.and_return(TestInventoryUnitBuilder)

order.shipments.first.update!(created_at: 2.years.ago)
end

let(:arbitrary_inventory_unit) { build :inventory_unit, order: order }

it "relies on the custom builder" do
expect { subject }.to change { order.shipments.count }.by 1

expect(order.shipments.order(:created_at).last.inventory_units)
.to contain_exactly arbitrary_inventory_unit
end
end
end

describe '#shipping_discount' do
Expand Down
8 changes: 8 additions & 0 deletions core/spec/models/spree/stock/simple_coordinator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ module Stock
subject.shipments.count == StockLocation.count
end

it 'uses the pluggable inventory unit builder class' do
expect(Spree::Config.stock)
.to receive(:inventory_unit_builder_class)
.and_call_original

subject.shipments
end

context "missing stock items in active stock location" do
let!(:another_location) { create(:stock_location, propagate_all_variants: false) }

Expand Down

0 comments on commit 2483b46

Please sign in to comment.