Files
openfoodnetwork/lib/reporting/report_row_builder.rb
Maikel Linke e37ec4b552 Avoid auto-formatting currencies
Each report can define formats for each column. But currency formatting
was also applied to all columns that had "price" in the name. Removing
this automation gives us more control and we can decide for each case.

At the moment, the currency formatting in Excel spreadsheets is not
ideal and it's easier to keep it as number.

This PR introduces a visual regression as prices are not formatted as
nicely but the columns can be used in calculations.
2022-06-20 13:18:41 +10:00

127 lines
3.4 KiB
Ruby

# frozen_string_literal: true
module Reporting
class ReportRowBuilder
include ActionView::Helpers::NumberHelper
include ActionView::Helpers::TagHelper
attr_reader :report
def initialize(report)
@report = report
end
# Compute the query result item into a result row
# We use OpenStruct to it's easier to access the properties
# i.e. row.my_field, rows.sum(&:quantity)
def build_row(item)
OpenStruct.new(
report.columns.transform_values do |column_constructor|
if column_constructor.is_a?(Symbol)
report.__send__(column_constructor, item)
else
column_constructor.call(item)
end
end
)
end
def slice_and_format_row(row)
result = row.to_h.reject { |k, _v| k.in?(report.fields_to_hide) }
unless report.raw_render?
result = result.map { |k, v| [k, format_cell(v, k)] }.to_h
end
OpenStruct.new(result)
end
def build_header(rule, group_value, group_datas)
return if rule[:header].blank?
rule[:header].call(group_value, group_datas.map(&:item), group_datas.map(&:full_row))
end
def build_summary_row(rule, group_value, datas)
return if rule[:summary_row].blank?
proc_args = [group_value, datas.map(&:item), datas.map(&:full_row)]
row = rule[:summary_row].call(*proc_args)
row = slice_and_format_row(OpenStruct.new(row.reverse_merge!(blank_row)))
add_summary_row_label(row, rule, proc_args)
end
private
def add_summary_row_label(row, rule, proc_args)
previous_key = nil
label = rule[:summary_row_label]
label = label.call(*proc_args) if label.respond_to?(:call)
# Adds Total before first non empty column
row.each_pair do |key, value|
if value.present? && previous_key.present? && row[previous_key].blank?
row[previous_key] = label and break
end
previous_key = key
end
row
end
def blank_row
report.columns.transform_values { |_v| "" }
end
# rubocop:disable Metrics/CyclomaticComplexity
def format_cell(value, column = nil)
return "" if value.nil?
# Currency
if report.columns_format[column] == :currency
format_currency(value)
# Quantity
elsif report.columns_format[column] == :quantity && report.html_render?
format_quantity(value)
# Numeric
elsif report.columns_format[column] == :numeric
format_numeric(value)
# Boolean
elsif value.in? [true, false]
format_boolean(value)
# Time
elsif value.is_a?(Time)
format_time(value)
# Date
elsif value.is_a?(Date)
format_date(value)
# Default
else
value
end
end
# rubocop:enable Metrics/CyclomaticComplexity
def format_currency(value)
value.present? ? number_to_currency(value, unit: Spree::Money.currency_symbol) : ""
end
def format_quantity(value)
content_tag(value > 1 ? :strong : :span, value)
end
def format_boolean(value)
value ? I18n.t(:yes) : I18n.t(:no)
end
def format_time(value)
value.to_datetime.in_time_zone.strftime "%Y-%m-%d %H:%M"
end
def format_date(value)
value.to_datetime.in_time_zone.strftime "%Y-%m-%d"
end
def format_numeric(value)
number_with_delimiter(value)
end
end
end