From d908c21abfbe0e2557333d1ebb9409d4c73cb1ec Mon Sep 17 00:00:00 2001
From: Mausam Pun
Date: Tue, 9 Jun 2026 12:41:46 +0545
Subject: [PATCH 1/2] feat: Add export product drive participants button to the
product drive show view
---
app/controllers/product_drives_controller.rb | 9 +++++
..._product_drive_participants_csv_service.rb | 39 +++++++++++++++++++
app/views/product_drives/show.html.erb | 12 ++++++
spec/requests/product_drives_requests_spec.rb | 28 +++++++++++++
4 files changed, 88 insertions(+)
create mode 100644 app/services/exports/export_product_drive_participants_csv_service.rb
diff --git a/app/controllers/product_drives_controller.rb b/app/controllers/product_drives_controller.rb
index 33826f3a79..7f1d527c13 100644
--- a/app/controllers/product_drives_controller.rb
+++ b/app/controllers/product_drives_controller.rb
@@ -74,6 +74,15 @@ def show
@selected_name_filter = filter_params[:by_name]
@selected_item_category = filter_params[:by_item_category_id]
@product_drive = current_organization.product_drives.includes(:donations).find(params[:id])
+
+ respond_to do |format|
+ format.html
+ format.csv do
+ send_data Exports::ExportProductDriveParticipantsCSVService.new(
+ @product_drive
+ ).generate_csv, filename: "Product-Drive-Participants-#{@product_drive.name}-#{Time.zone.today}.csv"
+ end
+ end
end
def update
diff --git a/app/services/exports/export_product_drive_participants_csv_service.rb b/app/services/exports/export_product_drive_participants_csv_service.rb
new file mode 100644
index 0000000000..2098cec8f0
--- /dev/null
+++ b/app/services/exports/export_product_drive_participants_csv_service.rb
@@ -0,0 +1,39 @@
+module Exports
+ class ExportProductDriveParticipantsCSVService
+ include ItemsHelper
+
+ HEADERS = ["Donation Id", "Product Drive Participant", "Storage Location", "Quantity", "In Kind Value"].freeze
+
+ def initialize(product_drive)
+ @product_drive = product_drive
+ end
+
+ def generate_csv
+ CSV.generate do |csv|
+ csv << generate_headers
+
+ product_drive.donations.includes(:product_drive_participant).find_each do |donation|
+ csv << generate_row(donation)
+ end
+ end
+ end
+
+ private
+
+ attr_reader :product_drive
+
+ def generate_row(donation)
+ [
+ donation.id.to_s,
+ donation.product_drive_participant_id ? donation.product_drive_participant.business_name : nil,
+ donation.storage_location.name,
+ donation.line_items.count(&:quantity),
+ dollar_value(donation.value_per_itemizable)
+ ]
+ end
+
+ def generate_headers
+ HEADERS
+ end
+ end
+end
diff --git a/app/views/product_drives/show.html.erb b/app/views/product_drives/show.html.erb
index d82a6fa2b3..f3c2b18223 100644
--- a/app/views/product_drives/show.html.erb
+++ b/app/views/product_drives/show.html.erb
@@ -95,6 +95,18 @@
To add additional donations to this product drive, please edit that donation and select this product drive from the appropriate dropdown.
+
diff --git a/spec/requests/product_drives_requests_spec.rb b/spec/requests/product_drives_requests_spec.rb
index f8d07e5e4b..5f7c812ad8 100644
--- a/spec/requests/product_drives_requests_spec.rb
+++ b/spec/requests/product_drives_requests_spec.rb
@@ -291,6 +291,34 @@
expect(response.body).to include("4862167")
end
+
+ context "csv" do
+ it 'is successful' do
+ product_drive = create(:product_drive, organization: organization)
+
+ get product_drive_path(id: product_drive.id, format: :csv)
+
+ expect(response).to be_successful
+ expect(response.header['Content-Type']).to include 'text/csv'
+
+ expected_headers = Exports::ExportProductDriveParticipantsCSVService::HEADERS
+ expect(response.body.chomp.split(",")).to eq(expected_headers)
+ end
+
+ it 'returns ONLY the associated product drive participants' do
+ product_drive = create(:product_drive, organization: organization)
+ participant = create(:product_drive_participant, business_name: "Associated Participant")
+ create(:donation, product_drive: product_drive, product_drive_participant: participant)
+
+ other_participant = create(:product_drive_participant, business_name: "Unassociated Participant")
+ create(:donation, product_drive: create(:product_drive, organization: organization), product_drive_participant: other_participant)
+
+ get product_drive_path(id: product_drive.id, format: :csv)
+
+ expect(response.body).to include("Associated Participant")
+ expect(response.body).not_to include("Unassociated Participant")
+ end
+ end
end
describe "DELETE #destroy" do
From f0bb30c6c4bea00179b47eb9bb1958cbdb793db0 Mon Sep 17 00:00:00 2001
From: Mausam Pun
Date: Sat, 13 Jun 2026 08:01:38 +0545
Subject: [PATCH 2/2] remove instantiation and move to class level method
---
app/controllers/product_drives_controller.rb | 5 +--
..._product_drive_participants_csv_service.rb | 44 ++++++++-----------
2 files changed, 20 insertions(+), 29 deletions(-)
diff --git a/app/controllers/product_drives_controller.rb b/app/controllers/product_drives_controller.rb
index 7f1d527c13..874957378f 100644
--- a/app/controllers/product_drives_controller.rb
+++ b/app/controllers/product_drives_controller.rb
@@ -78,9 +78,8 @@ def show
respond_to do |format|
format.html
format.csv do
- send_data Exports::ExportProductDriveParticipantsCSVService.new(
- @product_drive
- ).generate_csv, filename: "Product-Drive-Participants-#{@product_drive.name}-#{Time.zone.today}.csv"
+ send_data Exports::ExportProductDriveParticipantsCSVService.generate_csv(@product_drive),
+ filename: "Product-Drive-Participants-#{@product_drive.name}-#{Time.zone.today}.csv"
end
end
end
diff --git a/app/services/exports/export_product_drive_participants_csv_service.rb b/app/services/exports/export_product_drive_participants_csv_service.rb
index 2098cec8f0..f3810ea3cc 100644
--- a/app/services/exports/export_product_drive_participants_csv_service.rb
+++ b/app/services/exports/export_product_drive_participants_csv_service.rb
@@ -1,39 +1,31 @@
module Exports
class ExportProductDriveParticipantsCSVService
- include ItemsHelper
-
HEADERS = ["Donation Id", "Product Drive Participant", "Storage Location", "Quantity", "In Kind Value"].freeze
- def initialize(product_drive)
- @product_drive = product_drive
- end
+ class << self
+ include ItemsHelper
- def generate_csv
- CSV.generate do |csv|
- csv << generate_headers
+ def generate_csv(product_drive)
+ CSV.generate do |csv|
+ csv << HEADERS
- product_drive.donations.includes(:product_drive_participant).find_each do |donation|
- csv << generate_row(donation)
+ product_drive.donations.includes(:product_drive_participant).find_each do |donation|
+ csv << generate_row(donation)
+ end
end
end
- end
-
- private
- attr_reader :product_drive
+ private
- def generate_row(donation)
- [
- donation.id.to_s,
- donation.product_drive_participant_id ? donation.product_drive_participant.business_name : nil,
- donation.storage_location.name,
- donation.line_items.count(&:quantity),
- dollar_value(donation.value_per_itemizable)
- ]
- end
-
- def generate_headers
- HEADERS
+ def generate_row(donation)
+ [
+ donation.id.to_s,
+ donation.product_drive_participant_id ? donation.product_drive_participant.business_name : nil,
+ donation.storage_location.name,
+ donation.line_items.count(&:quantity),
+ dollar_value(donation.value_per_itemizable)
+ ]
+ end
end
end
end