Skip to content

Commit

Permalink
Add an option to control whether "in-kind value" of each item should …
Browse files Browse the repository at this point in the history
…be added in export of donations and distributions
  • Loading branch information
nozomirin committed Jan 6, 2025
1 parent 220af87 commit 4ae30dc
Show file tree
Hide file tree
Showing 16 changed files with 320 additions and 150 deletions.
2 changes: 1 addition & 1 deletion app/controllers/donations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def index
respond_to do |format|
format.html
format.csv do
send_data Exports::ExportDonationsCSVService.new(donation_ids: @donations.map(&:id)).generate_csv, filename: "Donations-#{Time.zone.today}.csv"
send_data Exports::ExportDonationsCSVService.new(donation_ids: @donations.map(&:id), organization: current_organization).generate_csv, filename: "Donations-#{Time.zone.today}.csv"
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/organizations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def organization_params
:enable_individual_requests, :enable_quantity_based_requests,
:ytd_on_distribution_printout, :one_step_partner_invite,
:hide_value_columns_on_receipt, :hide_package_column_on_receipt,
:signature_for_distribution_pdf,
:signature_for_distribution_pdf, :include_in_kind_values_in_exported_files,
partner_form_fields: [],
request_unit_names: []
)
Expand Down
63 changes: 32 additions & 31 deletions app/models/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,38 @@
#
# Table name: organizations
#
# id :integer not null, primary key
# city :string
# deadline_day :integer
# default_storage_location :integer
# distribute_monthly :boolean default(FALSE), not null
# email :string
# enable_child_based_requests :boolean default(TRUE), not null
# enable_individual_requests :boolean default(TRUE), not null
# enable_quantity_based_requests :boolean default(TRUE), not null
# hide_package_column_on_receipt :boolean default(FALSE)
# hide_value_columns_on_receipt :boolean default(FALSE)
# intake_location :integer
# invitation_text :text
# latitude :float
# longitude :float
# name :string
# one_step_partner_invite :boolean default(FALSE), not null
# partner_form_fields :text default([]), is an Array
# reminder_day :integer
# repackage_essentials :boolean default(FALSE), not null
# short_name :string
# signature_for_distribution_pdf :boolean default(FALSE)
# state :string
# street :string
# url :string
# ytd_on_distribution_printout :boolean default(TRUE), not null
# zipcode :string
# created_at :datetime not null
# updated_at :datetime not null
# account_request_id :integer
# ndbn_member_id :bigint
# id :integer not null, primary key
# city :string
# deadline_day :integer
# default_storage_location :integer
# distribute_monthly :boolean default(FALSE), not null
# email :string
# enable_child_based_requests :boolean default(TRUE), not null
# enable_individual_requests :boolean default(TRUE), not null
# enable_quantity_based_requests :boolean default(TRUE), not null
# hide_package_column_on_receipt :boolean default(FALSE)
# hide_value_columns_on_receipt :boolean default(FALSE)
# include_in_kind_values_in_exported_files :boolean default(FALSE)
# intake_location :integer
# invitation_text :text
# latitude :float
# longitude :float
# name :string
# one_step_partner_invite :boolean default(FALSE), not null
# partner_form_fields :text default([]), is an Array
# reminder_day :integer
# repackage_essentials :boolean default(FALSE), not null
# short_name :string
# signature_for_distribution_pdf :boolean default(FALSE)
# state :string
# street :string
# url :string
# ytd_on_distribution_printout :boolean default(TRUE), not null
# zipcode :string
# created_at :datetime not null
# updated_at :datetime not null
# account_request_id :integer
# ndbn_member_id :bigint
#

class Organization < ApplicationRecord
Expand Down
23 changes: 19 additions & 4 deletions app/services/exports/export_distributions_csv_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def base_table
end
},
"Total Value" => ->(distribution) {
distribution.cents_to_dollar(distribution.line_items.total_value)
Money.from_cents(distribution.line_items.total_value)
},
"Delivery Method" => ->(distribution) {
distribution.delivery_method
Expand Down Expand Up @@ -119,22 +119,37 @@ def item_headers
return @item_headers if @item_headers

@item_headers = @organization.items.select("DISTINCT ON (LOWER(name)) items.name").order("LOWER(name) ASC").map(&:name)
@item_headers = @item_headers.flat_map { |h| [h, "#{h} In-Kind Value"] } if @organization.include_in_kind_values_in_exported_files

@item_headers
end

def build_row_data(distribution)
row = base_table.values.map { |closure| closure.call(distribution) }

row += Array.new(item_headers.size, 0)

row += make_item_quantity_and_value_slots
distribution.line_items.each do |line_item|
item_name = line_item.item.name
item_column_idx = headers_with_indexes[item_name]
next unless item_column_idx

row[item_column_idx] += line_item.quantity
row[item_column_idx + 1] += Money.new(line_item.value_per_line_item) if @organization.include_in_kind_values_in_exported_files
end

row
convert_to_dollar(row)
end

def make_item_quantity_and_value_slots
slots = Array.new(item_headers.size, 0)
slots = slots.map.with_index { |value, index| index.odd? ? Money.new(0) : value } if @organization.include_in_kind_values_in_exported_files
slots
end

def convert_to_dollar(row)
row.map do |column|
column.is_a?(Money) ? column.to_f : column
end
end
end
end
25 changes: 21 additions & 4 deletions app/services/exports/export_donations_csv_service.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Exports
class ExportDonationsCSVService
def initialize(donation_ids:)
def initialize(donation_ids:, organization:)
# Use a where lookup so that I can eager load all the resources
# needed rather than depending on external code to do it for me.
# This makes this code more self contained and efficient!
Expand All @@ -13,6 +13,7 @@ def initialize(donation_ids:)
).where(
id: donation_ids,
).order(created_at: :asc)
@organization = organization
end

def generate_csv
Expand Down Expand Up @@ -80,7 +81,7 @@ def base_table
"Variety of Items" => ->(donation) {
donation.line_items.map(&:name).uniq.size
},
"In-Kind Value" => ->(donation) {
"In-Kind Total" => ->(donation) {
donation.in_kind_value_money
},
"Comments" => ->(donation) {
Expand All @@ -105,20 +106,36 @@ def item_headers
end

@item_headers = item_names.sort
@item_headers = @item_headers.flat_map { |h| [h, "#{h} In-Kind Value"] } if @organization.include_in_kind_values_in_exported_files

@item_headers
end

def build_row_data(donation)
row = base_table.values.map { |closure| closure.call(donation) }

row += Array.new(item_headers.size, 0)
row += make_item_quantity_and_value_slots

donation.line_items.each do |line_item|
item_name = line_item.item.name
item_column_idx = headers_with_indexes[item_name]
row[item_column_idx] += line_item.quantity
row[item_column_idx + 1] += Money.new(line_item.value_per_line_item) if @organization.include_in_kind_values_in_exported_files
end

row
convert_to_dollar(row)
end

def make_item_quantity_and_value_slots
slots = Array.new(item_headers.size, 0)
slots = slots.map.with_index { |value, index| index.odd? ? Money.new(0) : value } if @organization.include_in_kind_values_in_exported_files
slots
end

def convert_to_dollar(row)
row.map do |column|
column.is_a?(Money) ? column.to_f : column
end
end
end
end
6 changes: 6 additions & 0 deletions app/views/organizations/_details.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@
<%= humanize_boolean(@organization.hide_package_column_on_receipt) %>
</p>
</div>
<div class="mb-4">
<h3 class='font-bold'>Include in-kind value in donation and distribution exports:</h3>
<p>
<%= humanize_boolean(@organization.include_in_kind_values_in_exported_files) %>
</p>
</div>
<% if @organization.logo.attached? %>
<div class="mb-4">
<h3 class='font-bold'>Logo</h3>
Expand Down
1 change: 1 addition & 0 deletions app/views/organizations/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
<%= f.input :one_step_partner_invite, label: 'Use One Step Invite and Approve partner process?', as: :radio_buttons, collection: [[true, 'Yes'], [false, 'No']], label_method: :second, value_method: :first %>
<%= f.input :hide_value_columns_on_receipt, label: 'Hide both value columns on distribution receipts?', as: :radio_buttons, collection: [[true, 'Yes'], [false, 'No']], label_method: :second, value_method: :first %>
<%= f.input :hide_package_column_on_receipt, label: 'Hide the package column on distribution receipts?', as: :radio_buttons, collection: [[true, 'Yes'], [false, 'No']], label_method: :second, value_method: :first %>
<%= f.input :include_in_kind_values_in_exported_files, label: 'Include in-kind value in donation and distribution exports?', as: :radio_buttons, collection: [[true, 'Yes'], [false, 'No']], label_method: :second, value_method: :first %>

<% default_email_text_hint = "You can use the variables <code>%{partner_name}</code>, <code>%{delivery_method}</code>, <code>%{distribution_date}</code>, and <code>%{comment}</code> to include the partner's name, delivery method, distribution date, and comments sent in the request." %>
<%= f.input :default_email_text, label: "Distribution Email Content", hint: default_email_text_hint.html_safe do %>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddIncludeInKindValuesInExportedFilesToOrganizations < ActiveRecord::Migration[7.2]
def change
add_column :organizations, :include_in_kind_values_in_exported_files, :boolean
change_column_default :organizations, :include_in_kind_values_in_exported_files, false
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class BackfillAddIncludeInKindValuesInExportedFilesToOrganizations < ActiveRecord::Migration[7.2]
disable_ddl_transaction!
def change
Organization.unscoped.in_batches do |relation|
relation.update_all include_in_kind_values_in_exported_files: false
sleep(0.01)
end
end
end
3 changes: 2 additions & 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[7.2].define(version: 2024_12_20_020009) do
ActiveRecord::Schema[7.2].define(version: 2024_12_30_044251) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

Expand Down Expand Up @@ -491,6 +491,7 @@
t.boolean "hide_value_columns_on_receipt", default: false
t.boolean "hide_package_column_on_receipt", default: false
t.boolean "signature_for_distribution_pdf", default: false
t.boolean "include_in_kind_values_in_exported_files", default: false
t.index ["latitude", "longitude"], name: "index_organizations_on_latitude_and_longitude"
t.index ["short_name"], name: "index_organizations_on_short_name"
end
Expand Down
12 changes: 12 additions & 0 deletions docs/user_guide/bank/exports.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ For each of the distributions in the filtered list:

[!NOTE] This includes inactive Items as well as active ones.

### Add In-Kind Value for each item
The default export includes the number of items exported, but not the values. If you want to also have the export include the in-kind value for each item in the distributions, you can set that option.
Click "My Organization" in the left hand manu. Click "Edit" button. Set the "Include in-kind value in donation and distribution exports?" to "yes", then click "Save".

[!NOTE] Setting this affects both the donation and distribution exports.

## Donations

### Navigating to export Donations
Expand Down Expand Up @@ -157,6 +163,12 @@ For each of the Donations in the filtered list:
- Comments,
- and the quantity of each of your organization's Items in the Donations.

### Add In-Kind Value for each item
The default export includes the number of items exported, but not the values. If you want to also have the export include the in-kind value for each item in the distributions, you can set that option.
Click "My Organization" in the left hand manu. Click "Edit" button. Set the "Include in-kind value in donation and distribution exports?" to "yes", then click "Save".

[!NOTE] Setting this affects both the donation and distribution exports.

## Donation Sites
### Navigating to export Donation Sites
Click "Community", then "Donation Sites" in the left hand menu. Then click "Export Donation Sites"
Expand Down
1 change: 1 addition & 0 deletions spec/factories/items.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
sequence(:name) { |n| "#{n}T Diapers" }
organization { Organization.try(:first) || create(:organization) }
partner_key { BaseItem.first&.partner_key || create(:base_item).partner_key }
value_in_cents { 0 }
kit { nil }

trait :active do
Expand Down
63 changes: 32 additions & 31 deletions spec/factories/organizations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,38 @@
#
# Table name: organizations
#
# id :integer not null, primary key
# city :string
# deadline_day :integer
# default_storage_location :integer
# distribute_monthly :boolean default(FALSE), not null
# email :string
# enable_child_based_requests :boolean default(TRUE), not null
# enable_individual_requests :boolean default(TRUE), not null
# enable_quantity_based_requests :boolean default(TRUE), not null
# hide_package_column_on_receipt :boolean default(FALSE)
# hide_value_columns_on_receipt :boolean default(FALSE)
# intake_location :integer
# invitation_text :text
# latitude :float
# longitude :float
# name :string
# one_step_partner_invite :boolean default(FALSE), not null
# partner_form_fields :text default([]), is an Array
# reminder_day :integer
# repackage_essentials :boolean default(FALSE), not null
# short_name :string
# signature_for_distribution_pdf :boolean default(FALSE)
# state :string
# street :string
# url :string
# ytd_on_distribution_printout :boolean default(TRUE), not null
# zipcode :string
# created_at :datetime not null
# updated_at :datetime not null
# account_request_id :integer
# ndbn_member_id :bigint
# id :integer not null, primary key
# city :string
# deadline_day :integer
# default_storage_location :integer
# distribute_monthly :boolean default(FALSE), not null
# email :string
# enable_child_based_requests :boolean default(TRUE), not null
# enable_individual_requests :boolean default(TRUE), not null
# enable_quantity_based_requests :boolean default(TRUE), not null
# hide_package_column_on_receipt :boolean default(FALSE)
# hide_value_columns_on_receipt :boolean default(FALSE)
# include_in_kind_values_in_exported_files :boolean default(FALSE)
# intake_location :integer
# invitation_text :text
# latitude :float
# longitude :float
# name :string
# one_step_partner_invite :boolean default(FALSE), not null
# partner_form_fields :text default([]), is an Array
# reminder_day :integer
# repackage_essentials :boolean default(FALSE), not null
# short_name :string
# signature_for_distribution_pdf :boolean default(FALSE)
# state :string
# street :string
# url :string
# ytd_on_distribution_printout :boolean default(TRUE), not null
# zipcode :string
# created_at :datetime not null
# updated_at :datetime not null
# account_request_id :integer
# ndbn_member_id :bigint
#
require 'seeds'

Expand Down
Loading

0 comments on commit 4ae30dc

Please sign in to comment.