From b7255130b6130c4da8bf9126e77542ff7f83be3f Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Wed, 1 Jul 2020 17:18:07 +0100 Subject: [PATCH] Bring Package from spree_core --- app/models/spree/stock/package.rb | 116 ++++++++++++++++++++++++ spec/models/spree/stock/package_spec.rb | 110 ++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 app/models/spree/stock/package.rb create mode 100644 spec/models/spree/stock/package_spec.rb diff --git a/app/models/spree/stock/package.rb b/app/models/spree/stock/package.rb new file mode 100644 index 0000000000..68ef8837ab --- /dev/null +++ b/app/models/spree/stock/package.rb @@ -0,0 +1,116 @@ +module Spree + module Stock + class Package + ContentItem = Struct.new(:variant, :quantity, :state) + + attr_reader :stock_location, :order, :contents + attr_accessor :shipping_rates + + def initialize(stock_location, order, contents=[]) + @stock_location = stock_location + @order = order + @contents = contents + @shipping_rates = Array.new + end + + def add(variant, quantity, state=:on_hand) + contents << ContentItem.new(variant, quantity, state) + end + + def weight + contents.sum { |item| item.variant.weight * item.quantity } + end + + def on_hand + contents.select { |item| item.state == :on_hand } + end + + def backordered + contents.select { |item| item.state == :backordered } + end + + def find_item(variant, state=:on_hand) + contents.select do |item| + item.variant == variant && + item.state == state + end.first + end + + def quantity(state=nil) + case state + when :on_hand + on_hand.sum { |item| item.quantity } + when :backordered + backordered.sum { |item| item.quantity } + else + contents.sum { |item| item.quantity } + end + end + + def empty? + quantity == 0 + end + + def flattened + flat = [] + contents.each do |item| + item.quantity.times do + flat << ContentItem.new(item.variant, 1, item.state) + end + end + flat + end + + def flattened=(flattened) + contents.clear + flattened.each do |item| + current_item = find_item(item.variant, item.state) + if current_item + current_item.quantity += 1 + else + add(item.variant, item.quantity, item.state) + end + end + end + + def currency + #TODO calculate from first variant? + end + + def shipping_categories + contents.map { |item| item.variant.shipping_category }.compact.uniq + end + + def shipping_methods + shipping_categories.map { |sc| sc.shipping_methods }.flatten.uniq + end + + def inspect + out = "#{order} - " + out << contents.map do |content_item| + "#{content_item.variant.name} #{content_item.quantity} #{content_item.state}" + end.join('/') + out + end + + def to_shipment + shipment = Spree::Shipment.new + shipment.order = order + shipment.stock_location = stock_location + shipment.shipping_rates = shipping_rates + + contents.each do |item| + item.quantity.times do |n| + unit = shipment.inventory_units.build + unit.pending = true + unit.order = order + unit.variant = item.variant + unit.state = item.state.to_s + end + end + + shipment + end + end + end +end diff --git a/spec/models/spree/stock/package_spec.rb b/spec/models/spree/stock/package_spec.rb new file mode 100644 index 0000000000..532ce23dae --- /dev/null +++ b/spec/models/spree/stock/package_spec.rb @@ -0,0 +1,110 @@ +require 'spec_helper' + +module Spree + module Stock + describe Package do + let(:variant) { build(:variant, weight: 25.0) } + let(:stock_location) { build(:stock_location) } + let(:order) { build(:order) } + + subject { Package.new(stock_location, order) } + + it 'calculates the weight of all the contents' do + subject.add variant, 4 + subject.weight.should == 100.0 + end + + it 'filters by on_hand and backordered' do + subject.add variant, 4, :on_hand + subject.add variant, 3, :backordered + subject.on_hand.count.should eq 1 + subject.backordered.count.should eq 1 + end + + it 'calculates the quantity by state' do + subject.add variant, 4, :on_hand + subject.add variant, 3, :backordered + + subject.quantity.should eq 7 + subject.quantity(:on_hand).should eq 4 + subject.quantity(:backordered).should eq 3 + end + + it 'returns nil for content item not found' do + item = subject.find_item(variant, :on_hand) + item.should be_nil + end + + it 'finds content item for a variant' do + subject.add variant, 4, :on_hand + item = subject.find_item(variant, :on_hand) + item.quantity.should eq 4 + end + + it 'get flattened contents' do + subject.add variant, 4, :on_hand + subject.add variant, 2, :backordered + flattened = subject.flattened + flattened.select { |i| i.state == :on_hand }.size.should eq 4 + flattened.select { |i| i.state == :backordered }.size.should eq 2 + end + + it 'set contents from flattened' do + flattened = [Package::ContentItem.new(variant, 1, :on_hand), + Package::ContentItem.new(variant, 1, :on_hand), + Package::ContentItem.new(variant, 1, :backordered), + Package::ContentItem.new(variant, 1, :backordered)] + + subject.flattened = flattened + subject.on_hand.size.should eq 1 + subject.on_hand.first.quantity.should eq 2 + + subject.backordered.size.should eq 1 + end + + # Contains regression test for #2804 + it 'builds a list of shipping methods from all categories' do + shipping_method1 = create(:shipping_method) + shipping_method2 = create(:shipping_method) + variant1 = mock_model(Variant, shipping_category: shipping_method1.shipping_categories.first) + variant2 = mock_model(Variant, shipping_category: shipping_method2.shipping_categories.first) + variant3 = mock_model(Variant, shipping_category: nil) + contents = [Package::ContentItem.new(variant1, 1), + Package::ContentItem.new(variant1, 1), + Package::ContentItem.new(variant2, 1), + Package::ContentItem.new(variant3, 1)] + + package = Package.new(stock_location, order, contents) + package.shipping_methods.size.should eq 2 + end + + + it "can convert to a shipment" do + flattened = [Package::ContentItem.new(variant, 2, :on_hand), + Package::ContentItem.new(variant, 1, :backordered)] + subject.flattened = flattened + + shipping_method = build(:shipping_method) + subject.shipping_rates = [ Spree::ShippingRate.new(shipping_method: shipping_method, cost: 10.00, selected: true) ] + + shipment = subject.to_shipment + shipment.order.should == subject.order + shipment.stock_location.should == subject.stock_location + shipment.inventory_units.size.should == 3 + + first_unit = shipment.inventory_units.first + first_unit.variant.should == variant + first_unit.state.should == 'on_hand' + first_unit.order.should == subject.order + first_unit.should be_pending + + last_unit = shipment.inventory_units.last + last_unit.variant.should == variant + last_unit.state.should == 'backordered' + last_unit.order.should == subject.order + + shipment.shipping_method.should eq shipping_method + end + end + end +end