diff --git a/core/app/models/spree/stock_movement.rb b/core/app/models/spree/stock_movement.rb index baac5229050..b2fcb79a581 100644 --- a/core/app/models/spree/stock_movement.rb +++ b/core/app/models/spree/stock_movement.rb @@ -1,5 +1,10 @@ module Spree class StockMovement < Spree::Base + QUANTITY_LIMITS = { + max: 2**31 - 1, + min: -2**31 + }.freeze + belongs_to :stock_item, class_name: 'Spree::StockItem', inverse_of: :stock_movements belongs_to :originator, polymorphic: true @@ -8,11 +13,11 @@ class StockMovement < Spree::Base with_options presence: true do validates :stock_item validates :quantity, numericality: { - greater_than_or_equal_to: -2**31, - less_than_or_equal_to: 2**31 - 1, - only_integer: true, - allow_nil: true - } + greater_than_or_equal_to: QUANTITY_LIMITS[:min], + less_than_or_equal_to: QUANTITY_LIMITS[:max], + only_integer: true, + allow_nil: true + } end scope :recent, -> { order(created_at: :desc) } @@ -20,7 +25,7 @@ class StockMovement < Spree::Base self.whitelisted_ransackable_attributes = ['quantity'] def readonly? - !new_record? + persisted? end private diff --git a/core/spec/models/spree/stock_movement_spec.rb b/core/spec/models/spree/stock_movement_spec.rb index 33a178651b3..5abc7242f6c 100644 --- a/core/spec/models/spree/stock_movement_spec.rb +++ b/core/spec/models/spree/stock_movement_spec.rb @@ -1,55 +1,119 @@ require 'spec_helper' describe Spree::StockMovement, :type => :model 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) + describe 'Constants' do + describe 'QUANTITY_LIMITS[:max]' do + it 'return 2**31 - 1' do + expect(Spree::StockMovement::QUANTITY_LIMITS[:max]).to eq(2**31 - 1) + end + end + + describe 'QUANTITY_LIMITS[:min]' do + it 'return -2**31' do + expect(Spree::StockMovement::QUANTITY_LIMITS[:min]).to eq(-2**31) + end + end end - it 'is readonly unless new' do - subject.save - expect { - subject.save - }.to raise_error(ActiveRecord::ReadOnlyRecord) + describe 'Associations' do + it { is_expected.to belong_to(:stock_item).class_name('Spree::StockItem').inverse_of(:stock_movements) } + it { is_expected.to belong_to(:originator) } 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 - expect(stock_item.count_on_hand).to eq(10) + describe 'Validations' do + it do + is_expected.to validate_presence_of(:stock_item) + end + + it do + is_expected.to validate_presence_of(:quantity) + end + + it do + is_expected.to validate_numericality_of(:quantity). + is_greater_than_or_equal_to(Spree::StockMovement::QUANTITY_LIMITS[:min]). + is_less_than_or_equal_to(Spree::StockMovement::QUANTITY_LIMITS[:max]).only_integer.allow_nil + end end - it 'does not update count on hand when variant inventory tracking is off' do - stock_item.variant.track_inventory = false - subject.quantity = 1 - subject.save - stock_item.reload - expect(stock_item.count_on_hand).to eq(10) + describe 'Callbacks' do + it { is_expected.to callback(:update_stock_item_quantity).after(:create) } 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(9) + describe 'Scope' do + describe '.recent' do + it 'should order chronologically by created at' do + expect(Spree::StockMovement.recent.to_sql). + to eq Spree::StockMovement.unscoped.order(created_at: :desc).to_sql 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(11) + describe 'whitelisted ransackable attributes' do + it 'returns amount attribute' do + expect(Spree::StockMovement.whitelisted_ransackable_attributes).to eq(['quantity']) + end + end + + describe 'Insatance Methods' do + let(:stock_location) { create(:stock_location_with_items) } + let(:stock_item) { stock_location.stock_items.order(:id).first } + + describe '#readonly?' do + let(:stock_movement) { create(:stock_movement, stock_item: stock_item) } + it 'should not update a persisted record' do + expect { stock_movement.save }.to raise_error(ActiveRecord::ReadOnlyRecord) + end + end + + describe '#update_stock_item_quantity' do + let(:stock_movement) { build(:stock_movement, stock_item: stock_item) } + + context 'when track inventory levels is false' do + before do + Spree::Config[:track_inventory_levels] = false + stock_movement.quantity = 1 + stock_movement.save + stock_item.reload + end + it 'does not update count on hand' do + expect(stock_item.count_on_hand).to eq(10) + end + end + + context 'when track inventory tracking is off' do + before do + stock_item.variant.track_inventory = false + stock_movement.quantity = 1 + stock_movement.save + stock_item.reload + end + it 'does not update count on hand' do + expect(stock_item.count_on_hand).to eq(10) + end + end + + context 'when quantity is negative' do + before do + stock_movement.quantity = -1 + stock_movement.save + stock_item.reload + end + it 'should decrement the stock item count on hand' do + expect(stock_item.count_on_hand).to eq(9) + end + end + + context "when quantity is positive" do + before do + stock_movement.quantity = 1 + stock_movement.save + stock_item.reload + end + it "should increment the stock item count on hand" do + expect(stock_item.count_on_hand).to eq(11) + end end end end