salvage xls export from stand alone xls_export plugin

pull/7991/head
ulferts 5 years ago
parent d08a441f46
commit 8d348755a2
No known key found for this signature in database
GPG Key ID: A205708DE1284017
  1. 4
      modules/xls_export/app/views/hooks/xls_report/_view_cost_report_other_formats.html.erb
  2. 6
      modules/xls_export/lib/open_project/xls_export/engine.rb
  3. 7
      modules/xls_export/lib/open_project/xls_export/hooks/cost_report_hook.rb
  4. 49
      modules/xls_export/lib/open_project/xls_export/patches/cost_reports_controller_patch.rb
  5. 65
      modules/xls_export/lib/open_project/xls_export/xls_views.rb
  6. 57
      modules/xls_export/lib/open_project/xls_export/xls_views/cost_entry_table.xls.rb
  7. 134
      modules/xls_export/lib/open_project/xls_export/xls_views/cost_report_table.xls.rb
  8. 52
      modules/xls_export/lib/open_project/xls_export/xls_views/simple_cost_report_table.xls.rb

@ -1,7 +1,7 @@
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'excel_export', :plugin => 'redmine_additional_formats' %>
<%= stylesheet_link_tag 'excel_export', :plugin => 'xls_export' %>
<% end %>
<% if User.current.allowed_to? :export_issues, @project, :global => @project.nil? %>
<% if User.current.allowed_to? :export_work_packages, @project, :global => @project.nil? %>
<%= link_to(l(:export_to_excel), { :controller => "cost_reports" , :action => :index,
:format => 'xls', :project_id => @project }, :class => "icon icon-export-xls-descr") %>
<% end %>

@ -8,8 +8,7 @@ module OpenProject::XlsExport
author_url: 'http://openproject.com/',
bundled: true
patches %i[Queries::WorkPackages::Columns::WorkPackageColumn]
# disabled since not yet migrated: :CostReportsController
patches %i[Queries::WorkPackages::Columns::WorkPackageColumn CostReportsController]
extend_api_response(:v3, :work_packages, :work_package_collection) do
require_relative 'patches/api/v3/export_formats'
@ -20,8 +19,7 @@ module OpenProject::XlsExport
initializer 'xls_export.register_hooks' do
# don't use require_dependency to not reload hooks in development mode
# disabled since not yet migrated
# require 'open_project/xls_export/hooks/cost_report_hook.rb'
require 'open_project/xls_export/hooks/cost_report_hook.rb'
require 'open_project/xls_export/hooks/work_package_hook.rb'
end

@ -0,0 +1,7 @@
# Hooks to attach to the Redmine Issues.
module XlsReport
class CostReportHook < Redmine::Hook::ViewListener
# Renders the Cost Object subject and basic costs information
render_on :view_cost_report_table_bottom, :partial => 'hooks/xls_report/view_cost_report_other_formats'
end
end

@ -0,0 +1,49 @@
module OpenProject::XlsExport::Patches
module CostReportsControllerPatch
def self.included(base) # :nodoc:
base.prepend InstanceMethods
end
module InstanceMethods
def excel_export?
(params["action"] == "index" or params[:action] == "all") && params["format"] == "xls"
end
def ensure_project_scope?
!excel_export? && super
end
# If the index action is called, hook the xls format into the cost report
def respond_to
if excel_export?
super do |format|
yield format
format.xls do
report = report_to_xls
time = DateTime.now.strftime('%d-%m-%Y-T-%H-%M-%S')
send_data(report, type: :xls, filename: "export-#{time}.xls") if report
end
end
else
super
end
end
# Build an xls file from a cost report.
def report_to_xls
options = { query: @query, project: @project, cost_types: @cost_types }
sb = if @query.group_bys.empty?
::OpenProject::XlsExport::XlsViews::CostEntryTable.generate(options)
elsif @query.depth_of(:column) + @query.depth_of(:row) == 1
::OpenProject::XlsExport::XlsViews::SimpleCostReportTable.generate(options)
else
::OpenProject::XlsExport::XlsViews::CostReportTable.generate(options)
end
sb.xls
end
end
end
end
CostReportsController.send(:include, OpenProject::XlsExport::Patches::CostReportsControllerPatch)

@ -0,0 +1,65 @@
class OpenProject::XlsExport::XlsViews
include Redmine::I18n
include ActionView::Helpers::NumberHelper
include ApplicationHelper
include ReportingHelper
include OpenProject::StaticRouting::UrlHelpers
attr_accessor :spreadsheet, :query, :cost_type, :unit_id, :options
# Overwrite a few mappings.
def field_representation_map(key, value)
case key.to_sym
when :units then value.to_i
when :spent_on then value
when :activity_id then mapped value, Enumeration, I18n.t(:caption_material_costs)
when :project_id then (I18n.t(:label_none) if value.to_i.zero?) or Project.find(value.to_i).name
when :user_id, :assigned_to_id then (I18n.t(:label_none) if value.to_i.zero?) or User.find(value.to_i).name
when :work_package_id
return I18n.t(:label_none) if value.to_i.zero?
work_package = WorkPackage.find(value.to_i)
"#{work_package.project.name + ' - ' if @project}#{work_package.type} ##{work_package.id}: #{work_package.subject}"
else super(key, value)
end
end
def show_result(row, unit_id = @unit_id, as_text = false)
return super(row, unit_id) if as_text
case unit_id
when 0 then row.real_costs || '-'
else row.units
end
end
def cost_type_unit_label(cost_type_id, cost_type_inst = nil, plural = true)
case cost_type_id
when -1 then l_hours(2).split[1..-1].join(" ") # get the plural for hours
when 0 then Setting.plugin_openproject_costs['costs_currency']
else cost_type_label(cost_type_id, cost_type_inst, plural)
end
end
def serialize_query_without_hidden(query)
serialized_query = query.serialize
serialized_query[:filters] = serialized_query[:filters].reject do |_, options|
options[:display] == false
end
serialized_query
end
def self.generate(opts)
new.tap do |obj|
obj.query = opts[:query]
obj.cost_type = opts[:cost_type]
obj.unit_id = opts[:unit_id]
obj.options = opts
end.generate
end
end
# Load subclasses
require_relative './xls_views/cost_entry_table.xls'
require_relative './xls_views/simple_cost_report_table.xls'
require_relative './xls_views/cost_report_table.xls'

@ -0,0 +1,57 @@
class OpenProject::XlsExport::XlsViews::CostEntryTable < OpenProject::XlsExport::XlsViews
def generate
spreadsheet = OpenProject::XlsExport::SpreadsheetBuilder.new(I18n.t(:label_money))
default_query = serialize_query_without_hidden(@query)
available_cost_type_tabs(options[:cost_types]).each_with_index do |ary, idx|
@query = CostQuery.deserialize(default_query)
@cost_type = nil
@unit_id = ary.first
name = ary.last
if @unit_id != 0
@query.filter :cost_type_id, operator: '=', value: @unit_id.to_s
@cost_type = CostType.find(unit_id) if unit_id.positive?
end
spreadsheet.worksheet(idx, name)
build_spreadsheet(spreadsheet)
end
spreadsheet
end
def build_spreadsheet(spreadsheet)
spreadsheet.add_title("#{@project.name + " >> " if @project}#{I18n.t(:cost_reports_title)} (#{format_date(Date.today)})")
list = %i[spent_on user_id activity_id issue_id comments project_id]
headers = list.collect { |field| label_for(field) }
headers << I18n.t(:units)
headers << I18n.t(:field_cost_type)
headers << I18n.t(:field_costs)
spreadsheet.add_headers(headers)
spreadsheet.add_format_option_to_column(headers.length - 1, number_format: number_to_currency(0.00))
spreadsheet.add_format_option_to_column(headers.length - 2, number_format: "0.0")
query.each_direct_result do |result|
row = list.collect { |field| show_field field, result.fields[field.to_s] }
current_cost_type_id = result.fields['cost_type_id'].to_i
row << show_result(result, current_cost_type_id) # units
row << cost_type_label(current_cost_type_id, @cost_type) # cost type
row << show_result(result, 0) # costs/currency
spreadsheet.add_row(row)
end
footer = [''] * list.size
footer += if show_result(query, 0) != show_result(query)
[show_result(query), '', show_result(query, 0)]
else
['', '', show_result(query)]
end
spreadsheet.add_row(footer) # footer
spreadsheet
end
end

@ -0,0 +1,134 @@
class OpenProject::XlsExport::XlsViews::CostReportTable < OpenProject::XlsExport::XlsViews
def final_row(final_row, cells)
row = [show_row(final_row)]
row += cells
row << show_result(final_row)
end
def row(row, subrows)
unless row.fields.empty?
# Here we get the border setting, vertically. The rowspan #{subrows.size} need be
# converted to a proper Excel bordering
subrows = subrows.inject([]) do |array, subrow|
if subrow.flatten == subrow
array << subrow
else
array += subrow.collect(&:flatten)
end
end
subrows.each_with_index do |subrow, idx|
if idx.to_i.zero?
subrow.insert(0, show_row(row))
subrow << show_result(row)
else
subrow.unshift("")
subrow << ""
end
end
end
subrows
end
def cell(result)
show_result result
end
def headers(list, first, first_in_col, last_in_col)
if first_in_col # Open a new header row
@header = [""] * query.depth_of(:row) # TODO: needs borders: rowspan=query.depth_of(:column)
end
list.each do |column|
@header << show_row(column)
@header += [""] * (column.final_number(:column) - 1).abs
end
if last_in_col # Finish this header row
@header += [""] * query.depth_of(:row) # TODO: needs borders: rowspan=query.depth_of(:column)
@headers << @header
end
end
def footers(list, first, first_in_col, last_in_col)
if first_in_col # Open a new footer row
@footer = [""] * query.depth_of(:row) # TODO: needs borders: rowspan=query.depth_of(:column)
end
list.each do |column|
@footer << show_result(column)
@footer += [""] * (column.final_number(:column) - 1).abs
end
if last_in_col # Finish this footer row
if first
@footer << show_result(query)
@footer += [""] * (query.depth_of(:row) - 1).abs # TODO: add rowspan=query.depth_of(:column)
else
@footer += [""] * query.depth_of(:row) # TODO: add rowspan=query.depth_of(:column)
end
@footers << @footer
end
end
def body(*line)
@rows += line.flatten
end
def generate
@spreadsheet ||= OpenProject::XlsExport::SpreadsheetBuilder.new(I18n.t(:label_money))
default_query = serialize_query_without_hidden(@query)
available_cost_type_tabs(options[:cost_types]).each_with_index do |ary, idx|
@query = CostQuery.deserialize(default_query)
@unit_id = ary.first
name = ary.last
if @unit_id != 0
@query.filter :cost_type_id, operator: '=', value: @unit_id.to_s
@cost_type = CostType.find(unit_id) if unit_id.positive?
end
spreadsheet.worksheet(idx, name)
run_walker
build_spreadsheet unless (@headers + @footers).empty?
end
spreadsheet
end
def run_walker
walker = query.walker
walker.for_final_row &method(:final_row)
walker.for_row &method(:row)
walker.for_empty_cell { "" }
walker.for_cell &method(:cell)
@headers = []
@header = []
walker.headers &method(:headers)
@footers = []
@footer = []
walker.reverse_headers &method(:footers)
@rows = []
walker.body &method(:body)
end
def build_spreadsheet
spreadsheet.add_title("#{@project.name + ' >> ' if @project}#{I18n.t(:cost_reports_title)} (#{format_date(Date.today)})")
spreadsheet.add_headers [label]
row_length = @headers.first.length
@headers.each { |head| spreadsheet.add_headers(head, spreadsheet.current_row) }
@rows.in_groups_of(row_length).each { |body| spreadsheet.add_row(body) }
@footers.each { |foot| spreadsheet.add_headers(foot, spreadsheet.current_row) }
spreadsheet
end
def label
"#{I18n.t(:caption_cost_type)}: " + case unit_id
when -1 then I18n.t(:field_hours)
when 0 then "EUR"
else cost_type.unit_plural
end
end
end

@ -0,0 +1,52 @@
class OpenProject::XlsExport::XlsViews::SimpleCostReportTable < OpenProject::XlsExport::XlsViews
def generate
spreadsheet = OpenProject::XlsExport::SpreadsheetBuilder.new(I18n.t(:label_money))
default_query = serialize_query_without_hidden(@query)
available_cost_type_tabs(options[:cost_types]).each_with_index do |ary, idx|
@query = CostQuery.deserialize(default_query)
@cost_type = nil
@unit_id = ary.first
name = ary.last
if @unit_id != 0
@query.filter :cost_type_id, operator: '=', value: @unit_id.to_s
@cost_type = CostType.find(unit_id) if unit_id.positive?
end
spreadsheet.worksheet(idx, name)
build_spreadsheet(spreadsheet)
end
spreadsheet
end
def build_spreadsheet(spreadsheet)
spreadsheet.add_title("#{@project.name + ' >> ' if @project}#{I18n.t(:cost_reports_title)} (#{format_date(Date.today)})")
list = query.collect { |r| r.important_fields }.flatten.uniq
show_units = list.include? "cost_type_id"
headers = list.collect { |field| label_for(field) }
headers << label_for(:field_units) << "" if show_units
headers << label_for(:label_sum) << ""
spreadsheet.add_headers(headers)
column = 0
spreadsheet.add_format_option_to_column(headers.length - (column += 1), number_format: number_to_currency(0.00))
spreadsheet.add_format_option_to_column(headers.length - (column += 1), number_format: "0.0 ?") if show_units
query.each do |result|
current_cost_type_id = result.fields[:cost_type_id].to_i
row = [show_row(result)]
row << show_result(result, current_cost_type_id) if show_units
row << cost_type_unit_label(current_cost_type_id, @cost_type) if show_units
row << show_result(result)
row << cost_type_unit_label(@unit_id, @cost_type)
spreadsheet.add_row(row)
end
footer = [''] * list.size
footer += ['', ''] if show_units
spreadsheet.add_row(footer + [show_result(query), cost_type_unit_label(@unit_id, @cost_type)]) # footer
spreadsheet
end
end
Loading…
Cancel
Save