mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
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:
60
app/models/spree/stock_location.rb
Normal file
60
app/models/spree/stock_location.rb
Normal 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
|
||||
@@ -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
|
||||
25
app/models/spree/stock_movement.rb
Normal file
25
app/models/spree/stock_movement.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
6
spec/factories/stock_movement_factory.rb
Normal file
6
spec/factories/stock_movement_factory.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
FactoryBot.define do
|
||||
factory :stock_movement, class: Spree::StockMovement do
|
||||
quantity 1
|
||||
action 'sold'
|
||||
end
|
||||
end
|
||||
@@ -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 }
|
||||
|
||||
|
||||
104
spec/models/spree/stock_location_spec.rb
Normal file
104
spec/models/spree/stock_location_spec.rb
Normal 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
|
||||
42
spec/models/spree/stock_movement_spec.rb
Normal file
42
spec/models/spree/stock_movement_spec.rb
Normal 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
|
||||
Reference in New Issue
Block a user