diff --git a/app/controllers/reports/annual_reports_controller.rb b/app/controllers/reports/annual_reports_controller.rb index 76bc4aff23..e7d9ecfd6e 100644 --- a/app/controllers/reports/annual_reports_controller.rb +++ b/app/controllers/reports/annual_reports_controller.rb @@ -1,14 +1,14 @@ class Reports::AnnualReportsController < ApplicationController before_action :validate_show_params, only: [:show, :recalculate] + before_action :validate_range_params, only: [:range] def index # 2813_update_annual_report -- changed to earliest_reporting_year # so that we can do system tests and staging - foundation_year = current_organization.earliest_reporting_year + @foundation_year = current_organization.earliest_reporting_year + @current_year = Time.current.year - @actual_year = Time.current.year - - @years = (foundation_year...@actual_year).to_a + @years = (@foundation_year...@current_year).to_a @month_remaining_to_report = 12 - Time.current.month end @@ -32,13 +32,54 @@ def recalculate redirect_to reports_annual_report_path(year), notice: "Recalculated annual report!" end + def range + # Set range to be within valid reporting bounds + # Start year cannot be before org founding year + # End year cannot be after current year + year_start = [range_params[:year_start].to_i, current_organization.earliest_reporting_year].max + year_end = [range_params[:year_end].to_i, Time.current.year].min + + # Sort years if out of order + year_start, year_end = [year_start, year_end].minmax + + reports = get_range_report(year_start, year_end) + + respond_to do |format| + format.csv do + send_data Exports::ExportReportCSVService.new(reports:).generate_csv(range: true), + filename: "NdbnAnnuals-#{year_start}-#{year_end}.csv" + end + end + end + private + def get_range_report(year_start, year_end) + (year_start..year_end).map do |year| + Reports.retrieve_report(organization: current_organization, year: year, recalculate: true) + rescue ActiveRecord::RecordInvalid => e + Rails.logger.error("Failed to retrieve annual report for year #{year}: #{e.message}") + nil + end.compact + end + def year_param params.require(:year) end + def range_params + params.permit(:year_start, :year_end) + end + def validate_show_params not_found! unless year_param.to_i.positive? end + + def validate_range_params + not_found! unless range_params[:year_start] =~ year_regex && range_params[:year_end] =~ year_regex + end + + def year_regex + /^\d{4}$/ + end end diff --git a/app/services/exports/export_report_csv_service.rb b/app/services/exports/export_report_csv_service.rb index ad6c51da99..857feac77a 100644 --- a/app/services/exports/export_report_csv_service.rb +++ b/app/services/exports/export_report_csv_service.rb @@ -7,8 +7,8 @@ def initialize(reports:) @reports = reports end - def generate_csv - csv_data = generate_csv_data + def generate_csv(range: false) + csv_data = range ? generate_range_csv_data : generate_csv_data ::CSV.generate(headers: true) do |csv| csv_data.each { |row| csv << row } @@ -31,5 +31,22 @@ def generate_csv_data csv_data end + + def generate_range_csv_data + csv_data = [] + return csv_data if @reports.empty? + + headers = @reports.first.all_reports.flat_map { |r| r['entries'].keys } + csv_data << ['Year'] + headers + + @reports.each do |report| + report_data = report.all_reports + year = report['year'] + values = report_data.flat_map { |r| r['entries'].values } + csv_data << [year] + values + end + + csv_data + end end end diff --git a/app/views/reports/annual_reports/index.html.erb b/app/views/reports/annual_reports/index.html.erb index fbfffdfdb3..2b5b9453cc 100644 --- a/app/views/reports/annual_reports/index.html.erb +++ b/app/views/reports/annual_reports/index.html.erb @@ -27,6 +27,15 @@