Bring stocck movement and stock location from spree

This commit is contained in:
Luis Ramos
2020-08-06 10:16:54 +01:00
parent c56962b949
commit d13bd86e4c
4 changed files with 357 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
module Spree
class StockLocation < ActiveRecord::Base
has_many :stock_items, dependent: :delete_all
has_many :stock_movements, through: :stock_items
belongs_to :state, class_name: 'Spree::State'
belongs_to :country, class_name: 'Spree::Country'
validates_presence_of :name
scope :active, -> { where(active: true) }
after_create :create_stock_items, :if => "self.propagate_all_variants?"
# Wrapper for creating a new stock item respecting the backorderable config
def propagate_variant(variant)
self.stock_items.create!(variant: variant, backorderable: self.backorderable_default)
end
# Return either an existing stock item or create a new one. Useful in
# scenarios where the user might not know whether there is already a stock
# item for a given variant
def set_up_stock_item(variant)
self.stock_item(variant) || propagate_variant(variant)
end
def stock_item(variant)
stock_items.where(variant_id: variant).order(:id).first
end
def stock_item_or_create(variant)
stock_item(variant) || stock_items.create(variant: variant)
end
def count_on_hand(variant)
stock_item(variant).try(:count_on_hand)
end
def backorderable?(variant)
stock_item(variant).try(:backorderable?)
end
def restock(variant, quantity, originator = nil)
move(variant, quantity, originator)
end
def unstock(variant, quantity, originator = nil)
move(variant, -quantity, originator)
end
def move(variant, quantity, originator = nil)
stock_item_or_create(variant).stock_movements.create!(quantity: quantity,
originator: originator)
end
def fill_status(variant, quantity)
if item = stock_item(variant)
if item.count_on_hand >= quantity
on_hand = quantity
backordered = 0
else
on_hand = item.count_on_hand
on_hand = 0 if on_hand < 0
backordered = item.backorderable? ? (quantity - on_hand) : 0
end
[on_hand, backordered]
else
[0, 0]
end
end
private
def create_stock_items
Variant.find_each { |variant| self.propagate_variant(variant) }
end
end
end

View File

@@ -0,0 +1,25 @@
module Spree
class StockMovement < ActiveRecord::Base
belongs_to :stock_item, class_name: 'Spree::StockItem'
belongs_to :originator, polymorphic: true
after_create :update_stock_item_quantity
validates :stock_item, presence: true
validates :quantity, presence: true
scope :recent, -> { order('created_at DESC') }
def readonly?
!new_record?
end
private
def update_stock_item_quantity
return unless Spree::Config[:track_inventory_levels]
stock_item.adjust_count_on_hand quantity
end
end
end

View File

@@ -0,0 +1,205 @@
require 'spec_helper'
module Spree
describe StockLocation do
subject { create(:stock_location_with_items, backorderable_default: true) }
let(:stock_item) { subject.stock_items.order(:id).first }
let(:variant) { stock_item.variant }
it 'creates stock_items for all variants' do
subject.stock_items.count.should eq Variant.count
end
context "handling stock items" do
let!(:variant) { create(:variant) }
context "given a variant" do
subject { StockLocation.create(name: "testing", propagate_all_variants: false) }
context "set up" do
it "creates stock item" do
subject.should_receive(:propagate_variant)
subject.set_up_stock_item(variant)
end
context "stock item exists" do
let!(:stock_item) { subject.propagate_variant(variant) }
it "returns existing stock item" do
subject.set_up_stock_item(variant).should == stock_item
end
end
end
context "propagate variants" do
let(:stock_item) { subject.propagate_variant(variant) }
it "creates a new stock item" do
expect {
subject.propagate_variant(variant)
}.to change{ StockItem.count }.by(1)
end
context "passes backorderable default config" do
context "true" do
before { subject.backorderable_default = true }
it { stock_item.backorderable.should be_true }
end
context "false" do
before { subject.backorderable_default = false }
it { stock_item.backorderable.should be_false }
end
end
end
context "propagate all variants" do
subject { StockLocation.new(name: "testing") }
context "true" do
before { subject.propagate_all_variants = true }
specify do
subject.should_receive(:propagate_variant).at_least(:once)
subject.save!
end
end
context "false" do
before { subject.propagate_all_variants = false }
specify do
subject.should_not_receive(:propagate_variant)
subject.save!
end
end
end
end
end
it 'finds a stock_item for a variant' do
stock_item = subject.stock_item(variant)
stock_item.count_on_hand.should eq 10
end
it 'finds a stock_item for a variant by id' do
stock_item = subject.stock_item(variant.id)
stock_item.variant.should eq variant
end
it 'returns nil when stock_item is not found for variant' do
stock_item = subject.stock_item(100)
stock_item.should be_nil
end
it 'creates a stock_item if not found for a variant' do
variant = create(:variant)
variant.stock_items.destroy_all
variant.save
stock_item = subject.stock_item_or_create(variant)
stock_item.variant.should eq variant
end
it 'finds a count_on_hand for a variant' do
subject.count_on_hand(variant).should eq 10
end
it 'finds determines if you a variant is backorderable' do
subject.backorderable?(variant).should be_true
end
it 'restocks a variant with a positive stock movement' do
originator = double
subject.should_receive(:move).with(variant, 5, originator)
subject.restock(variant, 5, originator)
end
it 'unstocks a variant with a negative stock movement' do
originator = double
subject.should_receive(:move).with(variant, -5, originator)
subject.unstock(variant, 5, originator)
end
it 'it creates a stock_movement' do
expect {
subject.move variant, 5
}.to change { subject.stock_movements.where(stock_item_id: stock_item).count }.by(1)
end
it 'can be deactivated' do
create(:stock_location, :active => true)
create(:stock_location, :active => false)
Spree::StockLocation.active.count.should eq 1
end
context 'fill_status' do
it 'all on_hand with no backordered' do
on_hand, backordered = subject.fill_status(variant, 5)
on_hand.should eq 5
backordered.should eq 0
end
it 'some on_hand with some backordered' do
on_hand, backordered = subject.fill_status(variant, 20)
on_hand.should eq 10
backordered.should eq 10
end
it 'zero on_hand with all backordered' do
zero_stock_item = mock_model(StockItem,
count_on_hand: 0,
backorderable?: true)
subject.should_receive(:stock_item).with(variant).and_return(zero_stock_item)
on_hand, backordered = subject.fill_status(variant, 20)
on_hand.should eq 0
backordered.should eq 20
end
context 'when backordering is not allowed' do
before do
@stock_item = mock_model(StockItem, backorderable?: false)
subject.should_receive(:stock_item).with(variant).and_return(@stock_item)
end
it 'all on_hand' do
@stock_item.stub(count_on_hand: 10)
on_hand, backordered = subject.fill_status(variant, 5)
on_hand.should eq 5
backordered.should eq 0
end
it 'some on_hand' do
@stock_item.stub(count_on_hand: 10)
on_hand, backordered = subject.fill_status(variant, 20)
on_hand.should eq 10
backordered.should eq 0
end
it 'zero on_hand' do
@stock_item.stub(count_on_hand: 0)
on_hand, backordered = subject.fill_status(variant, 20)
on_hand.should eq 0
backordered.should eq 0
end
end
context 'without stock_items' do
subject { create(:stock_location) }
let(:variant) { create(:base_variant) }
it 'zero on_hand and backordered', focus: true do
subject
variant.stock_items.destroy_all
on_hand, backordered = subject.fill_status(variant, 1)
on_hand.should eq 0
backordered.should eq 0
end
end
end
end
end

View File

@@ -0,0 +1,48 @@
require 'spec_helper'
describe Spree::StockMovement do
let(:stock_location) { create(:stock_location_with_items) }
let(:stock_item) { stock_location.stock_items.order(:id).first }
subject { build(:stock_movement, stock_item: stock_item) }
it 'should belong to a stock item' do
subject.should respond_to(:stock_item)
end
it 'is readonly unless new' do
subject.save
expect {
subject.save
}.to raise_error(ActiveRecord::ReadOnlyRecord)
end
it 'does not update count on hand when track inventory levels is false' do
Spree::Config[:track_inventory_levels] = false
subject.quantity = 1
subject.save
stock_item.reload
stock_item.count_on_hand.should == 10
end
context "when quantity is negative" do
context "after save" do
it "should decrement the stock item count on hand" do
subject.quantity = -1
subject.save
stock_item.reload
stock_item.count_on_hand.should == 9
end
end
end
context "when quantity is positive" do
context "after save" do
it "should increment the stock item count on hand" do
subject.quantity = 1
subject.save
stock_item.reload
stock_item.count_on_hand.should == 11
end
end
end
end