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