Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -858,4 +858,4 @@ DEPENDENCIES
webmock (~> 3.26)

BUNDLED WITH
2.7.1
4.0.11
9 changes: 2 additions & 7 deletions app/controllers/distributions_by_county_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@ class DistributionsByCountyController < ApplicationController

def report
setup_date_range_picker
start_date = helpers.selected_range.first.utc.iso8601
end_date = helpers.selected_range.last.utc.iso8601

@breakdown = DistributionSummaryByCountyQuery.call(
organization_id: current_organization.id,
start_date: start_date,
end_date: end_date
)
@dbc_info = View::DistributionsByCounty.from_params(params: params,
organization: current_organization, helpers: helpers)
end
end
1 change: 1 addition & 0 deletions app/models/item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class Item < ApplicationRecord
scope :alphabetized, -> { order(:name) }
scope :by_base_item, ->(base_item) { where(base_item: base_item) }
scope :by_reporting_category, ->(reporting_category) { where(reporting_category: reporting_category) }
scope :by_name, ->(name) { where(name: name) }
scope :by_partner_key, ->(partner_key) { where(partner_key: partner_key) }

scope :period_supplies, -> {
Expand Down
46 changes: 46 additions & 0 deletions app/models/view/distributions_by_county.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module View
DistributionsByCounty = Data.define(
:breakdown,
:filters,
:items,
:reporting_categories
) do
include DateRangeHelper

class << self
def filter_params(params)
return {} unless params.key?(:filters)
params
.require(:filters)
.permit(:by_item_id, :by_reporting_category, :date_range)
end

def from_params(params:, organization:, helpers:)
filters = filter_params(params)
start_date = helpers.selected_range.first.utc.iso8601
end_date = helpers.selected_range.last.utc.iso8601
breakdown = DistributionSummaryByCountyQuery.call(
organization_id: organization.id,
start_date: start_date,
end_date: end_date,
reporting_category: filters[:by_reporting_category].presence,
item_id: filters[:by_item_id].presence
)

new(
breakdown: breakdown,
filters: filters,
reporting_categories: Item.reporting_categories_for_select,
items: organization.items.loose.alphabetized.select(:id, :name)
)
end
end
def selected_reporting_category
filters[:by_reporting_category].presence
end

def selected_item
filters[:by_item_id].presence
end
end
end
51 changes: 43 additions & 8 deletions app/queries/distribution_summary_by_county_query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ class DistributionSummaryByCountyQuery
SQL_MULTILINE_COMMENTS = /\/\*.*?\*\//

DISTRIBUTION_BY_COUNTY_SQL = <<~SQL.squish.gsub(SQL_MULTILINE_COMMENTS, "").freeze
/* Calculate total item quantity and value per distribution */
WITH distribution_totals AS
/* Calculate total item quantity and value per distribution of "loose" items */

WITH loose_distribution_totals AS
(
SELECT DISTINCT d.id,
d.partner_id,
Expand All @@ -17,9 +18,39 @@ class DistributionSummaryByCountyQuery
JOIN items i ON i.id = li.item_id
WHERE d.issued_at BETWEEN :start_date AND :end_date
AND d.organization_id = :organization_id
AND i.reporting_category LIKE CONCAT('%', :reporting_category , '%')
AND i.id = CASE WHEN :item_id <> 0 THEN :item_id ELSE i.id END
GROUP BY d.id, li.id, i.id
),
/* Match distribution totals with client share and counties.
/* Calculate total item and value per distribution of items that happen to be in kits */
kitted_distribution_totals AS (
SELECT DISTINCT d.id,
d.partner_id,
COALESCE(SUM(li.quantity * kli.quantity) OVER (PARTITION BY d.id), 0) AS quantity,
COALESCE(SUM(COALESCE(ki.value_in_cents, 0) * li.quantity * kli.quantity) OVER (PARTITION BY d.id), 0) AS value
FROM distributions d
INNER JOIN line_items li ON li.itemizable_id = d.id AND li.itemizable_type = 'Distribution'
INNER JOIN items i ON i.id = li.item_id
INNER JOIN line_items AS kli ON i.id = kli.itemizable_id AND kli.itemizable_type = 'Item'
INNER JOIN items AS ki ON ki.id = kli.item_id
WHERE d.issued_at BETWEEN :start_date AND :end_date
AND d.organization_id = :organization_id
AND ki.reporting_category LIKE CONCAT('%', :reporting_category , '%')
AND ki.id = CASE WHEN :item_id <> 0 THEN :item_id ELSE ki.id END
GROUP BY d.id, li.id, i.id, kli.id, ki.id
),

/* Combine the loose and kitted */
full_distribution_totals as (
SELECT distinct COALESCE(ld.id,kd.id) as id,
COALESCE(ld.partner_id, kd.partner_id) AS partner_id,
COALESCE(ld.quantity,0) + COALESCE(kd.quantity, 0) as quantity,
COALESCE(ld.value,0) + COALESCE(kd.value, 0) as value
FROM loose_distribution_totals ld
FULL OUTER JOIN kitted_distribution_totals kd ON ld.id = kd.id
),

/* Match full distribution totals with client share and counties.
If distribution has no associated county, set county name to "Unspecified"
and set region to ZZZ so it will be last when sorted */
totals_by_county AS
Expand All @@ -30,7 +61,7 @@ class DistributionSummaryByCountyQuery
COALESCE(psa.client_share::float / 100, 1) AS percentage,
COALESCE(c.name, 'Unspecified') county_name,
COALESCE(c.region, 'ZZZ') county_region
FROM distribution_totals dt
FROM full_distribution_totals dt
LEFT JOIN partners p ON p.id = dt.partner_id
LEFT JOIN partner_profiles pp ON pp.partner_id = p.id
LEFT JOIN partner_served_areas psa ON psa.partner_profile_id = pp.id
Expand Down Expand Up @@ -58,11 +89,13 @@ class DistributionSummaryByCountyQuery
class << self
# Timestamps are stored in Postgres without timezones so
# start_date and end_date must be strings in UTC.
def call(organization_id:, start_date: nil, end_date: nil)
def call(organization_id:, start_date: nil, end_date: nil, reporting_category: nil, item_id: nil)
params = {
organization_id: organization_id,
start_date: start_date || "1000-01-01",
end_date: end_date || "3000-01-01"
end_date: end_date || "3000-01-01",
reporting_category: reporting_category,
item_id: item_id
}

execute(to_sql(DISTRIBUTION_BY_COUNTY_SQL, **params)).to_a.map(&to_county_summary)
Expand All @@ -74,13 +107,15 @@ def execute(sql)
ActiveRecord::Base.connection.execute(sql)
end

def to_sql(query, organization_id:, start_date:, end_date:)
def to_sql(query, organization_id:, start_date:, end_date:, reporting_category:, item_id:)
ActiveRecord::Base.sanitize_sql_array(
[
query,
organization_id: organization_id,
start_date: start_date,
end_date: end_date
end_date: end_date,
reporting_category: reporting_category,
item_id: item_id
]
)
end
Expand Down
37 changes: 28 additions & 9 deletions app/views/distributions_by_county/report.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<h1>
Estimated Distributions by County for <%= current_organization.name %>
</h1>
<h5> Please note that any items within kits are included in these estimates
</h5>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
Expand All @@ -25,17 +27,33 @@
<div class="row">
<div class="col-12">
<!-- Default box -->
<div class="card">
<div class="row">
<div class="col-sm-3 col-6">
<div class="card card-primary">
<div class="card-body">

<div class="description-block border-right">
<%= simple_form_for :filters, url: distributions_by_county_report_path(current_organization), method: :get do |f| %>
<%= render partial: "shared/date_range_picker", locals: {css_class: "form-control"} %><br>
<%= filter_button %>
<%= form_tag(distributions_by_county_report_path(current_organization), method: :get) do |f| %>
<div class="row">
<% if @dbc_info.items.present? %>
<div class="form-group col-lg-2 col-md-2 col-sm-6 col-xs-12">
<%= filter_select(label: "Item", scope: :by_item_id, collection: @dbc_info.items, selected: @dbc_info.selected_item) %>
</div>
<% end %>
<div class="form-group col-lg-2 col-md-2 col-sm-6 col-xs-12">
<%= filter_select(label: "Reporting Category", scope: :by_reporting_category, collection: @dbc_info.reporting_categories, selected: @dbc_info.selected_reporting_category) %>
</div>

<div class="form-group col-lg-3 col-md-3 col-sm-6 col-xs-12">
<%= label_tag "Date Range", "Date Range" %>
<%= render partial: "shared/date_range_picker", locals: {css_class: "form-control"} %>
</div>
</div>
<div class="card-footer">
<%= filter_button %>
<%= clear_filter_button %>
</div>
<% end %>
</div>
<!-- /.description-block -->
</div>

</div>

<div class="card-body table-responsive p-0">
Expand All @@ -48,7 +66,7 @@
</tr>
</thead>
<tbody>
<% @breakdown.each do |bd| %>
<% @dbc_info.breakdown.each do |bd| %>
<tr>
<td><%= bd.name %></td>
<td class="numeric"><%= number_with_delimiter(bd.quantity) %></td>
Expand All @@ -65,4 +83,5 @@
</div>
</div>
</div>
</div>
</section>
2 changes: 1 addition & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[8.0].define(version: 2025_10_07_141240) do
ActiveRecord::Schema[8.0].define(version: 2025_10_17_194543) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 10 additions & 3 deletions docs/user_guide/bank/reports_distributions_by_county.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@

![distributions_by_county_default_view](images/reports/reports_distributions_by_county_1.png)

This report shows the total items and estimated total market value of the distributed Items for the time period.
This report initially shows the total items and estimated total market value of the distributed Items for the time period.

We use the area served information entered in each partner's [profile](pm_partner_profiles.md) to allocate the items that have been distributed to them. If that information has not been provided, the items you've distributed are put into the 'Unspecified' category.


**N.B.** If you are using Kits, please note that this treats each Kit as an item, rather than counting the items within the Kits separately.
**N.B. If you are using Kits, please note that this counts the items within the kits, rather than the kits. You can not use this report to determine the number of kits that have been distributed to each county.**

If you want to see a time period other than the default of 60 days prior to 30 days forward from today, change the date range and click "Filter". We recommend you use the little pop-up gizmo to enter your date range, as the format of the date range is very fussy.

![distributions_by_county_with_date_range_gizmo](images/reports/reports_distributions_by_county_2.png)


Similarly, you can also filter by a single item, or by a reporting category, if, for example, you wanted to see distribution of disposable diapers, you would select "Disposalbe Diapers" from the Reporting Category menu, and then click "Filter"

![distributions_by_county_with_reporting_category_dropdown](images/reports/reports_distributions_by_county_3.png)


To go back to the default values, click "Clear filters".

[Back to Annual Survey](reports_annual_survey.md)

[Next: Manufacturer Donations Report](reports_manufacturers_donations.md)
Loading
Loading