+ <%= form_for @storage_location, method: :get do %>
+ <%= label_tag "Date Range" %>
+
+
+ <%= render partial: "shared/date_range_picker", locals: {css_class: "form-control"} %>
+
+
+ <%= filter_button %>
+ <%= clear_filter_button %>
+
+
+ <% end %>
+
| Item |
- Quantity |
+ Quantity In |
+ Quantity Out |
+ Change |
- <%= render partial: "line_item_row", collection: @items_out %>
+ <%= render partial: "item_row", collection: @items %>
| Total |
- <%= @items_out_total %> |
+ <%= @total_quantity_in %> |
+ <%= @total_quantity_out %> |
+ <% css_class = @total_quantity_change.negative? ? 'modal-body-warning-text' : '' %>
+ <%= @total_quantity_change %> |
diff --git a/spec/queries/items_flow_query_spec.rb b/spec/queries/items_flow_query_spec.rb
new file mode 100644
index 0000000000..6634be8bff
--- /dev/null
+++ b/spec/queries/items_flow_query_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require "rspec"
+
+RSpec.describe ItemsFlowQuery do
+ let(:items) { create_list(:item, 2) }
+ let!(:storage_location) { create(:storage_location, name: "here") }
+ let(:organization) { storage_location.organization }
+ let!(:result) do
+ [
+ {
+ item_id: items[0].id,
+ item_name: items[0].name,
+ quantity_in: 10,
+ quantity_out: 5,
+ change: 5,
+ total_quantity_in: 19,
+ total_quantity_out: 7,
+ total_change: 12
+ },
+ {
+ item_id: items[1].id,
+ item_name: items[1].name,
+ quantity_in: 9,
+ quantity_out: 2,
+ change: 7,
+ total_quantity_in: 19,
+ total_quantity_out: 7,
+ total_change: 12
+ }
+ ].map(&:with_indifferent_access)
+ end
+
+ before do
+ create(:donation, :with_items, item: items[0], item_quantity: 10, storage_location: storage_location)
+ distribution = create(:distribution, :with_items, item: items[0], item_quantity: 5, storage_location: storage_location)
+ DistributionEvent.publish(distribution)
+ create(:donation, :with_items, item: items[1], item_quantity: 3, storage_location: storage_location)
+ adjustment = create(:adjustment, :with_items, item: items[1], item_quantity: 3, storage_location: storage_location)
+ AdjustmentEvent.publish(adjustment)
+ transfer = create(:transfer, :with_items, item: items[1], item_quantity: 2, from: storage_location, to: create(:storage_location))
+ TransferEvent.publish(transfer)
+ audit = create(:audit, :with_items, item: items[1], item_quantity: 3, adjustment: adjustment, storage_location: storage_location)
+ AuditEvent.publish(audit)
+ end
+
+ subject { described_class.new(organization: organization, storage_location: storage_location).call }
+
+ context "without filter params" do
+ it "returns array of hashes" do
+ expect(subject.to_a).to match_array(result)
+ end
+ end
+
+ context "with filter params" do
+ let(:filter_params) { [11.days.ago, 9.days.ago] }
+ let!(:old_items) { create_list(:item, 2) }
+ let(:other_location) { create(:storage_location, organization: organization) }
+
+ subject { described_class.new(organization: organization, storage_location: storage_location, filter_params: filter_params).call }
+
+ before do
+ create(:donation, :with_items, item: old_items[0], item_quantity: 10, storage_location: storage_location)
+ Event.last.update(created_at: 10.days.ago)
+ create(:donation, :with_items, item: old_items[1], item_quantity: 8, storage_location: storage_location)
+ distribution = create(:distribution, :with_items, item: old_items[1], item_quantity: 5, storage_location: storage_location)
+ DistributionEvent.publish(distribution)
+ Event.last.update(created_at: 10.days.ago)
+ end
+
+ let(:filtered_result) do
+ [
+ {
+ item_id: old_items[0].id,
+ item_name: old_items[0].name,
+ quantity_in: 10,
+ quantity_out: 0,
+ change: 10,
+ total_quantity_in: 10,
+ total_quantity_out: 5,
+ total_change: 5
+ },
+ {
+ item_id: old_items[1].id,
+ item_name: old_items[1].name,
+ quantity_in: 0,
+ quantity_out: 5,
+ change: -5,
+ total_quantity_in: 10,
+ total_quantity_out: 5,
+ total_change: 5
+ }
+ ].map(&:with_indifferent_access)
+ end
+
+ it "returns array of hashes" do
+ expect(subject.to_a).to match_array(filtered_result)
+ end
+ end
+end
diff --git a/spec/queries/items_in_query_spec.rb b/spec/queries/items_in_query_spec.rb
deleted file mode 100644
index 25632c92e7..0000000000
--- a/spec/queries/items_in_query_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# Spec for /app/queries/items_in_query.rb
-
-RSpec.describe ItemsInQuery do
- let(:organization) { create(:organization) }
- let!(:storage_location) { create(:storage_location, organization: organization) }
- let(:other_item) { create(:item, organization: organization) }
- let!(:other_storage_location) { create(:storage_location, :with_items, item: other_item, item_quantity: 10, organization: organization) }
-
- subject { ItemsInQuery.new(storage_location: storage_location, organization: organization).call }
-
- describe "items_in" do
- before do
- create_list(:donation, 2, :with_items, item: create(:item, organization: organization), item_quantity: 5, storage_location: storage_location, organization: organization)
- create_list(:purchase, 2, :with_items, item: create(:item, organization: organization), item_quantity: 5, storage_location: storage_location, organization: organization)
- create_list(:adjustment, 2, :with_items, item: create(:item, organization: organization), item_quantity: 5, storage_location: storage_location, organization: organization)
- create_list(:transfer, 2, :with_items, item_quantity: 5, item: other_item, from: other_storage_location, to: storage_location, organization: organization)
- end
-
- it "returns a collection with the fields name, item_id, quantity" do
- expect(subject.first).to be_respond_to(:name)
- expect(subject.first).to be_respond_to(:quantity)
- expect(subject.first).to be_respond_to(:item_id)
- end
-
- it "includes donations, purchases, adjustments, transfers among sources" do
- expect(subject.to_a.size).to eq(4)
- end
-
- it "does not count negative adjustments towards in-flow" do
- shared_item = create(:item, organization: organization)
- create(:donation, :with_items, item: shared_item, item_quantity: 10, storage_location: storage_location, organization: organization)
- create(:adjustment, :with_items, item: shared_item, item_quantity: -10, storage_location: storage_location, organization: organization)
- expect(subject.to_a.size).to eq(5)
- end
-
- it "does not count transfers going in the negative direction" do
- shared_item = create(:item, organization: organization)
- create(:donation, :with_items, item: shared_item, item_quantity: 10, storage_location: storage_location, organization: organization)
- create(:transfer, :with_items, item_quantity: 10, item: shared_item, from: storage_location, to: other_storage_location, organization: organization)
- expect(subject.to_a.size).to eq(5)
- end
- end
-end
diff --git a/spec/queries/items_in_total_query_spec.rb b/spec/queries/items_in_total_query_spec.rb
deleted file mode 100644
index 00c18b9bf7..0000000000
--- a/spec/queries/items_in_total_query_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# Spec for /app/queries/items_in_total_query.rb
-
-RSpec.describe ItemsInTotalQuery do
- let(:organization) { create(:organization) }
- let!(:storage_location) { create(:storage_location, organization: organization) }
- let(:other_item) { create(:item, organization: organization) }
- let!(:other_storage_location) { create(:storage_location, :with_items, item: other_item, item_quantity: 10, organization: organization) }
- let!(:shared_item) { create(:item, organization: organization) }
-
- subject { ItemsInTotalQuery.new(storage_location: storage_location, organization: organization).call }
-
- describe "items_in_total_query" do
- before do
- create(:donation, :with_items, item: create(:item, organization: organization), item_quantity: 10, storage_location: storage_location, organization: organization)
- create(:purchase, :with_items, item: create(:item, organization: organization), item_quantity: 10, storage_location: storage_location, organization: organization)
- create(:adjustment, :with_items, item: create(:item, organization: organization), item_quantity: 10, storage_location: storage_location, organization: organization)
- create(:transfer, :with_items, item_quantity: 10, item: other_item, from: other_storage_location, to: storage_location, organization: organization)
- end
-
- it "returns a sum total of all in-flows" do
- expect(subject).to eq(40)
- end
-
- it "counts shared items together" do
- create(:donation, :with_items, item: shared_item, item_quantity: 50, storage_location: storage_location, organization: organization)
- create(:purchase, :with_items, item: shared_item, item_quantity: 100, storage_location: storage_location, organization: organization)
- create(:adjustment, :with_items, item: shared_item, item_quantity: 50, storage_location: storage_location, organization: organization)
- create(:donation, :with_items, item: shared_item, item_quantity: 100, storage_location: storage_location, organization: organization)
- create(:purchase, :with_items, item: shared_item, item_quantity: 50, storage_location: storage_location, organization: organization)
- create(:adjustment, :with_items, item: shared_item, item_quantity: 100, storage_location: storage_location, organization: organization)
- expect(subject).to eq(490)
- end
-
- it "does not count negative adjustments towards in-flow" do
- create(:donation, :with_items, item: shared_item, item_quantity: 10, storage_location: storage_location, organization: organization)
- create(:adjustment, :with_items, item: shared_item, item_quantity: -10, storage_location: storage_location, organization: organization)
- expect(subject).to eq(50)
- end
-
- it "does not count transfers going in the negative direction" do
- create(:donation, :with_items, item: shared_item, item_quantity: 10, storage_location: storage_location, organization: organization)
- create(:transfer, :with_items, item_quantity: 10, item: shared_item, from: storage_location, to: other_storage_location, organization: organization)
- expect(subject).to eq(50)
- end
-
- it "does not count donations, purchases, adjustments to other storage locations" do
- create(:donation, :with_items, item: create(:item, organization: organization), item_quantity: 100, storage_location: other_storage_location, organization: organization)
- create(:purchase, :with_items, item: create(:item, organization: organization), item_quantity: 10, storage_location: other_storage_location, organization: organization)
- create(:adjustment, :with_items, item: create(:item, organization: organization), item_quantity: 10, storage_location: other_storage_location, organization: organization)
- expect(subject).to eq(40)
- end
- end
-end
diff --git a/spec/queries/items_out_query_spec.rb b/spec/queries/items_out_query_spec.rb
deleted file mode 100644
index 9bbe735ac7..0000000000
--- a/spec/queries/items_out_query_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# Spec for /app/queries/items_out_query.rb
-
-RSpec.describe ItemsOutQuery do
- let(:organization) { create(:organization) }
- let!(:storage_location) { create(:storage_location, organization: organization) }
- subject { ItemsOutQuery.new(storage_location: storage_location, organization: organization).call }
-
- describe "items_out_query" do
- before do
- items = create_list(:item, 3, organization: organization)
- TestInventory.create_inventory(storage_location.organization, {
- storage_location.id => items.to_h { |i| [i.id, 10] }
- })
- other_storage_location = create(:storage_location, organization: organization)
- create(:transfer, :with_items, item_quantity: 8, item: items[0], to: other_storage_location, from: storage_location, organization: organization)
- create(:distribution, :with_items, item: items[1], item_quantity: 9, storage_location: storage_location, organization: organization)
- create(:adjustment, :with_items, item: items[2], item_quantity: -10, storage_location: storage_location, organization: organization)
- end
-
- it "returns a collection with the fields name, item_id, quantity" do
- expect(subject.first).to be_respond_to(:name)
- expect(subject.first).to be_respond_to(:quantity)
- expect(subject.first).to be_respond_to(:item_id)
- end
-
- it "includes distributions, adjustments, transfers among sources" do
- expect(subject.to_a.size).to eq(3)
- end
- end
-end
diff --git a/spec/queries/items_out_total_query_spec.rb b/spec/queries/items_out_total_query_spec.rb
deleted file mode 100644
index deab96d3ed..0000000000
--- a/spec/queries/items_out_total_query_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# Spec for /app/queries/items_out_total_query.rb
-
-RSpec.describe ItemsOutTotalQuery do
- let(:organization) { create(:organization) }
- let!(:storage_location) { create(:storage_location, organization: organization) }
- subject { ItemsOutTotalQuery.new(storage_location: storage_location, organization: organization).call }
-
- describe "items_out_total_query" do
- before do
- items = create_list(:item, 3, organization: organization)
- TestInventory.create_inventory(storage_location.organization, {
- storage_location.id => items.to_h { |i| [i.id, 10] }
- })
- other_storage_location = create(:storage_location, organization: organization)
- create(:transfer, :with_items, item_quantity: 10, item: items[0], to: other_storage_location, from: storage_location, organization: organization)
- create(:distribution, :with_items, item: items[1], item_quantity: 10, storage_location: storage_location, organization: organization)
- create(:adjustment, :with_items, item: items[2], item_quantity: -10, storage_location: storage_location, organization: organization)
- end
-
- it "returns a sum total of all out-flows" do
- expect(subject).to eq(30)
- end
- end
-end
diff --git a/spec/system/storage_location_system_spec.rb b/spec/system/storage_location_system_spec.rb
index 662af5f3a3..e2d8b3b6b6 100644
--- a/spec/system/storage_location_system_spec.rb
+++ b/spec/system/storage_location_system_spec.rb
@@ -99,13 +99,6 @@
click_on "View", match: :first
- find("#custom-tabs-inventory-in-tab").click
-
- within "#custom-tabs-inventory-in" do
- expect(page).to have_content("Needle")
- expect(page).to have_content(100)
- end
-
find("#custom-tabs-inventory-tab").click
within "#custom-tabs-inventory" do
@@ -189,24 +182,104 @@
end
context "when viewing an existing storage location" do
- let(:item) { create(:item, name: "AAA Diapers") }
- let!(:storage_location) { create(:storage_location, :with_items, item: item, name: "here") }
- let!(:adjustment) { create(:adjustment, :with_items, storage_location: storage_location) }
+ let(:items) { create_list(:item, 2) }
+ let!(:storage_location) { create(:storage_location, name: "here") }
+ let(:result) do
+ [
+ {
+ item_id: items[0].id,
+ item_name: items[0].name,
+ quantity_in: 10,
+ quantity_out: 5,
+ change: 5,
+ total_quantity_in: 16,
+ total_quantity_out: 7,
+ total_change: 9
+ },
+ {
+ item_id: items[1].id,
+ item_name: items[1].name,
+ quantity_in: 6,
+ quantity_out: 2,
+ change: 4,
+ total_quantity_in: 16,
+ total_quantity_out: 7,
+ total_change: 9
+ }
+ ].map(&:with_indifferent_access)
+ end
subject { storage_location_path(storage_location.id) }
- it "Items in (adjustments)" do
- visit subject
- find("#custom-tabs-inventory-in-tab").click
-
- expect(page.find("#custom-tabs-inventory-in", visible: true)).to have_content "100"
- end
+ context "Inventory Flow Tab" do
+ before do
+ create(:donation, :with_items, item: items[0], item_quantity: 10, storage_location: storage_location)
+ distribution = create(:distribution, :with_items, item: items[0], item_quantity: 5, storage_location: storage_location)
+ DistributionEvent.publish(distribution)
+ create(:donation, :with_items, item: items[1], item_quantity: 3, storage_location: storage_location)
+ adjustment = create(:adjustment, :with_items, item: items[1], item_quantity: 3, storage_location: storage_location)
+ AdjustmentEvent.publish(adjustment)
+ transfer = create(:transfer, :with_items, item: items[1], item_quantity: 2, from: storage_location, to: create(:storage_location))
+ TransferEvent.publish(transfer)
+ visit subject
+ find("#custom-tabs-inventory-flow-tab").click
+ end
- it "Items out (distributions)" do
- create(:distribution, :with_items, storage_location: storage_location)
- visit subject
- find("#custom-tabs-inventory-out-tab").click
+ it "shows the inventory flow for the storage location" do
+ within("#custom-tabs-inventory-flow table tbody") do
+ result.each do |item|
+ row = find(:css, "tr[id='#{item[:item_id]}']")
+ change_column_css = item[:change].negative? ? "td.modal-body-warning-text" : "td"
+ expect(row).to have_link(item[:name], href: item_path(item[:item_id]))
+ expect(row).to have_css("td", text: item[:quantity_in])
+ expect(row).to have_css("td", text: item[:quantity_out])
+ expect(row).to have_css(change_column_css, text: item[:change])
+ end
+ end
+ within("#custom-tabs-inventory-flow table tfoot") do
+ expect(page).to have_css("td", text: "Total")
+ expect(page).to have_css("td", text: result.first[:total_quantity_in])
+ expect(page).to have_css("td", text: result.first[:total_quantity_out])
+ expect(page).to have_css("td", text: result.first[:total_change])
+ end
+ end
- expect(page.find("#custom-tabs-inventory-out", visible: true)).to have_content "100"
+ context "date range filter" do
+ let!(:start_date) { 2.days.ago }
+ let!(:end_date) { 1.day.ago }
+ let!(:item) { create(:item, name: "Filtered Item") }
+ let(:result) do
+ [
+ {
+ item_id: item.id,
+ item_name: item.name,
+ quantity_in: 10,
+ quantity_out: 0,
+ change: 10,
+ total_quantity_in: 10,
+ total_quantity_out: 0,
+ total_change: 10
+ }
+ ].map(&:with_indifferent_access)
+ end
+ before do
+ create(:donation, :with_items, item: item, item_quantity: 10, storage_location: storage_location)
+ Event.last.update(created_at: start_date)
+ fill_in "filters[date_range]", with: "#{start_date} - #{end_date}"
+ click_button "Filter"
+ find("#custom-tabs-inventory-flow-tab").click
+ end
+
+ it "filters the inventory flow by date range" do
+ within("#custom-tabs-inventory-flow table tbody") do
+ expect(page).to have_css("tr", count: 1)
+ row = find(:css, "tr[id='#{result.first[:item_id]}']")
+ expect(row).to have_link(result.first[:name], href: item_path(result.first[:item_id]))
+ expect(row).to have_css("td", text: result.first[:quantity_in])
+ expect(row).to have_css("td", text: result.first[:quantity_out])
+ expect(row).to have_css("td", text: result.first[:change])
+ end
+ end
+ end
end
end
end