Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4283 period supplies #4582

Merged
merged 24 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2b945e8
Add distributed_period_supplies_from_kits method modified from aquesi…
jadekstewart3 Apr 26, 2024
73e757c
Begin test set up for period supplies in kits
jadekstewart3 Apr 26, 2024
0e70618
Modify test set up to include distributions containing period supply …
jadekstewart3 Apr 26, 2024
392b434
Fix kit creation in the test set up so it returns the correct integer
jadekstewart3 May 9, 2024
65c47a1
Modify normal value report to report total period supplies including …
jadekstewart3 May 9, 2024
9b3c42c
Complete metric: Period supplies per adult per month
jadekstewart3 Jun 4, 2024
deed965
Create dontated items from kits method. Still need testing for this m…
jadekstewart3 Jun 4, 2024
2d64b4c
Create test data and testing for calculating the number of period sup…
jadekstewart3 Jun 4, 2024
b413e36
Add purchased_kits method and add a simple test for the method. Needs…
jadekstewart3 Jun 4, 2024
16e2da4
Fix method counting purchased kits
jadekstewart3 Aug 6, 2024
ca9831d
Added a method to calculate the number of purchased period supplies w…
jadekstewart3 Aug 6, 2024
7d0ae93
Fix calculations for period supply report, and modify test to reflect…
jadekstewart3 Aug 6, 2024
038e4cf
add the :with_items back in on creation of organization
jadekstewart3 Aug 6, 2024
e3240ed
Merge branch 'main' into 4238_period_supplies
jadekstewart3 Aug 6, 2024
bf472aa
Rubocop
jadekstewart3 Aug 6, 2024
46b2fcf
Merge branch '4238_period_supplies' of github.com:jadekstewart3/human…
jadekstewart3 Aug 6, 2024
87ac6af
Fix errors within spec
jadekstewart3 Aug 6, 2024
11ce2f7
Rubocop
jadekstewart3 Aug 6, 2024
daa09b0
Added specificity to kit_items_calculation by ensuring diaper and clo…
jadekstewart3 Aug 19, 2024
9528c08
Added another distribution with a total of 200 items to see if expect…
jadekstewart3 Aug 19, 2024
3cf19e1
Fix spelling error
jadekstewart3 Aug 28, 2024
552bbfc
Modify period supplies per adult per month calculations to ensure onl…
jadekstewart3 Sep 10, 2024
fc10668
Modify expect statement to align with new calculations
jadekstewart3 Sep 10, 2024
5417ec2
Remove methods and test set up to calculate per person per month
jadekstewart3 Oct 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 65 additions & 10 deletions app/services/reports/period_supply_report_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ def initialize(year:, organization:)
def report
@report ||= {name: "Period Supplies",
entries: {
"Period supplies distributed" => number_with_delimiter(distributed_supplies),
"Period supplies per adult per month" => monthly_supplies&.round || 0,
"Period supplies distributed" => number_with_delimiter(total_distributed_period_supplies),
"Period supplies per adult per month" => (monthly_supplies + distributed_period_supplies_from_kits_per_month)&.round,
"Period supplies" => types_of_supplies,
"% period supplies donated" => "#{percent_donated.round}%",
"% period supplies bought" => "#{percent_bought.round}%",
Expand All @@ -24,7 +24,7 @@ def report
end

# @return [Integer]
def distributed_supplies
def distributed_loose_period_supplies
@distributed_supplies ||= organization
.distributions
.for_year(year)
Expand All @@ -33,6 +33,18 @@ def distributed_supplies
.sum("line_items.quantity")
end

def distributed_period_supplies_from_kits
kit_items_calculation("distributions", "Distribution")
end

def distributed_period_supplies_from_kits_per_month
distributed_period_supplies_from_kits / 12 || 0
end

def total_distributed_period_supplies
distributed_loose_period_supplies + distributed_period_supplies_from_kits
end

# @return [Integer]
def monthly_supplies
# NOTE: This is asking "per adult per month" but there doesn't seem to be much difference
Expand All @@ -43,7 +55,7 @@ def monthly_supplies
.for_year(year)
.joins(line_items: :item)
.merge(Item.period_supplies)
.average("COALESCE(items.distribution_quantity, 50)")
.average("COALESCE(items.distribution_quantity, 50)") || 0
end

def types_of_supplies
Expand All @@ -54,14 +66,14 @@ def types_of_supplies
def percent_donated
return 0.0 if total_supplies.zero?

(donated_supplies / total_supplies.to_f) * 100
(total_donated_supplies / total_supplies.to_f) * 100
end

# @return [Float]
def percent_bought
return 0.0 if total_supplies.zero?

(purchased_supplies / total_supplies.to_f) * 100
(total_purchased_supplies / total_supplies.to_f) * 100
end

# @return [String]
Expand All @@ -72,24 +84,67 @@ def money_spent_on_supplies
###### HELPER METHODS ######

# @return [Integer]
def purchased_supplies
def total_purchased_supplies
@purchased_supplies ||= LineItem.joins(:item)
.merge(Item.period_supplies)
.where(itemizable: organization.purchases.for_year(year))
.sum(:quantity)

@purchased_supplies + purchased_supplies_from_kits
end

def purchased_supplies_from_kits
kit_items_calculation("purchases", "Purchase")
end

# @return [Integer]
def total_supplies
@total_supplies ||= purchased_supplies + donated_supplies
@total_supplies ||= total_purchased_supplies + total_donated_supplies
end

# @return [Integer]
def donated_supplies
@donated_supplies ||= LineItem.joins(:item)
def total_donated_supplies
loose_donated_supplies = LineItem.joins(:item)
.merge(Item.period_supplies)
.where(itemizable: organization.donations.for_year(year))
.sum(:quantity)

loose_donated_supplies + donated_supplies_from_kits
end

def donated_supplies_from_kits
kit_items_calculation("donations", "Donation")
end

private

def kit_items_calculation(itemizable_type, string_itemizable_type)
organization_id = @organization.id
year = @year

# Sanitize and validate inputs
itemizable_type = ActiveRecord::Base.connection.quote_table_name(itemizable_type)
string_itemizable_type = ActiveRecord::Base.connection.quote(string_itemizable_type)

sql_query = <<-SQL
SELECT SUM(line_items.quantity * kit_line_items.quantity)
FROM #{itemizable_type}
INNER JOIN line_items ON line_items.itemizable_type = #{string_itemizable_type} AND line_items.itemizable_id = #{itemizable_type}.id
INNER JOIN items ON items.id = line_items.item_id
INNER JOIN kits ON kits.id = items.kit_id
INNER JOIN line_items AS kit_line_items ON kits.id = kit_line_items.itemizable_id
INNER JOIN items AS kit_items ON kit_items.id = kit_line_items.item_id
INNER JOIN base_items ON base_items.partner_key = kit_items.partner_key
WHERE #{itemizable_type}.organization_id = ?
AND EXTRACT(year FROM issued_at) = ?
AND LOWER(base_items.category) LIKE '%menstrual supplies%'
AND NOT (LOWER(base_items.category) LIKE '%diaper%' OR LOWER(base_items.name) LIKE '%cloth%')
AND kit_line_items.itemizable_type = 'Kit';
SQL

sanitized_sql = ActiveRecord::Base.send(:sanitize_sql_array, [sql_query, organization_id, year])
result = ActiveRecord::Base.connection.execute(sanitized_sql)
result.first["sum"].to_i
end
end
end
110 changes: 87 additions & 23 deletions spec/services/reports/period_supply_report_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@
end

describe "#report" do
it "should report zero values" do
expect(report.report[:entries]).to match(hash_including({
"% period supplies bought" => "0%",
"% period supplies donated" => "0%",
"Period supplies distributed" => "0",
"Period supplies per adult per month" => 0,
"Money spent purchasing period supplies" => "$0.00"
}))
expect(report.report[:entries]["Period supplies"].split(", "))
.to contain_exactly("Tampons", "Pads", "Liners (Menstrual)")
context "with no values" do
it "should report zero values" do
expect(report.report[:entries]).to match(hash_including({
"% period supplies bought" => "0%",
"% period supplies donated" => "0%",
"Period supplies distributed" => "0",
"Period supplies per adult per month" => 0,
"Money spent purchasing period supplies" => "$0.00"
}))
expect(report.report[:entries]["Period supplies"].split(", "))
.to contain_exactly("Tampons", "Pads", "Liners (Menstrual)")
end
end

describe "with values" do
context "with values" do
before(:each) do
Organization.seed_items(organization)

Expand All @@ -32,6 +34,54 @@
# We will create data both within and outside our date range, and both period_supplies and non period_supplies.
# Spec will ensure that only the required data is included.

# Kits
period_supplies_kit = create(:kit, :with_item, organization: organization)
another_period_supply_kit = create(:kit, :with_item, organization: organization)
donated_period_supply_kit = create(:kit, :with_item, organization: organization)
purchased_period_supply_kit = create(:kit, :with_item, organization: organization)
pad_and_tampon_kit = create(:kit, :with_item, organization: organization)

create(:base_item, name: "Adult Pads", partner_key: "adult pads", category: "Menstrual Supplies")
create(:base_item, name: "Adult Tampons", partner_key: "adult tampons", category: "Menstrual Supplies")

period_supplies_kit_item = create(:item, name: "Adult Pads", partner_key: "adult pads")
another_period_supplies_kit_item = create(:item, name: "Adult Tampons", partner_key: "adult tampons")
purchased_period_supplies_kit_item = create(:item, name: "Liners", partner_key: "adult tampons")

period_supplies_kit.line_items.first.update!(item_id: period_supplies_kit_item.id, quantity: 5)
another_period_supply_kit.line_items.first.update!(item_id: another_period_supplies_kit_item.id, quantity: 5)
donated_period_supply_kit.line_items.first.update!(item_id: another_period_supplies_kit_item.id, quantity: 5)
purchased_period_supply_kit.line_items.first.update!(item_id: purchased_period_supplies_kit_item.id, quantity: 5)

pad_and_tampon_kit.line_items.first.update!(item_id: period_supplies_kit_item.id, quantity: 10)
pad_and_tampon_kit.line_items.first.update!(item_id: another_period_supplies_kit_item.id, quantity: 10)

period_supplies_kit_distribution = create(:distribution, organization: organization, issued_at: within_time)
another_period_supplies_kit_distribution = create(:distribution, organization: organization, issued_at: within_time)
pad_and_tampon_kit_distribution = create(:distribution, organization: organization, issued_at: within_time)

kit_donation = create(:donation, product_drive: nil, issued_at: within_time, money_raised: 1000, organization: organization)

kit_purchase = create(:purchase, issued_at: within_time, organization: organization, purchased_from: "TikTok Shop", amount_spent_in_cents: 1000, amount_spent_on_period_supplies_cents: 1000, line_items: [
create(:line_item, :purchase, item: period_supplies_kit_item, quantity: 5),
create(:line_item, :purchase, item: purchased_period_supplies_kit_item, quantity: 5)
])

create(:line_item, :distribution, quantity: 10, item: period_supplies_kit.item, itemizable: period_supplies_kit_distribution)
create(:line_item, :distribution, quantity: 10, item: another_period_supply_kit.item, itemizable: another_period_supplies_kit_distribution)

create(:line_item, :distribution, quantity: 10, item: pad_and_tampon_kit.item, itemizable: pad_and_tampon_kit_distribution)
create(:line_item, :distribution, quantity: 10, item: pad_and_tampon_kit.item, itemizable: pad_and_tampon_kit_distribution)

create(:line_item, :donation, quantity: 10, item: donated_period_supply_kit.item, itemizable: kit_donation)

create(:line_item, :purchase, quantity: 30, item: purchased_period_supply_kit.item, itemizable: kit_purchase)

# create(:purchase, issued_at: within_time, organization: organization, line_items: [
# create(:line_item, :purchase, item: period_supplies_kit_item, quantity: 5),
# create(:line_item, :purchase, item: purchased_period_supply_kit_item, quantity: 5)
# ])

# Distributions
distributions = create_list(:distribution, 2, issued_at: within_time, organization: organization)
outside_distributions = create_list(:distribution, 2, issued_at: outside_time, organization: organization)
Expand Down Expand Up @@ -84,19 +134,33 @@
end
end

it "should report normal values" do
organization.items.period_supplies.first.update!(distribution_quantity: 20)
describe "with values" do
it "should report normal values" do
organization.items.period_supplies.first.update!(distribution_quantity: 20)

expect(report.report[:name]).to eq("Period Supplies")
expect(report.report[:entries]).to match(hash_including({
"% period supplies bought" => "60%",
"% period supplies donated" => "40%",
"Period supplies distributed" => "2,000",
"Period supplies per adult per month" => 20,
"Money spent purchasing period supplies" => "$30.00"
}))
expect(report.report[:entries]["Period supplies"].split(", "))
.to contain_exactly("Tampons", "Pads", "Liners (Menstrual)")
expect(report.report[:name]).to eq("Period Supplies")
expect(report.report[:entries]).to match(hash_including({
"% period supplies bought" => "67%",
"% period supplies donated" => "33%",
"Period supplies distributed" => "2,300",
"Period supplies per adult per month" => 45,
"Money spent purchasing period supplies" => "$40.00"
}))
expect(report.report[:entries]["Period supplies"].split(", "))
.to contain_exactly("Adult Pads", "Adult Tampons", "Liners", "Liners (Menstrual)", "Pads", "Tampons")
end

it "returns the correct quantity of period supplies from kits" do
expect(report.distributed_period_supplies_from_kits).to eq(300)
end

it "returns the correct quantity of donated period supplies from kits" do
expect(report.donated_supplies_from_kits).to eq(50)
end

it "returns the correct quantity of purchased items in kits" do
expect(report.purchased_supplies_from_kits).to eq(150)
end
end
end
end
Expand Down