simple_drilldown 0.7.2 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cee0cdbb64ae05c37521d974dba3f99c1ca6e3d459ff43d898f7bf5554227124
4
- data.tar.gz: 91532c9f27039c5f117a1c1a1fa43fb7bbcf3484dd4248b4207bd7d921f65d55
3
+ metadata.gz: 02a013f2b885c10991f53c854f8d127147b9ad141e1e80d80b2edef5ee7a2e8a
4
+ data.tar.gz: 949351a24e5171784d0c608126e835a19d736e8bd0248b8816728c37528632be
5
5
  SHA512:
6
- metadata.gz: b04ed3837f8f104088992a942557d50ab3889f1e5264662f27705b568f4828b29e9b9fd0c485babdf66ebf774613fea3d1a4a6d0ff13b6adb73bcc1f3a7eb796
7
- data.tar.gz: 2e685226b70f9472f530669f88f35517044499acce6a88b005bf2e0181a8643d8558190eeb038996c68e3e457de52573d68bc2ab59c669d8ca85a6ab572dc6db
6
+ metadata.gz: 3f0f84cedda3f43e60380c4ac010f7c0f5643c9e1e384efcafb86e2583d1a44e4920368c81a26542018b448960d3b3f89dcebd5f0682fd28064a00ce73f0fb4c
7
+ data.tar.gz: 749dab308a7a85d6cb5d8ae55f5ec4449d90e2c81b81ca1642fd6e048bf2a5730a7e6992f8eb0204762b067c1c371b6b743c38f071cb3de9bddfe16ec0f53a4a
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ def excel_header_row(sheet)
4
+ padding_cells = @dimensions.empty? ? 1 : @dimensions.size
5
+ row = (1...(padding_cells - 1)).map { |_n| nil }
6
+ @search.fields.each_with_index { |field, _i| row << t(field) }
7
+ sheet.add_row row
8
+ end
9
+
10
+ def excel_row(sheet, wrap, transaction:)
11
+ padding_cells = @dimensions.empty? ? 1 : @dimensions.size
12
+ row = (1..(padding_cells - 1)).map { |_n| nil }
13
+
14
+ @search.fields.each_with_index do |field, _i|
15
+ value = if field == 'time'
16
+ (transaction.respond_to?(:completed_at) ? transaction.completed_at : transaction.created_at)
17
+ .localtime.strftime('%Y-%m-%d %H:%M')
18
+ elsif controller.c_fields[field.to_sym][:attr_method]
19
+ controller.c_fields[field.to_sym][:attr_method].call(transaction)
20
+ else
21
+ transaction.send(field)
22
+ end
23
+
24
+ field_def = controller.c_fields[field.to_sym]
25
+ if @search.list_change_times && field_def[:list_change_times] && transaction.assignment.try(:order)
26
+ changes = transaction.assignment.order.send("#{field}_changes")
27
+ .map do |al|
28
+ [al.created_at.localtime.strftime('%H:%M'),
29
+ PacMan.yaml_load(al.new_values).symbolize_keys[field.to_sym]]
30
+ end
31
+ last_change = changes.last.try(:[], 1)
32
+ value =
33
+ "#{changes.map { |al| al.join(' ') }.join("\n")}#{"\nActual: #{value}" if value != last_change}"
34
+ end
35
+
36
+ row << value
37
+ end
38
+ sheet.add_row row, style: wrap
39
+ end
40
+
41
+ sheet.add_row []
42
+ excel_header_row(sheet)
43
+ wrap = sheet.styles.add_style alignment: { wrap_text: true }
44
+ result[:records].each { |t| excel_row(sheet, wrap, transaction: t) }
45
+ sheet.add_row []
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ row = [
4
+ *(1..(dimension - headers.size - 1)).map { |_n| '' },
5
+ *headers.map.with_index { |h, i| value_label(@dimensions.size - headers.size + i - 1, h[:value]) },
6
+ *(value_label(dimension - 1, result[:value]) if dimension.positive?),
7
+ *((result[:count].to_f / parent_result[:count]).inspect if parent_result && @search.percent),
8
+ result[:count].inspect,
9
+ *(if parent_result && @search.percent
10
+ (
11
+ if parent_result[controller.c_summary_fields[0]].positive?
12
+ result[controller.c_summary_fields[0]].to_f / parent_result[controller.c_summary_fields[0]]
13
+ else
14
+ 0
15
+ end).inspect
16
+ end),
17
+ result[controller.c_summary_fields[0]].inspect
18
+ ]
19
+ if controller.c_summary_fields.size > 1
20
+ if parent_result && @search.percent
21
+ percent =
22
+ if parent_result[controller.c_summary_fields[1]].positive?
23
+ result[controller.c_summary_fields[1]].to_f / parent_result[controller.c_summary_fields[1]]
24
+ else
25
+ 0
26
+ end
27
+ row << percent.inspect
28
+ end
29
+ row << result[controller.c_summary_fields[1]].inspect
30
+ end
31
+ sheet.add_row row
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ row = (1..dimension).map { |_n| nil }
4
+
5
+ (@dimensions.size - dimension).times { |_i| row << nil }
6
+ row << result[:count]
7
+ sheet.add_row row
@@ -1,2 +1,3 @@
1
1
  <%= link_to 'Excel', @search.url_options.merge(action: :excel_export) %>&nbsp;|
2
+ <%= link_to 'Excel (XML)', @search.url_options.merge(action: :excel_export, format: :xml) %>&nbsp;|
2
3
  <%= link_to 'HTML', @search.url_options.merge(action: :html_export), data_popup: ['Elections', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes, width=1024px'] %>
@@ -1,11 +1,13 @@
1
1
  <% unless result[:records].empty? %>
2
2
  <tr>
3
3
  <td colspan="<%= controller.c_summary_fields.size + 1 %>">
4
- <table class="table table-condensed table-bordered" style="padding-bottom: 10px;">
4
+ <table id="drilldown-records-<%= result[:value] %>" class="table table-condensed table-bordered" style="padding-bottom: 10px;">
5
5
  <%= render :partial => '/drilldown/row_header' %>
6
- <% result[:records].each do |t| %>
7
- <%= render :partial => '/drilldown/row', :locals => { :transaction => t, :previous_transaction => nil, :errors => [], :error_row => false, :meter1_errors => false } %>
8
- <% end %>
6
+ <tbody>
7
+ <% result[:records].each do |t| %>
8
+ <%= render :partial => '/drilldown/row', :locals => { :transaction => t, :previous_transaction => nil, :errors => [], :error_row => false, :meter1_errors => false } %>
9
+ <% end %>
10
+ </tbody>
9
11
  </table>
10
12
  </td>
11
13
  </tr>
@@ -1,12 +1,14 @@
1
- <table class="table table-condensed table-bordered">
2
- <tr>
3
- <% @dimensions.each do |d| %>
4
- <th><%=h d[:pretty_name]%></th>
5
- <% end %>
6
- <th><%= t controller.c_target_class.table_name.capitalize %></th>
7
- <%= controller.c_summary_fields.map{|l| "<th>#{t(l)}</th>"}.join("\n").html_safe %>
8
- </tr>
9
-
10
- <%=summary_row(@result) %>
11
-
1
+ <table id="drilldown-summary-table" class="table table-condensed table-bordered">
2
+ <thead>
3
+ <tr>
4
+ <% @dimensions.each do |d| %>
5
+ <th><%= h d[:pretty_name] %></th>
6
+ <% end %>
7
+ <th><%= t controller.c_target_class.table_name.capitalize %></th>
8
+ <%= controller.c_summary_fields.map { |l| "<th>#{t(l)}</th>" }.join("\n").html_safe %>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+ <%= summary_row(@result) %>
13
+ </tbody>
12
14
  </table>
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ def excel_summary_row_xlsx(sheet, result, parent_result = nil, dimension = 0, headers = [])
4
+ if result[:rows]
5
+ significant_rows = result[:rows].reject { |r| r[:row_count].zero? }
6
+ significant_rows.each_with_index do |r, i|
7
+ sub_headers =
8
+ if i.zero?
9
+ if dimension.zero?
10
+ headers
11
+ else
12
+ headers + [{
13
+ value: result[:value],
14
+ display_row_count: result[:nodes] + result[:row_count] * (@search.list ? 1 : 0)
15
+ }]
16
+ end
17
+ else
18
+ [] # [{:value => result[:value], :row_count => result[:row_count]}]
19
+ end
20
+ excel_summary_row_xlsx(sheet, r, result, dimension + 1, sub_headers)
21
+ end
22
+ else
23
+ render(partial: '/drilldown/excel_summary_row_xlsx',
24
+ locals: { sheet: sheet, result: result, parent_result: parent_result, headers: headers.dup,
25
+ dimension: dimension })
26
+
27
+ if @search.list
28
+ render(partial: '/drilldown/excel_record_list_xlsx',
29
+ locals: { sheet: sheet, result: result })
30
+ end
31
+ end
32
+
33
+ return if dimension >= @dimensions.size
34
+
35
+ render(partial: '/drilldown/excel_summary_total_row_xlsx', locals: {
36
+ sheet: sheet, result: result, headers: headers.dup, dimension: dimension
37
+ })
38
+ end
39
+
40
+ xlsx_package.use_shared_strings = true
41
+ wb = xlsx_package.workbook
42
+ wb.add_worksheet(name: 'Transaction Summary') do |sheet|
43
+ # header_colspan =
44
+ # if @search.list
45
+ # [@dimensions.size - 1, 0].max + @search.fields.size - 1
46
+ # else
47
+ # @dimensions.size - 1 + (controller.c_summary_fields.size + 1) * (@search.percent ? 2 : 1)
48
+ # end
49
+
50
+ # xml.Row 'ss:Height' => '18.75' do
51
+ # xml.Cell 'ss:MergeAcross' => header_colspan, 'ss:StyleID' => 'MainTitle' do
52
+ # xml.Data caption, 'ss:Type' => 'String'
53
+ # end
54
+ # end
55
+ sheet.add_row [caption]
56
+
57
+ # xml.Row 'ss:Height' => '15.75' do
58
+ # xml.Cell 'ss:MergeAcross' => header_colspan, 'ss:StyleID' => 'SubTitle' do
59
+ # xml.Data subcaption, 'ss:Type' => 'String'
60
+ # end
61
+ # end
62
+ sheet.add_row [subcaption]
63
+
64
+ # xml.Row do
65
+ # @dimensions.each do |d|
66
+ # xml.Cell 'ss:StyleID' => 'DimensionHeading' do
67
+ # xml.Data (h d[:pretty_name]).to_s, 'ss:Type' => 'String'
68
+ # end
69
+ # end
70
+ # xml.Cell 'ss:StyleID' => 'Heading', 'ss:MergeAcross' => @search.percent ? 1 : 0 do
71
+ # xml.Data t(controller.c_target_class.table_name), 'ss:Type' => 'String'
72
+ # end
73
+ # controller.c_summary_fields.each do |f|
74
+ # xml.Cell 'ss:StyleID' => 'Heading', 'ss:MergeAcross' => @search.percent ? 1 : 0 do
75
+ # xml.Data f, 'ss:Type' => 'String'
76
+ # end
77
+ # end
78
+ # end
79
+ sheet.add_row [
80
+ *@dimensions.map { |d| (h d[:pretty_name]).to_s },
81
+ t(controller.c_target_class.table_name),
82
+ *controller.c_summary_fields.map { |f| f }
83
+ ]
84
+
85
+ excel_summary_row_xlsx(sheet, @result)
86
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'caxlsx_rails'
3
4
  require 'simple_drilldown/engine'
4
5
 
5
6
  module SimpleDrilldown
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleDrilldown
4
+ # Allow tracking changes for a field
5
+ module Changes
6
+ def self.included(clazz)
7
+ clazz.extend ClassMethods
8
+ end
9
+
10
+ # Class methods for Changes
11
+ module ClassMethods
12
+ def changes_for(*fields)
13
+ fields.each do |field|
14
+ condition_proc = lambda do
15
+ in_join = is_a?(ActiveRecord::Associations::JoinDependency::JoinAssociation)
16
+ table_alias = in_join ? aliased_table_name : AuditLog.table_name
17
+ "#{table_alias}.new_values LIKE '%#{field}%'"
18
+ end
19
+ has_many :"#{field}_changes", -> { where(condition_proc.call).order(:created_at) },
20
+ class_name: :AuditLog, foreign_key: :record_id
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -15,7 +15,7 @@ module SimpleDrilldown
15
15
  class_attribute :c_default_fields, default: []
16
16
  class_attribute :c_default_select_value, default: SimpleDrilldown::Search::SelectValue::COUNT
17
17
  class_attribute :c_dimension_defs
18
- class_attribute :c_fields
18
+ class_attribute :c_fields, default: []
19
19
  class_attribute :c_list_includes, default: []
20
20
  class_attribute :c_list_order
21
21
  class_attribute :c_select, default: 'count(*) as count'
@@ -36,6 +36,9 @@ module SimpleDrilldown
36
36
  # No default target class found
37
37
  end
38
38
  end
39
+ return unless base.c_target_class.try :paranoid?
40
+
41
+ base.c_base_condition = "#{base.c_target_class.table_name}.deleted_at IS NULL"
39
42
  end
40
43
 
41
44
  def base_condition(base_condition)
@@ -150,14 +153,15 @@ module SimpleDrilldown
150
153
  end
151
154
  includes.uniq!
152
155
  end
153
- rows = c_target_class.unscoped.where(c_base_condition)
154
- .select("#{query[:select]} AS value")
155
- .where(filter_conditions || '1=1')
156
- .where(query[:where] || '1=1')
157
- .joins(make_join([], c_target_class.name.underscore.to_sym, includes))
158
- .order('value')
159
- .group(:value)
160
- .to_a
156
+ rows_query = c_target_class.unscoped.where(c_base_condition)
157
+ .select("#{query[:select]} AS value")
158
+ .joins(make_join([], c_target_class.name.underscore.to_sym, includes))
159
+ .order('value')
160
+ .group(:value)
161
+ # rows_query = rows_query.without_deleted if c_target_class.try :paranoid?
162
+ rows_query = rows_query.where(filter_conditions) if filter_conditions
163
+ rows_query = rows_query.where(query[:where]) if query[:where]
164
+ rows = rows_query.to_a
161
165
  filter_fields = search.filter[field.to_s]
162
166
  filter_fields&.each do |selected_value|
163
167
  next if rows.find { |r| r[:value].to_s == selected_value }
@@ -271,7 +275,8 @@ module SimpleDrilldown
271
275
  ass_order_prefixed.gsub!(/\b#{cname}\b/, "#{include_alias}.#{cname}")
272
276
  end
273
277
  paranoid_clause = 'AND t2.deleted_at IS NULL' if ass.klass.paranoid?
274
- # FIXME(uwe): Should we add "where" from the ScopeHolder here as well? Ref: DrilldownChanges#changes_for
278
+ # FIXME(uwe): Should we add "where" from the ScopeHolder here as well?
279
+ # Ref: SimpleDrilldown::Changes#changes_for
275
280
  min_query = <<~SQL
276
281
  SELECT MIN(#{ass_order}) FROM #{include_table} t2 WHERE t2.#{fk_col} = #{model_table}.id #{paranoid_clause}
277
282
  SQL
@@ -320,6 +325,7 @@ module SimpleDrilldown
320
325
 
321
326
  conditions, @filter_text, filter_includes = self.class.make_conditions(@search.filter)
322
327
  includes += filter_includes
328
+ includes.flatten!
323
329
  includes.keep_if(&:present?).uniq!
324
330
  if @search.order_by_value && @dimensions.size <= 1
325
331
  order = case @search.select_value
@@ -343,8 +349,8 @@ module SimpleDrilldown
343
349
  rows = c_target_class.unscoped.where(c_base_condition).select(select).where(conditions)
344
350
  .joins(joins)
345
351
  .group(group)
346
- .order(order).to_a
347
-
352
+ .order(order)
353
+ .to_a
348
354
  if rows.empty?
349
355
  @result = { value: 'All', count: 0, row_count: 0, nodes: 0, rows: [] }
350
356
  c_summary_fields.each { |f| @result[f] = 0 }
@@ -391,12 +397,19 @@ module SimpleDrilldown
391
397
 
392
398
  def excel_export
393
399
  index(false)
394
- set_excel_headers
395
- if params.dig(:search, :list) == '1'
396
- @records = get_records(@result)
397
- render template: '/drilldown/excel_export_records', layout: false
398
- else
399
- render template: '/drilldown/excel_export', layout: false
400
+ respond_to do |format|
401
+ format.xlsx do
402
+ render xlsx: c_target_class.table_name, template: 'drilldown/excel_export_xlsx'
403
+ end
404
+ format.xml do
405
+ set_excel_headers
406
+ if params.dig(:search, :list) == '1'
407
+ @records = get_records(@result)
408
+ render template: 'drilldown/excel_export_records', layout: false
409
+ else
410
+ render template: 'drilldown/excel_export', layout: false
411
+ end
412
+ end
400
413
  end
401
414
  end
402
415
 
@@ -38,7 +38,7 @@ module SimpleDrilldown
38
38
  html << summary_row(r, result, dimension + 1, sub_headers, i.positive?)
39
39
  end
40
40
  elsif @search.list
41
- html << render(partial: '/drilldown/record_list', locals: { result: result })
41
+ html << render(partial: '/drilldown/record_list', locals: { result: result, dimension: dimension })
42
42
  end
43
43
  if dimension < @dimensions.size
44
44
  html << render(partial: '/drilldown/summary_total_row',
@@ -89,7 +89,7 @@ module SimpleDrilldown
89
89
  private
90
90
 
91
91
  def caption_txt
92
- "#{controller.c_target_class} #{t(@search.select_value.downcase)}" +
92
+ "#{controller.c_target_class} #{I18n.t(@search.select_value.downcase)}" +
93
93
  (@dimensions && @dimensions.any? ? " by #{@dimensions.map { |d| d[:pretty_name] }.join(' and ')}" : '')
94
94
  end
95
95
  end
@@ -8,8 +8,11 @@ module SimpleDrilldown
8
8
  controller ||= path
9
9
  get "#{path}(.:format)" => "#{controller}#index", as: path
10
10
  scope path, controller: controller, as: path do
11
- %i[excel_export excel_export_records html_export].each { |action| get action }
12
- get "choices/:dimension_name", action: :choices, as: :choices
11
+ { excel_export: :xlsx, excel_export_records: :xlsx, html_export: :html }.each do |action, format|
12
+ get action, defaults: { format: format }
13
+ end
14
+ get 'choices/:dimension_name', action: :choices, as: :choices
15
+ yield if block_given?
13
16
  end
14
17
  end
15
18
  end
@@ -81,15 +81,15 @@ module SimpleDrilldown
81
81
 
82
82
  def url_options
83
83
  o = {
84
- search: {
85
- title: title,
86
- list: list ? '1' : '0',
87
- percent: percent ? '1' : '0',
88
- list_change_times: list_change_times ? '1' : '0',
89
- filter: filter,
90
- dimensions: dimensions,
91
- display_type: display_type,
92
- },
84
+ search: {
85
+ title: title,
86
+ list: list ? '1' : '0',
87
+ percent: percent ? '1' : '0',
88
+ list_change_times: list_change_times ? '1' : '0',
89
+ filter: filter,
90
+ dimensions: dimensions,
91
+ display_type: display_type,
92
+ },
93
93
  }
94
94
  o[:search][:fields] = fields unless fields == @default_fields
95
95
  o
@@ -1,3 +1,3 @@
1
1
  module SimpleDrilldown
2
- VERSION = '0.7.2'
2
+ VERSION = '0.8.2'
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_drilldown
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Uwe Kubosch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-03 00:00:00.000000000 Z
11
+ date: 2021-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: caxlsx_rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.6'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: chartkick
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -91,12 +105,13 @@ files:
91
105
  - app/mailers/simple_drilldown/application_mailer.rb
92
106
  - app/models/simple_drilldown/application_record.rb
93
107
  - app/views/drilldown/_chart.html.erb
94
- - app/views/drilldown/_excel_record_list.builder
95
- - app/views/drilldown/_excel_row.builder
108
+ - app/views/drilldown/_excel_record_list_xlsx.xlsx.axlsx
96
109
  - app/views/drilldown/_excel_row_header.builder
97
110
  - app/views/drilldown/_excel_styles.builder
98
111
  - app/views/drilldown/_excel_summary_row.builder
112
+ - app/views/drilldown/_excel_summary_row_xlsx.xlsx.axlsx
99
113
  - app/views/drilldown/_excel_summary_total_row.builder
114
+ - app/views/drilldown/_excel_summary_total_row_xlsx.xlsx.axlsx
100
115
  - app/views/drilldown/_export_links.html.erb
101
116
  - app/views/drilldown/_field.html.erb
102
117
  - app/views/drilldown/_fields.html.erb
@@ -114,6 +129,7 @@ files:
114
129
  - app/views/drilldown/data_3.builder
115
130
  - app/views/drilldown/excel_export.builder
116
131
  - app/views/drilldown/excel_export_records.builder
132
+ - app/views/drilldown/excel_export_xlsx.xlsx.axlsx
117
133
  - app/views/drilldown/html_export.html.erb
118
134
  - app/views/drilldown/index.html.erb
119
135
  - app/views/drilldown/print.html.erb
@@ -126,6 +142,7 @@ files:
126
142
  - lib/generators/drilldown_controller/templates/drilldown_controller.rb.erb
127
143
  - lib/generators/drilldown_controller/templates/drilldown_controller_test.rb.erb
128
144
  - lib/simple_drilldown.rb
145
+ - lib/simple_drilldown/changes.rb
129
146
  - lib/simple_drilldown/controller.rb
130
147
  - lib/simple_drilldown/engine.rb
131
148
  - lib/simple_drilldown/helper.rb
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- xml << render(partial: '/drilldown/excel_row_header')
4
-
5
- result[:records].each do |t|
6
- xml << render(
7
- partial: '/drilldown/excel_row',
8
- locals: { transaction: t, previous_transaction: nil, errors: [], error_row: false, meter1_errors: false }
9
- )
10
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- xml.Row do
4
- padding_cells = @dimensions.empty? ? 1 : @dimensions.size
5
- 1.upto(padding_cells - 1) { |_n| xml.Cell('ss:StyleID' => 'Outer') }
6
-
7
- @search.fields.each_with_index do |field, i|
8
- if field == 'time'
9
- value = ((
10
- if transaction.respond_to?(:completed_at)
11
- transaction.completed_at
12
- else
13
- transaction.created_at
14
- end)).localtime.strftime('%Y-%m-%d %H:%M')
15
- else
16
- value = if @transaction_fields_map[field.to_sym][:attr_method]
17
- @transaction_fields_map[field.to_sym][:attr_method].call(transaction)
18
- else
19
- transaction.send(field)
20
- end
21
- end
22
-
23
- field_def = @transaction_fields_map[field.to_sym]
24
-
25
- xml.Cell('ss:Index' => (padding_cells + i).to_s) do
26
- xml.Data value, 'ss:Type' => 'String'
27
- end
28
- end
29
- end