diff --git a/app/models/variant_override.rb b/app/models/variant_override.rb index 89d2fdcd13..d28bb2771f 100644 --- a/app/models/variant_override.rb +++ b/app/models/variant_override.rb @@ -10,6 +10,8 @@ class VariantOverride < ActiveRecord::Base # Default stock can be nil, indicating stock should not be reset or zero, meaning reset to zero. Need to ensure this can be set by the user. validates :default_stock, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true + before_validation :require_compatible_on_demand_and_count_on_hand + after_save :refresh_products_cache_from_save after_destroy :refresh_products_cache_from_destroy @@ -104,4 +106,38 @@ class VariantOverride < ActiveRecord::Base def refresh_products_cache_from_destroy OpenFoodNetwork::ProductsCache.variant_override_destroyed self end + + def require_compatible_on_demand_and_count_on_hand + disallow_count_on_hand_if_using_producer_stock_settings + disallow_count_on_hand_if_on_demand + require_count_on_hand_if_limited_stock + end + + def disallow_count_on_hand_if_using_producer_stock_settings + return unless on_demand.nil? && count_on_hand.present? + + error_message = I18n.t("using_producer_stock_settings_but_count_on_hand_set", + scope: [i18n_scope_for_error, "count_on_hand"]) + errors.add(:count_on_hand, error_message) + end + + def disallow_count_on_hand_if_on_demand + return unless on_demand? && count_on_hand.present? + + error_message = I18n.t("on_demand_but_count_on_hand_set", + scope: [i18n_scope_for_error, "count_on_hand"]) + errors.add(:count_on_hand, error_message) + end + + def require_count_on_hand_if_limited_stock + return unless on_demand == false && count_on_hand.blank? + + error_message = I18n.t("limited_stock_but_no_count_on_hand", + scope: [i18n_scope_for_error, "count_on_hand"]) + errors.add(:count_on_hand, error_message) + end + + def i18n_scope_for_error + "activerecord.errors.models.variant_override" + end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 1b04166115..4962171a82 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -47,6 +47,11 @@ en: attributes: orders_close_at: after_orders_open_at: must be after open date + variant_override: + count_on_hand: + using_producer_stock_settings_but_count_on_hand_set: "must be blank because using producer stock settings" + on_demand_but_count_on_hand_set: "must be blank if on demand" + limited_stock_but_no_count_on_hand: "must be specified because forcing limited stock" activemodel: errors: models: diff --git a/spec/models/variant_override_spec.rb b/spec/models/variant_override_spec.rb index bb799f8603..258971e110 100644 --- a/spec/models/variant_override_spec.rb +++ b/spec/models/variant_override_spec.rb @@ -35,6 +35,81 @@ describe VariantOverride do end end + describe "validation" do + describe "ensuring that on_demand and count_on_hand are compatible" do + let(:variant_override) { build(:variant_override, hub: hub, variant: variant, + on_demand: on_demand, count_on_hand: count_on_hand) } + + context "when using producer stock settings" do + let(:on_demand) { nil } + + context "when count_on_hand is blank" do + let(:count_on_hand) { nil } + + it "is valid" do + expect(variant_override.save).to be_truthy + end + end + + context "when count_on_hand is set" do + let(:count_on_hand) { 1 } + + it "is invalid" do + expect(variant_override.save).to be_falsey + error_message = I18n.t("using_producer_stock_settings_but_count_on_hand_set", + scope: [i18n_scope_for_error, "count_on_hand"]) + expect(variant_override.errors[:count_on_hand]).to eq([error_message]) + end + end + end + + context "when on demand" do + let(:on_demand) { true } + + context "when count_on_hand is blank" do + let(:count_on_hand) { nil } + + it "is valid" do + expect(variant_override.save).to be_truthy + end + end + + context "when count_on_hand is set" do + let(:count_on_hand) { 1 } + + it "is invalid" do + expect(variant_override.save).to be_falsey + error_message = I18n.t("on_demand_but_count_on_hand_set", + scope: [i18n_scope_for_error, "count_on_hand"]) + expect(variant_override.errors[:count_on_hand]).to eq([error_message]) + end + end + end + + context "when limited stock" do + let(:on_demand) { false } + + context "when count_on_hand is blank" do + let(:count_on_hand) { nil } + + it "is invalid" do + expect(variant_override.save).to be_falsey + error_message = I18n.t("limited_stock_but_no_count_on_hand", + scope: [i18n_scope_for_error, "count_on_hand"]) + expect(variant_override.errors[:count_on_hand]).to eq([error_message]) + end + end + + context "when count_on_hand is set" do + let(:count_on_hand) { 1 } + + it "is valid" do + expect(variant_override.save).to be_truthy + end + end + end + end + end describe "callbacks" do let!(:vo) { create(:variant_override, hub: hub, variant: variant) } @@ -51,7 +126,6 @@ describe VariantOverride do end end - describe "looking up prices" do it "returns the numeric price when present" do VariantOverride.create!(variant: variant, hub: hub, price: 12.34) @@ -65,7 +139,7 @@ describe VariantOverride do describe "looking up count on hand" do it "returns the numeric stock level when present" do - VariantOverride.create!(variant: variant, hub: hub, count_on_hand: 12) + VariantOverride.create!(variant: variant, hub: hub, count_on_hand: 12, on_demand: false) VariantOverride.count_on_hand_for(hub, variant).should == 12 end @@ -76,12 +150,12 @@ describe VariantOverride do describe "checking if stock levels have been overriden" do it "returns true when stock level has been overridden" do - create(:variant_override, variant: variant, hub: hub, count_on_hand: 12) + create(:variant_override, variant: variant, hub: hub, on_demand: false, count_on_hand: 12) VariantOverride.stock_overridden?(hub, variant).should be true end it "returns false when the override has no stock level" do - create(:variant_override, variant: variant, hub: hub, count_on_hand: nil) + create(:variant_override, variant: variant, hub: hub, on_demand: nil, count_on_hand: nil) VariantOverride.stock_overridden?(hub, variant).should be false end @@ -157,4 +231,8 @@ describe VariantOverride do context "extends LocalizedNumber" do it_behaves_like "a model using the LocalizedNumber module", [:price] end + + def i18n_scope_for_error + "activerecord.errors.models.variant_override" + end end