Skip to content

Commit a21bf2d

Browse files
committed
Add an option to control whether "in-kind value" of each item should be added in export of donations and distributions
1 parent 220af87 commit a21bf2d

16 files changed

+314
-150
lines changed

app/controllers/donations_controller.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def index
4848
respond_to do |format|
4949
format.html
5050
format.csv do
51-
send_data Exports::ExportDonationsCSVService.new(donation_ids: @donations.map(&:id)).generate_csv, filename: "Donations-#{Time.zone.today}.csv"
51+
send_data Exports::ExportDonationsCSVService.new(donation_ids: @donations.map(&:id), organization: current_organization).generate_csv, filename: "Donations-#{Time.zone.today}.csv"
5252
end
5353
end
5454
end

app/controllers/organizations_controller.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def organization_params
9898
:enable_individual_requests, :enable_quantity_based_requests,
9999
:ytd_on_distribution_printout, :one_step_partner_invite,
100100
:hide_value_columns_on_receipt, :hide_package_column_on_receipt,
101-
:signature_for_distribution_pdf,
101+
:signature_for_distribution_pdf, :include_in_kind_values_in_exported_files,
102102
partner_form_fields: [],
103103
request_unit_names: []
104104
)

app/models/organization.rb

+32-31
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,38 @@
22
#
33
# Table name: organizations
44
#
5-
# id :integer not null, primary key
6-
# city :string
7-
# deadline_day :integer
8-
# default_storage_location :integer
9-
# distribute_monthly :boolean default(FALSE), not null
10-
# email :string
11-
# enable_child_based_requests :boolean default(TRUE), not null
12-
# enable_individual_requests :boolean default(TRUE), not null
13-
# enable_quantity_based_requests :boolean default(TRUE), not null
14-
# hide_package_column_on_receipt :boolean default(FALSE)
15-
# hide_value_columns_on_receipt :boolean default(FALSE)
16-
# intake_location :integer
17-
# invitation_text :text
18-
# latitude :float
19-
# longitude :float
20-
# name :string
21-
# one_step_partner_invite :boolean default(FALSE), not null
22-
# partner_form_fields :text default([]), is an Array
23-
# reminder_day :integer
24-
# repackage_essentials :boolean default(FALSE), not null
25-
# short_name :string
26-
# signature_for_distribution_pdf :boolean default(FALSE)
27-
# state :string
28-
# street :string
29-
# url :string
30-
# ytd_on_distribution_printout :boolean default(TRUE), not null
31-
# zipcode :string
32-
# created_at :datetime not null
33-
# updated_at :datetime not null
34-
# account_request_id :integer
35-
# ndbn_member_id :bigint
5+
# id :integer not null, primary key
6+
# city :string
7+
# deadline_day :integer
8+
# default_storage_location :integer
9+
# distribute_monthly :boolean default(FALSE), not null
10+
# email :string
11+
# enable_child_based_requests :boolean default(TRUE), not null
12+
# enable_individual_requests :boolean default(TRUE), not null
13+
# enable_quantity_based_requests :boolean default(TRUE), not null
14+
# hide_package_column_on_receipt :boolean default(FALSE)
15+
# hide_value_columns_on_receipt :boolean default(FALSE)
16+
# include_in_kind_values_in_exported_files :boolean default(FALSE)
17+
# intake_location :integer
18+
# invitation_text :text
19+
# latitude :float
20+
# longitude :float
21+
# name :string
22+
# one_step_partner_invite :boolean default(FALSE), not null
23+
# partner_form_fields :text default([]), is an Array
24+
# reminder_day :integer
25+
# repackage_essentials :boolean default(FALSE), not null
26+
# short_name :string
27+
# signature_for_distribution_pdf :boolean default(FALSE)
28+
# state :string
29+
# street :string
30+
# url :string
31+
# ytd_on_distribution_printout :boolean default(TRUE), not null
32+
# zipcode :string
33+
# created_at :datetime not null
34+
# updated_at :datetime not null
35+
# account_request_id :integer
36+
# ndbn_member_id :bigint
3637
#
3738

3839
class Organization < ApplicationRecord

app/services/exports/export_distributions_csv_service.rb

+19-4
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def base_table
8282
end
8383
},
8484
"Total Value" => ->(distribution) {
85-
distribution.cents_to_dollar(distribution.line_items.total_value)
85+
Money.from_cents(distribution.line_items.total_value)
8686
},
8787
"Delivery Method" => ->(distribution) {
8888
distribution.delivery_method
@@ -119,22 +119,37 @@ def item_headers
119119
return @item_headers if @item_headers
120120

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

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

127-
row += Array.new(item_headers.size, 0)
128-
130+
row += make_item_quantity_and_value_slots
129131
distribution.line_items.each do |line_item|
130132
item_name = line_item.item.name
131133
item_column_idx = headers_with_indexes[item_name]
132134
next unless item_column_idx
133135

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

137-
row
140+
convert_to_dollar(row)
141+
end
142+
143+
def make_item_quantity_and_value_slots
144+
slots = Array.new(item_headers.size, 0)
145+
slots = slots.map.with_index { |value, index| index.odd? ? Money.new(0) : value } if @organization.include_in_kind_values_in_exported_files
146+
slots
147+
end
148+
149+
def convert_to_dollar(row)
150+
row.map do |column|
151+
column.is_a?(Money) ? column.to_f : column
152+
end
138153
end
139154
end
140155
end

app/services/exports/export_donations_csv_service.rb

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module Exports
22
class ExportDonationsCSVService
3-
def initialize(donation_ids:)
3+
def initialize(donation_ids:, organization:)
44
# Use a where lookup so that I can eager load all the resources
55
# needed rather than depending on external code to do it for me.
66
# This makes this code more self contained and efficient!
@@ -13,6 +13,7 @@ def initialize(donation_ids:)
1313
).where(
1414
id: donation_ids,
1515
).order(created_at: :asc)
16+
@organization = organization
1617
end
1718

1819
def generate_csv
@@ -80,7 +81,7 @@ def base_table
8081
"Variety of Items" => ->(donation) {
8182
donation.line_items.map(&:name).uniq.size
8283
},
83-
"In-Kind Value" => ->(donation) {
84+
"In-Kind Total" => ->(donation) {
8485
donation.in_kind_value_money
8586
},
8687
"Comments" => ->(donation) {
@@ -105,20 +106,36 @@ def item_headers
105106
end
106107

107108
@item_headers = item_names.sort
109+
@item_headers = @item_headers.flat_map { |h| [h, "#{h} In-Kind Value"] } if @organization.include_in_kind_values_in_exported_files
110+
111+
@item_headers
108112
end
109113

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

113-
row += Array.new(item_headers.size, 0)
117+
row += make_item_quantity_and_value_slots
114118

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

121-
row
126+
convert_to_dollar(row)
127+
end
128+
129+
def make_item_quantity_and_value_slots
130+
slots = Array.new(item_headers.size, 0)
131+
slots = slots.map.with_index { |value, index| index.odd? ? Money.new(0) : value } if @organization.include_in_kind_values_in_exported_files
132+
slots
133+
end
134+
135+
def convert_to_dollar(row)
136+
row.map do |column|
137+
column.is_a?(Money) ? column.to_f : column
138+
end
122139
end
123140
end
124141
end

app/views/organizations/_details.html.erb

+6
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,12 @@
194194
<%= humanize_boolean(@organization.hide_package_column_on_receipt) %>
195195
</p>
196196
</div>
197+
<div class="mb-4">
198+
<h3 class='font-bold'>Include in-kind value in donation and distribution exports:</h3>
199+
<p>
200+
<%= humanize_boolean(@organization.include_in_kind_values_in_exported_files) %>
201+
</p>
202+
</div>
197203
<% if @organization.logo.attached? %>
198204
<div class="mb-4">
199205
<h3 class='font-bold'>Logo</h3>

app/views/organizations/edit.html.erb

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@
131131
<%= 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 %>
132132
<%= 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 %>
133133
<%= 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 %>
134+
<%= 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 %>
134135

135136
<% 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." %>
136137
<%= f.input :default_email_text, label: "Distribution Email Content", hint: default_email_text_hint.html_safe do %>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class AddIncludeInKindValuesInExportedFilesToOrganizations < ActiveRecord::Migration[7.2]
2+
def change
3+
add_column :organizations, :include_in_kind_values_in_exported_files, :boolean
4+
change_column_default :organizations, :include_in_kind_values_in_exported_files, false
5+
end
6+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class BackfillAddIncludeInKindValuesInExportedFilesToOrganizations < ActiveRecord::Migration[7.2]
2+
disable_ddl_transaction!
3+
def change
4+
Organization.unscoped.in_batches do |relation|
5+
relation.update_all include_in_kind_values_in_exported_files: false
6+
sleep(0.01)
7+
end
8+
end
9+
end

db/schema.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[7.2].define(version: 2024_12_20_020009) do
13+
ActiveRecord::Schema[7.2].define(version: 2024_12_30_044251) do
1414
# These are extensions that must be enabled in order to support this database
1515
enable_extension "plpgsql"
1616

@@ -491,6 +491,7 @@
491491
t.boolean "hide_value_columns_on_receipt", default: false
492492
t.boolean "hide_package_column_on_receipt", default: false
493493
t.boolean "signature_for_distribution_pdf", default: false
494+
t.boolean "include_in_kind_values_in_exported_files", default: false
494495
t.index ["latitude", "longitude"], name: "index_organizations_on_latitude_and_longitude"
495496
t.index ["short_name"], name: "index_organizations_on_short_name"
496497
end

docs/user_guide/bank/exports.md

+6
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ For each of the distributions in the filtered list:
129129

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

132+
### Add In-Kind Value for each item
133+
Click "My Organization" in the left hand manu. Click "Edit" button. Set the "Include in-kind value in donation and distribution exports?" to "yes".
134+
132135
## Donations
133136

134137
### Navigating to export Donations
@@ -157,6 +160,9 @@ For each of the Donations in the filtered list:
157160
- Comments,
158161
- and the quantity of each of your organization's Items in the Donations.
159162

163+
### Add In-Kind Value for each item
164+
Click "My Organization" in the left hand manu. Click "Edit" button. Set the "Include in-kind value in donation and distribution exports?" to "yes".
165+
160166
## Donation Sites
161167
### Navigating to export Donation Sites
162168
Click "Community", then "Donation Sites" in the left hand menu. Then click "Export Donation Sites"

spec/factories/items.rb

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
sequence(:name) { |n| "#{n}T Diapers" }
2727
organization { Organization.try(:first) || create(:organization) }
2828
partner_key { BaseItem.first&.partner_key || create(:base_item).partner_key }
29+
value_in_cents { 0 }
2930
kit { nil }
3031

3132
trait :active do

spec/factories/organizations.rb

+32-31
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,38 @@
22
#
33
# Table name: organizations
44
#
5-
# id :integer not null, primary key
6-
# city :string
7-
# deadline_day :integer
8-
# default_storage_location :integer
9-
# distribute_monthly :boolean default(FALSE), not null
10-
# email :string
11-
# enable_child_based_requests :boolean default(TRUE), not null
12-
# enable_individual_requests :boolean default(TRUE), not null
13-
# enable_quantity_based_requests :boolean default(TRUE), not null
14-
# hide_package_column_on_receipt :boolean default(FALSE)
15-
# hide_value_columns_on_receipt :boolean default(FALSE)
16-
# intake_location :integer
17-
# invitation_text :text
18-
# latitude :float
19-
# longitude :float
20-
# name :string
21-
# one_step_partner_invite :boolean default(FALSE), not null
22-
# partner_form_fields :text default([]), is an Array
23-
# reminder_day :integer
24-
# repackage_essentials :boolean default(FALSE), not null
25-
# short_name :string
26-
# signature_for_distribution_pdf :boolean default(FALSE)
27-
# state :string
28-
# street :string
29-
# url :string
30-
# ytd_on_distribution_printout :boolean default(TRUE), not null
31-
# zipcode :string
32-
# created_at :datetime not null
33-
# updated_at :datetime not null
34-
# account_request_id :integer
35-
# ndbn_member_id :bigint
5+
# id :integer not null, primary key
6+
# city :string
7+
# deadline_day :integer
8+
# default_storage_location :integer
9+
# distribute_monthly :boolean default(FALSE), not null
10+
# email :string
11+
# enable_child_based_requests :boolean default(TRUE), not null
12+
# enable_individual_requests :boolean default(TRUE), not null
13+
# enable_quantity_based_requests :boolean default(TRUE), not null
14+
# hide_package_column_on_receipt :boolean default(FALSE)
15+
# hide_value_columns_on_receipt :boolean default(FALSE)
16+
# include_in_kind_values_in_exported_files :boolean default(FALSE)
17+
# intake_location :integer
18+
# invitation_text :text
19+
# latitude :float
20+
# longitude :float
21+
# name :string
22+
# one_step_partner_invite :boolean default(FALSE), not null
23+
# partner_form_fields :text default([]), is an Array
24+
# reminder_day :integer
25+
# repackage_essentials :boolean default(FALSE), not null
26+
# short_name :string
27+
# signature_for_distribution_pdf :boolean default(FALSE)
28+
# state :string
29+
# street :string
30+
# url :string
31+
# ytd_on_distribution_printout :boolean default(TRUE), not null
32+
# zipcode :string
33+
# created_at :datetime not null
34+
# updated_at :datetime not null
35+
# account_request_id :integer
36+
# ndbn_member_id :bigint
3637
#
3738
require 'seeds'
3839

0 commit comments

Comments
 (0)