Merge pull request #5867 from luisramos0/stock

[Bye bye Spree] Bring models stock_location and stock_movement from spree_core
This commit is contained in:
Luis Ramos
2020-09-03 18:35:55 +01:00
committed by GitHub
8 changed files with 250 additions and 18 deletions

View File

@@ -0,0 +1,60 @@
# frozen_string_literal: true
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 :name, presence: true
scope :active, -> { where(active: true) }
after_create :create_stock_items
# Wrapper for creating a new stock item respecting the backorderable config
def propagate_variant(variant)
stock_items.create!(variant: variant, backorderable: backorderable_default)
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)
variant.move(quantity, originator)
end
def fill_status(variant, quantity)
variant.fill_status(quantity)
end
private
def create_stock_items
Variant.find_each { |variant| propagate_variant(variant) }
end
end
end

View File

@@ -1,9 +0,0 @@
Spree::StockLocation.class_eval do
def move(variant, quantity, originator = nil)
variant.move(quantity, originator)
end
def fill_status(variant, quantity)
variant.fill_status(quantity)
end
end

View File

@@ -0,0 +1,25 @@
# frozen_string_literal: true
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
stock_item.adjust_count_on_hand quantity
end
end
end

View File

@@ -18,5 +18,17 @@ FactoryBot.define do
state do |stock_location|
stock_location.country.states.first || stock_location.association(:state, country: stock_location.country)
end
factory :stock_location_with_items do
after(:create) do |stock_location, evaluator|
# variant will add itself to all stock_locations in an after_create
# creating a product will automatically create a master variant
product_1 = create(:product)
product_2 = create(:product)
stock_location.stock_items.where(:variant_id => product_1.master.id).first.adjust_count_on_hand(10)
stock_location.stock_items.where(:variant_id => product_2.master.id).first.adjust_count_on_hand(20)
end
end
end
end

View File

@@ -0,0 +1,6 @@
FactoryBot.define do
factory :stock_movement, class: Spree::StockMovement do
quantity 1
action 'sold'
end
end

View File

@@ -3,15 +3,7 @@
require 'spec_helper'
RSpec.describe Spree::StockItem do
let(:stock_location) { create(:stock_location) }
before do
product_1 = create(:product)
product_2 = create(:product)
stock_location.stock_items.where(variant_id: product_1.master.id).first.adjust_count_on_hand(10)
stock_location.stock_items.where(variant_id: product_2.master.id).first.adjust_count_on_hand(20)
end
let(:stock_location) { create(:stock_location_with_items) }
subject { stock_location.stock_items.order(:id).first }

View File

@@ -0,0 +1,104 @@
# frozen_string_literal: true
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
expect(subject.stock_items.count).to eq Variant.count
end
context "handling stock items" do
let!(:variant) { create(:variant) }
context "given a variant" do
context "propagate all variants" do
subject { StockLocation.new(name: "testing") }
specify do
expect(subject).to receive(:propagate_variant).at_least(:once)
subject.save!
end
end
end
end
it 'finds a stock_item for a variant' do
stock_item = subject.stock_item(variant)
expect(stock_item.count_on_hand).to eq 15
end
it 'finds a stock_item for a variant by id' do
stock_item = subject.stock_item(variant.id)
expect(stock_item.variant).to eq variant
end
it 'returns nil when stock_item is not found for variant' do
stock_item = subject.stock_item(100)
expect(stock_item).to be_nil
end
it 'finds a count_on_hand for a variant' do
expect(subject.count_on_hand(variant)).to eq 15
end
it 'finds determines if you a variant is backorderable' do
expect(subject.backorderable?(variant)).to be_truthy
end
it 'restocks a variant with a positive stock movement' do
originator = double
expect(subject).to receive(:move).with(variant, 5, originator)
subject.restock(variant, 5, originator)
end
it 'unstocks a variant with a negative stock movement' do
originator = double
expect(subject).to receive(:move).with(variant, -5, originator)
subject.unstock(variant, 5, originator)
end
it 'it creates a stock_movement' do
variant.on_demand = false
expect {
subject.move variant, 5
}.to change { subject.stock_movements.where(stock_item_id: stock_item).count }.by(1)
end
context 'fill_status' do
before { variant.on_demand = false }
it 'is all on_hand if variant is on_demand' do
variant.on_demand = true
on_hand, backordered = subject.fill_status(variant, 25)
expect(on_hand).to eq 25
expect(backordered).to eq 0
end
it 'is all on_hand if on_hand is enough' do
on_hand, backordered = subject.fill_status(variant, 5)
expect(on_hand).to eq 5
expect(backordered).to eq 0
end
it 'is some on_hand if not all available' do
on_hand, backordered = subject.fill_status(variant, 20)
expect(on_hand).to eq 15
expect(backordered).to eq 0
end
it 'is zero on_hand if none available' do
variant.on_hand = 0
on_hand, backordered = subject.fill_status(variant, 20)
expect(on_hand).to eq 0
expect(backordered).to eq 0
end
end
end
end

View File

@@ -0,0 +1,42 @@
# frozen_string_literal: true
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
expect(subject).to respond_to(:stock_item)
end
it 'is readonly unless new' do
subject.save
expect {
subject.save
}.to raise_error(ActiveRecord::ReadOnlyRecord)
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
expect(stock_item.count_on_hand).to eq 14
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
expect(stock_item.count_on_hand).to eq 16
end
end
end
end