From 133f5b827198ed43b0259ef34e1ebfd4738df459 Mon Sep 17 00:00:00 2001 From: Cassandra Wallace Date: Sat, 7 Mar 2026 14:00:19 +0200 Subject: [PATCH 1/4] Add validation to prevent zero quantity line items in purchases (#5507) --- app/models/purchase.rb | 5 +++++ spec/models/purchase_spec.rb | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/app/models/purchase.rb b/app/models/purchase.rb index 0b75156296..b86d6a153c 100644 --- a/app/models/purchase.rb +++ b/app/models/purchase.rb @@ -59,6 +59,7 @@ class Purchase < ApplicationRecord validates :amount_spent_in_cents, numericality: { greater_than: 0 } validate :total_equal_to_all_categories + validate :line_items_quantity_is_positive before_destroy :check_no_intervening_snapshot validates :amount_spent_on_diapers_cents, numericality: { greater_than_or_equal_to: 0 } @@ -140,4 +141,8 @@ def check_no_intervening_snapshot raise "We can't delete purchases entered before #{intervening.event_time.to_date}." end end + + def line_items_quantity_is_positive + line_items_quantity_is_at_least(1) + end end diff --git a/spec/models/purchase_spec.rb b/spec/models/purchase_spec.rb index 4dfee762a2..a68899a4ab 100644 --- a/spec/models/purchase_spec.rb +++ b/spec/models/purchase_spec.rb @@ -65,6 +65,13 @@ expect(d).not_to be_valid end + it "is not valid if any line item has zero quantity" do + item = create(:item) + p = build(:purchase) + p.line_items.build(item_id: item.id, quantity: 0) + expect(p).not_to be_valid + end + it "is valid if all categories are positive and add up to the total" do d = build(:purchase, amount_spent_in_cents: 1150, amount_spent_on_diapers_cents: 200, From e6cfb13205387a2c72532e2070aa66debd03058a Mon Sep 17 00:00:00 2001 From: Cassandra Wallace Date: Mon, 9 Mar 2026 22:50:46 +0200 Subject: [PATCH 2/4] Fix existing test that created purchase w/ 0 quantity items --- spec/queries/low_inventory_query_spec.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/queries/low_inventory_query_spec.rb b/spec/queries/low_inventory_query_spec.rb index fd7454e851..5462607b01 100644 --- a/spec/queries/low_inventory_query_spec.rb +++ b/spec/queries/low_inventory_query_spec.rb @@ -34,7 +34,10 @@ context "when minimum_quantity is 0 and recommended_quantity is nil and item quantity is 0" do let(:item) { create :item, organization: organization } let(:minimum_quantity) { 0 } - let(:inventory_item_quantity) { 0 } + + before do + TestInventory.create_inventory(organization, { storage_location.id => { item.id => 0 } }) + end it { is_expected.to eq [] } end From 03cd0710f6551e0957c7731fe286320cb790909a Mon Sep 17 00:00:00 2001 From: Cassandra Wallace Date: Mon, 9 Mar 2026 22:58:54 +0200 Subject: [PATCH 3/4] Add test for neg quantity in purchase line items (#5507) --- spec/models/purchase_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/models/purchase_spec.rb b/spec/models/purchase_spec.rb index a68899a4ab..8accae1383 100644 --- a/spec/models/purchase_spec.rb +++ b/spec/models/purchase_spec.rb @@ -72,6 +72,13 @@ expect(p).not_to be_valid end + it "is not valid if any line item has negative quantity" do + item = create(:item) + p = build(:purchase) + p.line_items.build(item_id: item.id, quantity: -1) + expect(p).not_to be_valid + end + it "is valid if all categories are positive and add up to the total" do d = build(:purchase, amount_spent_in_cents: 1150, amount_spent_on_diapers_cents: 200, From 644e2e50d4ccae939424e82d1590ea45f3c36373 Mon Sep 17 00:00:00 2001 From: Cassandra Wallace Date: Sun, 15 Mar 2026 23:32:25 +0200 Subject: [PATCH 4/4] Fix lint issue & add comment to LowInventoryQuery test --- spec/queries/low_inventory_query_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/queries/low_inventory_query_spec.rb b/spec/queries/low_inventory_query_spec.rb index 5462607b01..213e43d66d 100644 --- a/spec/queries/low_inventory_query_spec.rb +++ b/spec/queries/low_inventory_query_spec.rb @@ -35,8 +35,10 @@ let(:item) { create :item, organization: organization } let(:minimum_quantity) { 0 } + # Use TestInventory to set up inventory directly, instead of creating + # a purchase with 0 quantity items (which our validation now rejects). before do - TestInventory.create_inventory(organization, { storage_location.id => { item.id => 0 } }) + TestInventory.create_inventory(organization, {storage_location.id => {item.id => 0}}) end it { is_expected.to eq [] }