From effc729864349a5e5b09b1143624ccc1ffad677c Mon Sep 17 00:00:00 2001 From: Cory Streiff Date: Wed, 19 Feb 2025 13:40:13 +0100 Subject: [PATCH 1/7] Fix children served with kits calculation --- .../reports/children_served_report_service.rb | 16 +++++++++------- .../children_served_report_service_spec.rb | 16 ++++++++-------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app/services/reports/children_served_report_service.rb b/app/services/reports/children_served_report_service.rb index 70886d5e00..3c9dfad236 100644 --- a/app/services/reports/children_served_report_service.rb +++ b/app/services/reports/children_served_report_service.rb @@ -42,15 +42,17 @@ def total_children_served_with_loose_disposables .sum('line_items.quantity / COALESCE(items.distribution_quantity, 50)') end + # These joins look circular but are needed due to polymorphic relationships. + # A distribution has many line_items, items, and base_items but kits also + # have the same relationships and we want to perform calculations on the + # items in the kits not the kit items themselves. def children_served_with_kits_containing_disposables organization - .distributions - .for_year(year) - .joins(line_items: {item: :kit}) - .merge(Item.disposable) - .where.not(items: {kit_id: nil}) - .distinct - .count("kits.id") + .distributions + .for_year(year) + .joins(line_items: { item: {kit: {line_items: {item: :base_item}}}}) + .merge(Item.disposable) + .sum("line_items.quantity * line_items_kits.quantity / COALESCE(items_line_items.distribution_quantity, 50)") end end end diff --git a/spec/services/reports/children_served_report_service_spec.rb b/spec/services/reports/children_served_report_service_spec.rb index 84a05ec7f7..875351b06a 100644 --- a/spec/services/reports/children_served_report_service_spec.rb +++ b/spec/services/reports/children_served_report_service_spec.rb @@ -32,8 +32,8 @@ create(:base_item, name: "Toddler Disposable Diaper", partner_key: "toddler diapers", category: "disposable diaper") create(:base_item, name: "Infant Disposable Diaper", partner_key: "infant diapers", category: "infant disposable diaper") - toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers", kit: kit) - infant_disposable_kit_item = create(:item, name: "Infant Disposable Diapers", partner_key: "infant diapers", kit: kit) + toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers", kit: kit, distribution_quantity: 2) + infant_disposable_kit_item = create(:item, name: "Infant Disposable Diapers", partner_key: "infant diapers", kit: kit, distribution_quantity: 1) # Distributions distributions = create_list(:distribution, 2, issued_at: within_time, organization: organization) @@ -53,8 +53,8 @@ expect(report).to eq({ name: 'Children Served', entries: { - 'Average children served monthly' => "8", - 'Total children served' => "101", + 'Average children served monthly' => "10", + 'Total children served' => "115", # 100 normal and 15 from kits 'Repackages diapers?' => 'Y', 'Monthly diaper distributions?' => 'Y' } @@ -90,15 +90,15 @@ infant_distribution = create(:distribution, organization: organization, issued_at: within_time) toddler_distribution = create(:distribution, organization: organization, issued_at: within_time) - create(:line_item, :distribution, quantity: 10, item: toddler_disposable_kit_item, itemizable: infant_distribution) - create(:line_item, :distribution, quantity: 10, item: infant_disposable_kit_item, itemizable: toddler_distribution) + create(:line_item, :distribution, quantity: 100, item: toddler_disposable_kit_item, itemizable: infant_distribution) + create(:line_item, :distribution, quantity: 200, item: infant_disposable_kit_item, itemizable: toddler_distribution) report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({ name: 'Children Served', entries: { - 'Average children served monthly' => "3", - 'Total children served' => "41", + 'Average children served monthly' => "4", + 'Total children served' => "46", # 40 normal and 6 from kits 'Repackages diapers?' => 'Y', 'Monthly diaper distributions?' => 'Y' } From 41bc8fbdf040cdf338f6668d13e0c737935b5c76 Mon Sep 17 00:00:00 2001 From: Cory Streiff Date: Wed, 19 Feb 2025 17:17:58 +0100 Subject: [PATCH 2/7] Count kits as 1 child served --- app/services/reports/children_served_report_service.rb | 6 +++--- .../reports/children_served_report_service_spec.rb | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/services/reports/children_served_report_service.rb b/app/services/reports/children_served_report_service.rb index 3c9dfad236..2077e87ffc 100644 --- a/app/services/reports/children_served_report_service.rb +++ b/app/services/reports/children_served_report_service.rb @@ -38,7 +38,7 @@ def total_children_served_with_loose_disposables .distributions .for_year(year) .joins(line_items: :item) - .merge(Item.disposable) + .merge(Item.loose.disposable) .sum('line_items.quantity / COALESCE(items.distribution_quantity, 50)') end @@ -50,9 +50,9 @@ def children_served_with_kits_containing_disposables organization .distributions .for_year(year) - .joins(line_items: { item: {kit: {line_items: {item: :base_item}}}}) + .joins(line_items: { item: :kit }) .merge(Item.disposable) - .sum("line_items.quantity * line_items_kits.quantity / COALESCE(items_line_items.distribution_quantity, 50)") + .count("kits.id") end end end diff --git a/spec/services/reports/children_served_report_service_spec.rb b/spec/services/reports/children_served_report_service_spec.rb index 875351b06a..0b3c924b04 100644 --- a/spec/services/reports/children_served_report_service_spec.rb +++ b/spec/services/reports/children_served_report_service_spec.rb @@ -48,13 +48,14 @@ create(:line_item, :distribution, quantity: 10, item: toddler_disposable_kit_item, itemizable: infant_distribution) create(:line_item, :distribution, quantity: 10, item: infant_disposable_kit_item, itemizable: toddler_distribution) + create(:line_item, :distribution, quantity: 10, item: toddler_disposable_kit_item, itemizable: toddler_distribution) report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({ name: 'Children Served', entries: { - 'Average children served monthly' => "10", - 'Total children served' => "115", # 100 normal and 15 from kits + 'Average children served monthly' => "9", + 'Total children served' => "103", # 100 normal and 3 from kits 'Repackages diapers?' => 'Y', 'Monthly diaper distributions?' => 'Y' } @@ -91,6 +92,7 @@ toddler_distribution = create(:distribution, organization: organization, issued_at: within_time) create(:line_item, :distribution, quantity: 100, item: toddler_disposable_kit_item, itemizable: infant_distribution) + create(:line_item, :distribution, quantity: 200, item: toddler_disposable_kit_item, itemizable: toddler_distribution) create(:line_item, :distribution, quantity: 200, item: infant_disposable_kit_item, itemizable: toddler_distribution) report = described_class.new(organization: organization, year: within_time.year).report @@ -98,7 +100,7 @@ name: 'Children Served', entries: { 'Average children served monthly' => "4", - 'Total children served' => "46", # 40 normal and 6 from kits + 'Total children served' => "43", # 40 normal and 3 from kits 'Repackages diapers?' => 'Y', 'Monthly diaper distributions?' => 'Y' } From fca5a035b21672c2f9f1c0eec6b056e7992b4746 Mon Sep 17 00:00:00 2001 From: Cory Streiff Date: Tue, 25 Feb 2025 16:32:30 +0100 Subject: [PATCH 3/7] Fix calculation --- .../reports/children_served_report_service.rb | 8 ++++--- .../children_served_report_service_spec.rb | 24 +++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app/services/reports/children_served_report_service.rb b/app/services/reports/children_served_report_service.rb index 2077e87ffc..2b69afae14 100644 --- a/app/services/reports/children_served_report_service.rb +++ b/app/services/reports/children_served_report_service.rb @@ -50,9 +50,11 @@ def children_served_with_kits_containing_disposables organization .distributions .for_year(year) - .joins(line_items: { item: :kit }) - .merge(Item.disposable) - .count("kits.id") + .joins(line_items: { item: { kit: { line_items: {item: :base_item} }}}) + .where("base_items.category ILIKE '%diaper%' AND + NOT base_items.category ILIKE '%cloth%' OR base_items.name ILIKE '%cloth%' AND + NOT base_items.category ILIKE '%adult%'") + .sum("line_items.quantity / COALESCE(items.distribution_quantity, 1)") end end end diff --git a/spec/services/reports/children_served_report_service_spec.rb b/spec/services/reports/children_served_report_service_spec.rb index 0b3c924b04..326f8d0ed2 100644 --- a/spec/services/reports/children_served_report_service_spec.rb +++ b/spec/services/reports/children_served_report_service_spec.rb @@ -32,8 +32,8 @@ create(:base_item, name: "Toddler Disposable Diaper", partner_key: "toddler diapers", category: "disposable diaper") create(:base_item, name: "Infant Disposable Diaper", partner_key: "infant diapers", category: "infant disposable diaper") - toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers", kit: kit, distribution_quantity: 2) - infant_disposable_kit_item = create(:item, name: "Infant Disposable Diapers", partner_key: "infant diapers", kit: kit, distribution_quantity: 1) + toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers", kit:, distribution_quantity: 2, organization:) + infant_disposable_kit_item = create(:item, name: "Infant Disposable Diapers", partner_key: "infant diapers", kit:, organization:) # Distributions distributions = create_list(:distribution, 2, issued_at: within_time, organization: organization) @@ -46,16 +46,16 @@ infant_distribution = create(:distribution, organization: organization, issued_at: within_time) toddler_distribution = create(:distribution, organization: organization, issued_at: within_time) - create(:line_item, :distribution, quantity: 10, item: toddler_disposable_kit_item, itemizable: infant_distribution) - create(:line_item, :distribution, quantity: 10, item: infant_disposable_kit_item, itemizable: toddler_distribution) - create(:line_item, :distribution, quantity: 10, item: toddler_disposable_kit_item, itemizable: toddler_distribution) + create(:line_item, quantity: 10, item: toddler_disposable_kit_item, itemizable: infant_distribution) + create(:line_item, quantity: 10, item: infant_disposable_kit_item, itemizable: toddler_distribution) + create(:line_item, quantity: 10, item: toddler_disposable_kit_item, itemizable: toddler_distribution) report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({ name: 'Children Served', entries: { - 'Average children served monthly' => "9", - 'Total children served' => "103", # 100 normal and 3 from kits + 'Average children served monthly' => "10", + 'Total children served' => "120", # 100 normal and 20 from kits (10 infant, 10 toddler) 'Repackages diapers?' => 'Y', 'Monthly diaper distributions?' => 'Y' } @@ -91,16 +91,16 @@ infant_distribution = create(:distribution, organization: organization, issued_at: within_time) toddler_distribution = create(:distribution, organization: organization, issued_at: within_time) - create(:line_item, :distribution, quantity: 100, item: toddler_disposable_kit_item, itemizable: infant_distribution) - create(:line_item, :distribution, quantity: 200, item: toddler_disposable_kit_item, itemizable: toddler_distribution) - create(:line_item, :distribution, quantity: 200, item: infant_disposable_kit_item, itemizable: toddler_distribution) + create(:line_item, :distribution, quantity: 10, item: toddler_disposable_kit_item, itemizable: infant_distribution) + create(:line_item, :distribution, quantity: 20, item: toddler_disposable_kit_item, itemizable: toddler_distribution) + create(:line_item, :distribution, quantity: 30, item: infant_disposable_kit_item, itemizable: toddler_distribution) report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({ name: 'Children Served', entries: { - 'Average children served monthly' => "4", - 'Total children served' => "43", # 40 normal and 3 from kits + 'Average children served monthly' => "8", + 'Total children served' => "100", # 40 normal and 60 from kits 'Repackages diapers?' => 'Y', 'Monthly diaper distributions?' => 'Y' } From 5be4c98141ce978017db83bd4f94085efbbe5e28 Mon Sep 17 00:00:00 2001 From: Cory Streiff Date: Thu, 27 Feb 2025 15:35:10 +0100 Subject: [PATCH 4/7] Change average children monthly to 2 decimal float and round children served to int ceiling --- .../reports/children_served_report_service.rb | 8 +- .../children_served_report_service_spec.rb | 85 +++++++++++++------ 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/app/services/reports/children_served_report_service.rb b/app/services/reports/children_served_report_service.rb index 2b69afae14..b1f0239a08 100644 --- a/app/services/reports/children_served_report_service.rb +++ b/app/services/reports/children_served_report_service.rb @@ -14,7 +14,7 @@ def initialize(year:, organization:) def report @report ||= { name: 'Children Served', entries: { - 'Average children served monthly' => number_with_delimiter(average_children_monthly.round), + 'Average children served monthly' => number_with_delimiter(average_children_monthly.round(2)), 'Total children served' => number_with_delimiter(total_children_served), 'Repackages diapers?' => organization.repackage_essentials? ? 'Y' : 'N', 'Monthly diaper distributions?' => organization.distribute_monthly? ? 'Y' : 'N' @@ -39,7 +39,8 @@ def total_children_served_with_loose_disposables .for_year(year) .joins(line_items: :item) .merge(Item.loose.disposable) - .sum('line_items.quantity / COALESCE(items.distribution_quantity, 50)') + .pick(Arel.sql("CEILING(SUM(line_items.quantity::numeric / COALESCE(items.distribution_quantity, 50)))")) + .to_i end # These joins look circular but are needed due to polymorphic relationships. @@ -54,7 +55,8 @@ def children_served_with_kits_containing_disposables .where("base_items.category ILIKE '%diaper%' AND NOT base_items.category ILIKE '%cloth%' OR base_items.name ILIKE '%cloth%' AND NOT base_items.category ILIKE '%adult%'") - .sum("line_items.quantity / COALESCE(items.distribution_quantity, 1)") + .pick(Arel.sql("CEILING(SUM(line_items.quantity::numeric / COALESCE(items.distribution_quantity, 1)))")) + .to_i end end end diff --git a/spec/services/reports/children_served_report_service_spec.rb b/spec/services/reports/children_served_report_service_spec.rb index 326f8d0ed2..da63d1f06b 100644 --- a/spec/services/reports/children_served_report_service_spec.rb +++ b/spec/services/reports/children_served_report_service_spec.rb @@ -1,27 +1,26 @@ RSpec.describe Reports::ChildrenServedReportService, type: :service do let(:year) { 2020 } + let(:within_time) { Time.zone.parse("2020-05-31 14:00:00") } + let(:outside_time) { Time.zone.parse("2019-05-31 14:00:00") } describe '#report' do it 'should report zero values' do organization = create(:organization) report = described_class.new(organization: organization, year: year).report expect(report).to eq({ - name: 'Children Served', - entries: { - 'Average children served monthly' => "0", - 'Total children served' => "0", - 'Repackages diapers?' => 'N', - 'Monthly diaper distributions?' => 'N' - } - }) + name: 'Children Served', + entries: { + 'Average children served monthly' => "0.0", + 'Total children served' => "0", + 'Repackages diapers?' => 'N', + 'Monthly diaper distributions?' => 'N' + } + }) end it 'should report normal values' do organization = create(:organization, :with_items, distribute_monthly: true, repackage_essentials: true) - within_time = Time.zone.parse("2020-05-31 14:00:00") - outside_time = Time.zone.parse("2019-05-31 14:00:00") - disposable_item = organization.items.disposable.first disposable_item.update!(distribution_quantity: 20) non_disposable_item = organization.items.where.not(id: organization.items.disposable).first @@ -52,14 +51,14 @@ report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({ - name: 'Children Served', - entries: { - 'Average children served monthly' => "10", - 'Total children served' => "120", # 100 normal and 20 from kits (10 infant, 10 toddler) - 'Repackages diapers?' => 'Y', - 'Monthly diaper distributions?' => 'Y' - } - }) + name: 'Children Served', + entries: { + 'Average children served monthly' => "10.0", + 'Total children served' => "120", # 100 normal and 20 from kits (10 infant, 10 toddler) + 'Repackages diapers?' => 'Y', + 'Monthly diaper distributions?' => 'Y' + } + }) end it 'should work with no distribution_quantity' do @@ -97,14 +96,46 @@ report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({ - name: 'Children Served', - entries: { - 'Average children served monthly' => "8", - 'Total children served' => "100", # 40 normal and 60 from kits - 'Repackages diapers?' => 'Y', - 'Monthly diaper distributions?' => 'Y' - } - }) + name: 'Children Served', + entries: { + 'Average children served monthly' => "8.33", + 'Total children served' => "100", # 40 normal and 60 from kits + 'Repackages diapers?' => 'Y', + 'Monthly diaper distributions?' => 'Y' + } + }) + end + + it "rounds children served to integer ceiling" do + organization = create(:organization, :with_items) + + disposable_item = organization.items.disposable.first + disposable_item.update!(distribution_quantity: 20) + + # Kits + kit = create(:kit, organization: organization) + create(:base_item, name: "Toddler Disposable Diaper", partner_key: "toddler diapers", category: "disposable diaper") + toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers", kit:, organization:, distribution_quantity: 10) + + # Distributions + distributions = create_list(:distribution, 2, issued_at: within_time, organization: organization) + distributions.each do |dist| + create_list(:line_item, 2, :distribution, quantity: 6, item: disposable_item, itemizable: dist) + end + + toddler_distribution = create(:distribution, organization: organization, issued_at: within_time) + create(:line_item, quantity: 1, item: toddler_disposable_kit_item, itemizable: toddler_distribution) + + report = described_class.new(organization: organization, year: within_time.year).report + expect(report).to eq({ + name: "Children Served", + entries: { + "Average children served monthly" => "0.25", + "Total children served" => "3", # 1 / 10 = 0.1 rounds to 1 child from kits + 6 / 20 * 4 = 1.25 rounds to 2 children from nonkits + "Repackages diapers?" => "N", + "Monthly diaper distributions?" => "N" + } + }) end end end From 250f63688572edb7828ba33ff3582cd4a8139a5f Mon Sep 17 00:00:00 2001 From: Cory Streiff Date: Thu, 27 Feb 2025 15:48:35 +0100 Subject: [PATCH 5/7] Move location of round call --- .../reports/children_served_report_service.rb | 4 ++-- .../children_served_report_service_spec.rb | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/services/reports/children_served_report_service.rb b/app/services/reports/children_served_report_service.rb index b1f0239a08..bcf79e021c 100644 --- a/app/services/reports/children_served_report_service.rb +++ b/app/services/reports/children_served_report_service.rb @@ -14,7 +14,7 @@ def initialize(year:, organization:) def report @report ||= { name: 'Children Served', entries: { - 'Average children served monthly' => number_with_delimiter(average_children_monthly.round(2)), + 'Average children served monthly' => number_with_delimiter(average_children_monthly), 'Total children served' => number_with_delimiter(total_children_served), 'Repackages diapers?' => organization.repackage_essentials? ? 'Y' : 'N', 'Monthly diaper distributions?' => organization.distribute_monthly? ? 'Y' : 'N' @@ -28,7 +28,7 @@ def total_children_served # @return [Float] def average_children_monthly - total_children_served / 12.0 + (total_children_served / 12.0).round(2) end private diff --git a/spec/services/reports/children_served_report_service_spec.rb b/spec/services/reports/children_served_report_service_spec.rb index da63d1f06b..ae57721bb1 100644 --- a/spec/services/reports/children_served_report_service_spec.rb +++ b/spec/services/reports/children_served_report_service_spec.rb @@ -128,14 +128,14 @@ report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({ - name: "Children Served", - entries: { - "Average children served monthly" => "0.25", - "Total children served" => "3", # 1 / 10 = 0.1 rounds to 1 child from kits + 6 / 20 * 4 = 1.25 rounds to 2 children from nonkits - "Repackages diapers?" => "N", - "Monthly diaper distributions?" => "N" - } - }) + name: "Children Served", + entries: { + "Average children served monthly" => "0.25", + "Total children served" => "3", # 1 / 10 = 0.1 rounds to 1 child from kits + 6 / 20 * 4 = 1.25 rounds to 2 children from nonkits + "Repackages diapers?" => "N", + "Monthly diaper distributions?" => "N" + } + }) end end end From db3073333c3ca72a08fef4a66b0ce1204a080275 Mon Sep 17 00:00:00 2001 From: Cory Streiff Date: Thu, 27 Feb 2025 16:57:31 +0100 Subject: [PATCH 6/7] Fix group by clause --- .../reports/children_served_report_service.rb | 3 +- .../children_served_report_service_spec.rb | 78 +++++++++++-------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/app/services/reports/children_served_report_service.rb b/app/services/reports/children_served_report_service.rb index bcf79e021c..8f29094b71 100644 --- a/app/services/reports/children_served_report_service.rb +++ b/app/services/reports/children_served_report_service.rb @@ -55,8 +55,9 @@ def children_served_with_kits_containing_disposables .where("base_items.category ILIKE '%diaper%' AND NOT base_items.category ILIKE '%cloth%' OR base_items.name ILIKE '%cloth%' AND NOT base_items.category ILIKE '%adult%'") + .group("line_items_kits.id") .pick(Arel.sql("CEILING(SUM(line_items.quantity::numeric / COALESCE(items.distribution_quantity, 1)))")) - .to_i + .to_i end end end diff --git a/spec/services/reports/children_served_report_service_spec.rb b/spec/services/reports/children_served_report_service_spec.rb index ae57721bb1..ac535b9b82 100644 --- a/spec/services/reports/children_served_report_service_spec.rb +++ b/spec/services/reports/children_served_report_service_spec.rb @@ -26,13 +26,18 @@ non_disposable_item = organization.items.where.not(id: organization.items.disposable).first # Kits - kit = create(:kit, organization: organization) - create(:base_item, name: "Toddler Disposable Diaper", partner_key: "toddler diapers", category: "disposable diaper") create(:base_item, name: "Infant Disposable Diaper", partner_key: "infant diapers", category: "infant disposable diaper") - toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers", kit:, distribution_quantity: 2, organization:) - infant_disposable_kit_item = create(:item, name: "Infant Disposable Diapers", partner_key: "infant diapers", kit:, organization:) + toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers") + infant_disposable_kit_item = create(:item, name: "Infant Disposable Diapers", partner_key: "infant diapers") + + kit = create(:kit, organization: organization, line_items: [ + create(:line_item, item: toddler_disposable_kit_item), + create(:line_item, item: infant_disposable_kit_item) + ]) + + create(:item, name: "Kit 1", kit:, organization:) # Distributions distributions = create_list(:distribution, 2, issued_at: within_time, organization: organization) @@ -45,16 +50,15 @@ infant_distribution = create(:distribution, organization: organization, issued_at: within_time) toddler_distribution = create(:distribution, organization: organization, issued_at: within_time) - create(:line_item, quantity: 10, item: toddler_disposable_kit_item, itemizable: infant_distribution) - create(:line_item, quantity: 10, item: infant_disposable_kit_item, itemizable: toddler_distribution) - create(:line_item, quantity: 10, item: toddler_disposable_kit_item, itemizable: toddler_distribution) + create(:line_item, quantity: 10, item: kit.item, itemizable: infant_distribution) + create(:line_item, quantity: 10, item: kit.item, itemizable: toddler_distribution) report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({ name: 'Children Served', entries: { 'Average children served monthly' => "10.0", - 'Total children served' => "120", # 100 normal and 20 from kits (10 infant, 10 toddler) + 'Total children served' => "120", # 100 normal and 20 from kits 'Repackages diapers?' => 'Y', 'Monthly diaper distributions?' => 'Y' } @@ -71,13 +75,18 @@ non_disposable_item = organization.items.where.not(id: organization.items.disposable).first # Kits - kit = create(:kit, organization: organization) - create(:base_item, name: "Toddler Disposable Diaper", partner_key: "toddler diapers", category: "disposable diaper") create(:base_item, name: "Infant Disposable Diaper", partner_key: "infant diapers", category: "infant disposable diaper") - toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers", kit: kit) - infant_disposable_kit_item = create(:item, name: "Infant Disposable Diapers", partner_key: "infant diapers", kit: kit) + toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers") + infant_disposable_kit_item = create(:item, name: "Infant Disposable Diapers", partner_key: "infant diapers") + + kit = create(:kit, organization: organization, line_items: [ + create(:line_item, item: toddler_disposable_kit_item), + create(:line_item, item: infant_disposable_kit_item) + ]) + + create(:item, name: "Kit 1", kit:, organization:) # Distributions distributions = create_list(:distribution, 2, issued_at: within_time, organization: organization) @@ -90,16 +99,15 @@ infant_distribution = create(:distribution, organization: organization, issued_at: within_time) toddler_distribution = create(:distribution, organization: organization, issued_at: within_time) - create(:line_item, :distribution, quantity: 10, item: toddler_disposable_kit_item, itemizable: infant_distribution) - create(:line_item, :distribution, quantity: 20, item: toddler_disposable_kit_item, itemizable: toddler_distribution) - create(:line_item, :distribution, quantity: 30, item: infant_disposable_kit_item, itemizable: toddler_distribution) + create(:line_item, quantity: 10, item: kit.item, itemizable: infant_distribution) + create(:line_item, quantity: 10, item: kit.item, itemizable: toddler_distribution) report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({ name: 'Children Served', entries: { - 'Average children served monthly' => "8.33", - 'Total children served' => "100", # 40 normal and 60 from kits + 'Average children served monthly' => "5.0", + 'Total children served' => "60", # 40 normal and 20 from kits 'Repackages diapers?' => 'Y', 'Monthly diaper distributions?' => 'Y' } @@ -109,29 +117,37 @@ it "rounds children served to integer ceiling" do organization = create(:organization, :with_items) - disposable_item = organization.items.disposable.first - disposable_item.update!(distribution_quantity: 20) - - # Kits - kit = create(:kit, organization: organization) create(:base_item, name: "Toddler Disposable Diaper", partner_key: "toddler diapers", category: "disposable diaper") - toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers", kit:, organization:, distribution_quantity: 10) + create(:base_item, name: "Infant Disposable Diaper", partner_key: "infant diapers", category: "infant disposable diaper") + create(:base_item, name: "Adult Diaper", partner_key: "adult diapers", category: "adult diaper") - # Distributions - distributions = create_list(:distribution, 2, issued_at: within_time, organization: organization) - distributions.each do |dist| - create_list(:line_item, 2, :distribution, quantity: 6, item: disposable_item, itemizable: dist) - end + toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers") + infant_disposable_kit_item = create(:item, name: "Infant Disposable Diapers", partner_key: "infant diapers") + not_disposable_kit_item = create(:item, name: "Adult Diapers", partner_key: "adult diapers") + + # this quantity shouldn't matter so I'm setting it to a high number to ensure it isn't used + kit = create(:kit, organization: organization, line_items: [ + create(:line_item, quantity: 1000, item: toddler_disposable_kit_item), + create(:line_item, quantity: 1000, item: infant_disposable_kit_item), + create(:line_item, quantity: 1000, item: not_disposable_kit_item) + ]) + create(:item, name: "Kit 1", kit:, organization:, distribution_quantity: 3) + + # Distributions toddler_distribution = create(:distribution, organization: organization, issued_at: within_time) - create(:line_item, quantity: 1, item: toddler_disposable_kit_item, itemizable: toddler_distribution) + infant_distribution = create(:distribution, organization: organization, issued_at: within_time) + + [toddler_distribution, infant_distribution].each do |distribution| + create(:line_item, quantity: 2, item: kit.item, itemizable: distribution) + end report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({ name: "Children Served", entries: { - "Average children served monthly" => "0.25", - "Total children served" => "3", # 1 / 10 = 0.1 rounds to 1 child from kits + 6 / 20 * 4 = 1.25 rounds to 2 children from nonkits + "Average children served monthly" => "0.17", + "Total children served" => "2", # 2 kits / 3 distribution_quantity = 1 child served * 2 distributions = 2 "Repackages diapers?" => "N", "Monthly diaper distributions?" => "N" } From b6e2e1be898c4739c2178a89cad8d9fb849c42c9 Mon Sep 17 00:00:00 2001 From: Cory Streiff Date: Wed, 5 Mar 2025 16:17:22 +0100 Subject: [PATCH 7/7] Fix bug where only first kit distribution item counted --- .../reports/children_served_report_service.rb | 10 +++++++--- .../children_served_report_service_spec.rb | 16 ++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/services/reports/children_served_report_service.rb b/app/services/reports/children_served_report_service.rb index 8f29094b71..e2828b440b 100644 --- a/app/services/reports/children_served_report_service.rb +++ b/app/services/reports/children_served_report_service.rb @@ -48,15 +48,19 @@ def total_children_served_with_loose_disposables # have the same relationships and we want to perform calculations on the # items in the kits not the kit items themselves. def children_served_with_kits_containing_disposables - organization + kits_subquery = organization .distributions .for_year(year) .joins(line_items: { item: { kit: { line_items: {item: :base_item} }}}) .where("base_items.category ILIKE '%diaper%' AND NOT base_items.category ILIKE '%cloth%' OR base_items.name ILIKE '%cloth%' AND NOT base_items.category ILIKE '%adult%'") - .group("line_items_kits.id") - .pick(Arel.sql("CEILING(SUM(line_items.quantity::numeric / COALESCE(items.distribution_quantity, 1)))")) + .select("DISTINCT ON (distributions.id, line_items.id, kits.id) line_items.quantity, items.distribution_quantity") + .to_sql + + Distribution + .from("(#{kits_subquery}) AS q") + .pick(Arel.sql("CEILING(SUM(q.quantity::numeric / COALESCE(q.distribution_quantity, 1)))")) .to_i end end diff --git a/spec/services/reports/children_served_report_service_spec.rb b/spec/services/reports/children_served_report_service_spec.rb index ac535b9b82..216ecc0fa4 100644 --- a/spec/services/reports/children_served_report_service_spec.rb +++ b/spec/services/reports/children_served_report_service_spec.rb @@ -32,12 +32,18 @@ toddler_disposable_kit_item = create(:item, name: "Toddler Disposable Diapers", partner_key: "toddler diapers") infant_disposable_kit_item = create(:item, name: "Infant Disposable Diapers", partner_key: "infant diapers") - kit = create(:kit, organization: organization, line_items: [ + kit_1 = create(:kit, organization: organization, line_items: [ create(:line_item, item: toddler_disposable_kit_item), create(:line_item, item: infant_disposable_kit_item) ]) - create(:item, name: "Kit 1", kit:, organization:) + kit_2 = create(:kit, organization: organization, line_items: [ + create(:line_item, item: toddler_disposable_kit_item), + create(:line_item, item: infant_disposable_kit_item) + ]) + + create(:item, name: "Kit 1", kit: kit_1, organization:) + create(:item, name: "Kit 2", kit: kit_2, organization:) # Distributions distributions = create_list(:distribution, 2, issued_at: within_time, organization: organization) @@ -50,8 +56,10 @@ infant_distribution = create(:distribution, organization: organization, issued_at: within_time) toddler_distribution = create(:distribution, organization: organization, issued_at: within_time) - create(:line_item, quantity: 10, item: kit.item, itemizable: infant_distribution) - create(:line_item, quantity: 10, item: kit.item, itemizable: toddler_distribution) + create(:line_item, quantity: 5, item: kit_1.item, itemizable: infant_distribution) + create(:line_item, quantity: 5, item: kit_1.item, itemizable: toddler_distribution) + create(:line_item, quantity: 5, item: kit_2.item, itemizable: infant_distribution) + create(:line_item, quantity: 5, item: kit_2.item, itemizable: toddler_distribution) report = described_class.new(organization: organization, year: within_time.year).report expect(report).to eq({