Skip to content

Commit d33b720

Browse files
authored
Merge pull request #5388 from rubyforgood/5358-donation-refactor
#5358: Refactor donations controller to use a view object
2 parents 3905494 + 515d088 commit d33b720

File tree

4 files changed

+161
-94
lines changed

4 files changed

+161
-94
lines changed

app/controllers/donations_controller.rb

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,40 +18,14 @@ def print
1818
def index
1919
setup_date_range_picker
2020

21-
@donations = current_organization.donations
22-
.includes(:storage_location, :donation_site, :product_drive, :product_drive_participant, :manufacturer, line_items: [:item])
23-
.order(created_at: :desc)
24-
.class_filter(filter_params)
25-
.during(helpers.selected_range)
26-
@item_categories = current_organization.item_categories.pluck(:name).uniq
27-
@paginated_donations = @donations.page(params[:page])
28-
29-
@product_drives = current_organization.product_drives.alphabetized
30-
@product_drive_participants = current_organization.product_drive_participants.alphabetized
31-
32-
# Are these going to be inefficient with large datasets?
33-
# Using the @donations allows drilling down instead of always starting with the total dataset
34-
@donations_quantity = @donations.collect(&:total_quantity).sum
35-
@paginated_donations_quantity = @paginated_donations.collect(&:total_quantity).sum
36-
@total_value_all_donations = total_value(@donations)
37-
@paginated_in_kind_value = total_value(@paginated_donations)
38-
@total_money_raised = total_money_raised(@donations)
39-
@storage_locations = @donations.filter_map { |donation| donation.storage_location if !donation.storage_location.discarded_at }.compact.uniq.sort
40-
@selected_storage_location = filter_params[:at_storage_location]
41-
@sources = @donations.collect(&:source).uniq.sort
42-
@selected_source = filter_params[:by_source]
43-
@selected_item_category = filter_params[:by_category]
44-
@donation_sites = @donations.collect(&:donation_site).compact.uniq.sort_by { |site| site.name.downcase }
45-
@selected_donation_site = filter_params[:from_donation_site]
46-
@selected_product_drive = filter_params[:by_product_drive]
47-
@selected_product_drive_participant = filter_params[:by_product_drive_participant]
48-
@manufacturers = @donations.collect(&:manufacturer).compact.uniq.sort
49-
@selected_manufacturer = filter_params[:from_manufacturer]
21+
@donation_info = View::Donations.from_params(params: params, organization: current_organization, helpers: helpers)
5022

5123
respond_to do |format|
5224
format.html
5325
format.csv do
54-
send_data Exports::ExportDonationsCSVService.new(donation_ids: @donations.map(&:id), organization: current_organization).generate_csv, filename: "Donations-#{Time.zone.today}.csv"
26+
send_data Exports::ExportDonationsCSVService.new(donation_ids: @donation_info.donations.map(&:id),
27+
organization: current_organization).generate_csv,
28+
filename: "Donations-#{Time.zone.today}.csv"
5529
end
5630
end
5731
end
@@ -177,16 +151,4 @@ def compact_line_items
177151
params[:donation][:line_items_attributes].delete_if { |_row, data| data["quantity"].blank? && data["item_id"].blank? }
178152
params
179153
end
180-
181-
def total_value(donations)
182-
total_value_all_donations = 0
183-
donations.each do |donation|
184-
total_value_all_donations += donation.value_per_itemizable
185-
end
186-
total_value_all_donations
187-
end
188-
189-
def total_money_raised(donations)
190-
donations.sum { |d| d.money_raised.to_i }
191-
end
192154
end

app/models/view/donations.rb

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
module View
2+
Donations = Data.define(
3+
:donations,
4+
:filters,
5+
:item_categories,
6+
:paginated_donations,
7+
:product_drives,
8+
:product_drive_participants,
9+
:storage_locations,
10+
:donation_sites,
11+
:manufacturers
12+
) do
13+
include DateRangeHelper
14+
15+
class << self
16+
def filter_params(params)
17+
if params.key?(:filters)
18+
params.require(:filters).permit(
19+
:at_storage_location, :by_source, :from_donation_site,
20+
:by_product_drive, :by_product_drive_participant,
21+
:from_manufacturer, :by_category
22+
)
23+
else
24+
{}
25+
end
26+
end
27+
28+
def from_params(params:, organization:, helpers:)
29+
filters = filter_params(params)
30+
donations = organization.donations
31+
.includes(:storage_location,
32+
:donation_site,
33+
:product_drive,
34+
:product_drive_participant,
35+
:manufacturer,
36+
line_items: [:item])
37+
.order(created_at: :desc)
38+
.class_filter(filters)
39+
.during(helpers.selected_range)
40+
41+
paginated_donations = donations.page(params[:page])
42+
43+
storage_locations = donations.filter_map do |donation|
44+
donation.storage_location unless donation.storage_location.discarded_at
45+
end.compact.uniq.sort
46+
47+
manufacturers = donations.collect(&:manufacturer).compact.uniq.sort
48+
49+
new(
50+
donations: donations,
51+
filters: filters,
52+
item_categories: organization.item_categories.pluck(:name).uniq,
53+
paginated_donations: paginated_donations,
54+
product_drives: organization.product_drives.alphabetized,
55+
product_drive_participants: organization.product_drive_participants.alphabetized,
56+
storage_locations: storage_locations,
57+
donation_sites: donations.map(&:donation_site).compact.uniq.sort_by { |site| site.name.downcase },
58+
manufacturers: manufacturers
59+
)
60+
end
61+
end
62+
63+
def selected_storage_location
64+
filters[:at_storage_location]
65+
end
66+
67+
def selected_source
68+
filters[:by_source]
69+
end
70+
71+
def selected_item_category
72+
filters[:by_category]
73+
end
74+
75+
def sources
76+
donations.map(&:source).uniq.sort
77+
end
78+
79+
def donations_quantity
80+
donations.map(&:total_quantity).sum
81+
end
82+
83+
def selected_donation_site
84+
filters[:from_donation_site]
85+
end
86+
87+
def selected_product_drive
88+
filters[:by_product_drive]
89+
end
90+
91+
def selected_product_drive_participant
92+
filters[:by_product_drive_participant]
93+
end
94+
95+
def selected_manufacturer
96+
filters[:from_manufacturer]
97+
end
98+
99+
def paginated_donations_quantity
100+
paginated_donations.map(&:total_quantity).sum
101+
end
102+
103+
def paginated_in_kind_value
104+
paginated_donations.sum { |donation| donation.value_per_itemizable }
105+
end
106+
107+
def total_money_raised
108+
donations.sum { |d| d.money_raised.to_i }
109+
end
110+
111+
def total_value_all_donations
112+
donations.sum { |donation| donation.value_per_itemizable }
113+
end
114+
end
115+
end

app/views/donations/index.html.erb

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,47 +36,63 @@
3636
<div class="card-body">
3737
<%= form_tag(donations_path, method: :get) do |f| %>
3838
<div class="row">
39-
<% if @storage_locations.present? %>
39+
<% if @donation_info.storage_locations.present? %>
4040
<div class="form-group col-lg-3 col-md-4 col-sm-6 col-xs-12">
41-
<%= filter_select(label: "Filter by Storage Location", scope: :at_storage_location, collection: @storage_locations, selected: @selected_storage_location) %>
41+
<%= filter_select(label: "Filter by Storage Location",
42+
scope: :at_storage_location,
43+
collection: @donation_info.storage_locations,
44+
selected: @donation_info.selected_storage_location) %>
4245
</div>
4346
<% end %>
44-
<% if @sources.present? %>
47+
<% if @donation_info.sources.present? %>
4548
<div class="form-group col-lg-3 col-md-4 col-sm-6 col-xs-12">
4649
<% id = "filter_#{SecureRandom.uuid}" %>
4750
<%= label_tag id, "Filter by Source" %>
4851
<%= select_tag "filters[by_source]",
49-
options_for_select(@sources, @selected_source),
52+
options_for_select(@donation_info.sources, @donation_info.selected_source),
5053
{ include_blank: true, class: "form-control", id: id } %>
5154
</div>
5255
<% end %>
53-
<% if @product_drives.present? %>
56+
<% if @donation_info.product_drives.present? %>
5457
<div class="form-group col-lg-3 col-md-4 col-sm-6 col-xs-12">
55-
<%= filter_select(scope: :by_product_drive, collection: @product_drives, selected: @selected_product_drive) %>
58+
<%= filter_select(scope: :by_product_drive,
59+
collection: @donation_info.product_drives,
60+
selected: @donation_info.selected_product_drive) %>
5661

5762
</div>
5863
<% end %>
59-
<% if @product_drive_participants.present? %>
64+
<% if @donation_info.product_drive_participants.present? %>
6065
<div class="form-group col-lg-3 col-md-4 col-sm-6 col-xs-12">
61-
<%= filter_select(scope: :by_product_drive_participant, collection: @product_drive_participants, value: :business_name, selected: @selected_product_drive_participant) %>
66+
<%= filter_select(scope: :by_product_drive_participant,
67+
collection: @donation_info.product_drive_participants,
68+
value: :business_name,
69+
selected: @donation_info.selected_product_drive_participant) %>
6270
</div>
6371
<% end %>
64-
<% if @manufacturers.present? %>
72+
<% if @donation_info.manufacturers.present? %>
6573
<div class="form-group col-lg-3 col-md-4 col-sm-6 col-xs-12">
66-
<%= filter_select(label: "Filter by manufacturer", scope: :from_manufacturer, collection: @manufacturers, selected: @selected_manufacturer) %>
74+
<%= filter_select(label: "Filter by manufacturer",
75+
scope: :from_manufacturer,
76+
collection: @donation_info.manufacturers,
77+
selected: @donation_info.selected_manufacturer) %>
6778
</div>
6879
<% end %>
69-
<% if @donation_sites.present? %>
80+
<% if @donation_info.donation_sites.present? %>
7081
<div class="form-group col-lg-3 col-md-4 col-sm-6 col-xs-12">
71-
<%= filter_select(label: "Filter by Donation Site", scope: :from_donation_site, collection: @donation_sites, key: :id, value: :name, selected: @selected_donation_site) %>
82+
<%= filter_select(label: "Filter by Donation Site",
83+
scope: :from_donation_site,
84+
collection: @donation_info.donation_sites,
85+
key: :id,
86+
value: :name,
87+
selected: @donation_info.selected_donation_site) %>
7288
</div>
7389
<% end %>
74-
<% if @item_categories.present? %>
90+
<% if @donation_info.item_categories.present? %>
7591
<div class="form-group col-lg-3 col-md-4 col-sm-6 col-xs-12">
7692
<% id = "filter_#{SecureRandom.uuid}" %>
7793
<%= label_tag id, "Filter by Category" %>
7894
<%= select_tag "filters[by_category]",
79-
options_for_select(@item_categories, @selected_item_category),
95+
options_for_select(@donation_info.item_categories, @donation_info.selected_item_category),
8096
{ include_blank: true, class: "form-control", id: id } %>
8197
</div>
8298
<% end %>
@@ -89,7 +105,11 @@
89105
<%= filter_button %>
90106
<%= clear_filter_button %>
91107
<span class="float-right">
92-
<%= download_button_to(donations_path(format: :csv, filters: filter_params.merge(date_range: date_range_params)), {text: "Export Donations", size: "md"}) if @donations.any? %>
108+
<% if @donation_info.donations.any? %>
109+
<%= download_button_to(donations_path(format: :csv,
110+
filters: filter_params.merge(date_range: date_range_params)),
111+
{text: "Export Donations", size: "md"}) %>
112+
<% end %>
93113
<%= new_button_to new_donation_path, {text: "New Donation"} %>
94114
</span>
95115
</div>
@@ -124,7 +144,7 @@
124144
</tr>
125145
</thead>
126146
<tbody>
127-
<%= render partial: "donation_row", collection: @paginated_donations %>
147+
<%= render partial: "donation_row", collection: @donation_info.paginated_donations %>
128148
</tbody>
129149
<tfoot>
130150
<tr>
@@ -133,24 +153,24 @@
133153
<td></td>
134154
<td></td>
135155
<td class="numeric">
136-
<%= @paginated_donations_quantity %>
156+
<%= @donation_info.paginated_donations_quantity %>
137157
<br>
138158
(This page)
139159
<br>
140160
<strong id="donation_quantity">
141-
<%= @donations_quantity %>
161+
<%= @donation_info.donations_quantity %>
142162
<br>
143163
(Total)
144164
</strong>
145165
</td>
146-
<td class="numeric"><strong><%= dollar_value(@total_money_raised) %></strong></td>
166+
<td class="numeric"><strong><%= dollar_value(@donation_info.total_money_raised) %></strong></td>
147167
<td class="numeric in-kind">
148-
<%= dollar_value(@paginated_in_kind_value) %>
168+
<%= dollar_value(@donation_info.paginated_in_kind_value) %>
149169
<br>
150170
(This page)
151171
<br>
152172
<strong>
153-
<%= dollar_value(@total_value_all_donations) %> (Total)
173+
<%= dollar_value(@donation_info.total_value_all_donations) %> (Total)
154174
</strong>
155175
</td>
156176
</tr>
@@ -159,7 +179,7 @@
159179
</div>
160180
<!-- /.card-body -->
161181
<div class="card-footer clearfix">
162-
<%= paginate @paginated_donations %>
182+
<%= paginate @donation_info.paginated_donations %>
163183
</div>
164184
<!-- /.card-footer-->
165185
</div>

spec/controllers/donations_controller_spec.rb

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -147,34 +147,4 @@
147147
end
148148
end
149149
end
150-
151-
context 'calculating total value of multiple donations' do
152-
it 'works correctly for multiple line items per donation' do
153-
donations = [
154-
create(:donation, :with_items, item_quantity: 1),
155-
create(:donation, :with_items, item_quantity: 2)
156-
]
157-
value = subject.send(:total_value, donations) # private method, need to use `send`
158-
expect(value).to eq(300)
159-
end
160-
161-
it 'returns zero for an empty array of donations' do
162-
expect(subject.send(:total_value, [])).to be_zero # private method, need to use `send`
163-
end
164-
end
165-
166-
context 'calculating total money raised for all donations' do
167-
it 'correctly calculates the total' do
168-
donations = [
169-
create(:donation, money_raised: 2),
170-
create(:donation, money_raised: 3)
171-
]
172-
value = subject.send(:total_money_raised, donations) # private method, need to use `send`
173-
expect(value).to eq(5)
174-
end
175-
176-
it 'returns zero for an empty array of donations' do
177-
expect(subject.send(:total_money_raised, [])).to be_zero # private method, need to use `send`
178-
end
179-
end
180150
end

0 commit comments

Comments
 (0)