diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index d818e0b085..7493b555cf 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -21,6 +21,7 @@ class ReportsController < ApplicationController menu_item :issues before_action :find_project, :authorize, :find_issue_statuses + include ReportsHelper def issue_report with_subprojects = Setting.display_subprojects_issues? @trackers = @project.rolled_up_trackers(with_subprojects).visible @@ -82,6 +83,12 @@ class ReportsController < ApplicationController else render_404 end + respond_to do |format| + format.html + format.csv do + send_data(issue_report_details_to_csv(@field, @statuses, @rows, @data), :type => 'text/csv; header=present', :filename => "report-#{params[:detail]}.csv") + end + end end private diff --git a/app/helpers/reports_helper.rb b/app/helpers/reports_helper.rb index a01abf3a35..0a0bc7dcb8 100644 --- a/app/helpers/reports_helper.rb +++ b/app/helpers/reports_helper.rb @@ -44,4 +44,22 @@ module ReportsHelper parameters = {:set_filter => 1, :subproject_id => '!*', field => (row.id || '!*')}.merge(options) project_issues_path(row.is_a?(Project) ? row : project, parameters) end + + def issue_report_details_to_csv(field_name, statuses, rows, data) + Redmine::Export::CSV.generate(:encoding => params[:encoding]) do |csv| + # csv headers + headers = [''] + statuses.map(&:name) + [l(:label_open_issues_plural), l(:label_closed_issues_plural), l(:label_total)] + csv << headers + + # csv lines + rows.each do |row| + csv << + [row.name] + + statuses.map{|s| aggregate(data, { field_name => row.id, "status_id" => s.id })} + + [aggregate(data, { field_name => row.id, "closed" => 0 })] + + [aggregate(data, { field_name => row.id, "closed" => 1 })] + + [aggregate(data, { field_name => row.id })] + end + end + end end diff --git a/app/views/reports/_details.html.erb b/app/views/reports/_details.html.erb index 3361117fcd..3c64727434 100644 --- a/app/views/reports/_details.html.erb +++ b/app/views/reports/_details.html.erb @@ -25,6 +25,20 @@ <% end %> +<% other_formats_links do |f| %> + <%= f.link_to_with_query_parameters 'CSV', {}, :onclick => "showModal('csv-export-options', '330px'); return false;" %> +<% end %> + +
diff --git a/test/functional/reports_controller_test.rb b/test/functional/reports_controller_test.rb index 37536f114e..f9b6070afe 100644 --- a/test/functional/reports_controller_test.rb +++ b/test/functional/reports_controller_test.rb @@ -242,4 +242,91 @@ class ReportsControllerTest < Redmine::ControllerTest ) assert_response 404 end + + def test_issue_report_details_should_csv_export + %w(tracker version priority category assigned_to author subproject).each do |detail| + get( + :issue_report_details, + params: { + id: 1, + detail: detail, + format: 'csv' + } + ) + assert_response :success + assert_equal 'text/csv; header=present', response.media_type + end + end + + def test_issue_report_details_with_tracker_detail_should_csv_export + project = Project.find(1) + tracker = project.trackers.find_by(:name => 'Support request') + project.trackers.delete(tracker) + + with_settings :display_subprojects_issues => '1' do + get( + :issue_report_details, + params: { + id: 1, + detail: 'tracker', + format: 'csv' + } + ) + assert_response :success + + assert_equal 'text/csv; header=present', response.media_type + lines = response.body.chomp.split("\n") + # Number of lines + rows = Project.find(1).rolled_up_trackers(true).visible + assert_equal rows.size + 1, lines.size + # Header + assert_equal "\"\",New,Assigned,Resolved,Feedback,Closed,Rejected,open,closed,Total", lines.first + # Details + to_test = [ + 'Bug,5,0,0,0,3,0,5,3,8', + 'Feature request,0,1,0,0,0,0,1,0,1', + 'Support request,0,0,0,0,0,0,0,0,0' + ] + to_test.each do |expected| + assert_includes lines, expected + end + end + end + + def test_issue_report_details_with_assigned_to_detail_should_csv_export + Issue.delete_all + Issue.generate! + Issue.generate! + Issue.generate!(:status_id => 5) + Issue.generate!(:assigned_to_id => 2) + + with_settings :issue_group_assignment => '1' do + get( + :issue_report_details, + params: { + id: 1, + detail: 'assigned_to', + format: 'csv' + } + ) + assert_response :success + + assert_equal 'text/csv; header=present', response.media_type + lines = response.body.chomp.split("\n") + # Number of lines + rows = Project.find(1).principals.sorted + [I18n.t(:label_none)] + assert_equal rows.size + 1, lines.size + # Header + assert_equal "\"\",New,Assigned,Resolved,Feedback,Closed,Rejected,open,closed,Total", lines.first + # Details + to_test = [ + 'Dave Lopper,0,0,0,0,0,0,0,0,0', + 'John Smith,1,0,0,0,0,0,1,0,1', + '[none] ,2,0,0,0,1,0,2,1,3' + ] + to_test.each do |expected| + assert_includes lines, expected + end + end + end end