Skip to content

Commit

Permalink
Trend reports are based on distribution/donation/purchase issued date (
Browse files Browse the repository at this point in the history
…#4769)

* Modify HistoricalTrendService to return quantity of distributed/donated/purchased items over the past year

* Modify HistoricalTrendService tests

* Refactor HistoricalTrendService
  • Loading branch information
jp524 authored Nov 21, 2024
1 parent 20cf220 commit a1d71fc
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 22 deletions.
40 changes: 25 additions & 15 deletions app/services/historical_trend_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,36 @@ def initialize(organization_id, type)
@type = type
end

# Returns: [{:name=>"Adult Briefs (XXL)", :data=>[0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0], :visible=>false}]
# :data contains quantity from 11 months ago to current month
def series
# Preload line_items with a single query to avoid N+1 queries.
items_with_line_items = @organization.items.active
.includes(:line_items)
.where(line_items: {itemizable_type: @type, created_at: 1.year.ago.beginning_of_month..Time.current})
.order(:name)
type_symbol = @type.tableize.to_sym # :distributions, :donations, :purchases
records_for_type = @organization.send(type_symbol)
.includes(items: :line_items)
.where(issued_at: 1.year.ago.beginning_of_month..Time.current)

month_offset = [*1..12].rotate(Time.zone.today.month)
default_dates = (1..12).index_with { |i| 0 }
array_of_items = []

items_with_line_items.each_with_object([]) do |item, array_of_items|
dates = default_dates.deep_dup
records_for_type.each do |record|
index = record.issued_at.month - Date.current.month - 1

item.line_items.each do |line_item|
month = line_item.created_at.month
index = month_offset.index(month) + 1
dates[index] = dates[index] + line_item.quantity
end
record.line_items.each do |line_item|
name = line_item.item.name
quantity = line_item.quantity
next if quantity.zero?

array_of_items << {name: item.name, data: dates.values, visible: false} unless dates.values.sum.zero?
existing_item = array_of_items.find { |item| item[:name] == name }
if existing_item
quantity_per_month = existing_item[:data]
quantity_per_month[index] += quantity
else
quantity_per_month = Array.new(12, 0)
quantity_per_month[index] += quantity
array_of_items << {name:, data: quantity_per_month, visible: false}
end
end
end

array_of_items.sort_by { |item| item[:name] }
end
end
21 changes: 14 additions & 7 deletions spec/services/historical_trend_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,29 @@
let(:service) { described_class.new(organization.id, type) }

describe "#series" do
let!(:item1) { create(:item, organization: organization, name: "Item 1") }
let!(:item2) { create(:item, organization: organization, name: "Item 2") }
let(:item1) { create(:item, organization: organization, name: "Item 1") }
let(:item2) { create(:item, organization: organization, name: "Item 2") }
let(:donation1) { create(:donation, organization:, issued_at: Date.current) }
let(:donation2) { create(:donation, organization:, issued_at: 2.months.ago) }
let!(:line_items) do
(0..11).map do |n|
create(:line_item, item: item1, itemizable_type: type, quantity: 10 * (n + 1), created_at: n.months.ago)
create(:line_item, item: item1, itemizable_type: type, itemizable_id: donation1.id, quantity: 10, created_at: n.months.ago)
end
end
let!(:line_item2) { create(:line_item, item: item2, itemizable_type: type, quantity: 60, created_at: 6.months.ago) }
let!(:line_item3) { create(:line_item, item: item2, itemizable_type: type, quantity: 30, created_at: 3.months.ago) }
let!(:line_item2) { create(:line_item, item: item2, itemizable_type: type, itemizable_id: donation2.id, quantity: 60, created_at: 6.months.ago) }
let!(:line_item3) { create(:line_item, item: item2, itemizable_type: type, itemizable_id: donation2.id, quantity: 30, created_at: 3.months.ago) }

it "returns an array of items with their monthly data" do
expected_result = [
{name: "Item 1", data: [120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10], visible: false},
{name: "Item 2", data: [0, 0, 0, 0, 0, 60, 0, 0, 30, 0, 0, 0], visible: false}
{name: "Item 1", data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120], visible: false},
{name: "Item 2", data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0], visible: false}
]
expect(service.series).to eq(expected_result)
end

it "the last data point is the quantity for the current month" do
item1_quantities = service.series.first[:data]
expect(item1_quantities.last).to be(line_items.pluck(:quantity).sum)
end
end
end

0 comments on commit a1d71fc

Please sign in to comment.