Merge branch 'b-3.6.1' of ../kivitendo-erp_20220811
[kivitendo-erp.git] / SL / Controller / Helper / ReportGenerator.pm
1 package SL::Controller::Helper::ReportGenerator;
2
3 use strict;
4
5 use Carp;
6 use List::Util qw(max);
7 use Scalar::Util qw(blessed);
8
9 use SL::Common;
10 use SL::MoreCommon;
11 use SL::ReportGenerator;
12
13 use Exporter 'import';
14 our @EXPORT = qw(
15   action_report_generator_export_as_pdf action_report_generator_export_as_csv
16   action_report_generator_back report_generator_do
17   report_generator_list_objects
18 );
19
20 sub _setup_action_bar {
21   my ($self, $type) = @_;
22
23   my $key   = $::form->{CONTROLLER_DISPATCH} ? 'action'                             : 'report_generator_form.report_generator_dispatch_to';
24   my $value = $::form->{CONTROLLER_DISPATCH} ? $::form->{CONTROLLER_DISPATCH} . "/" : '';
25
26   for my $bar ($::request->layout->get('actionbar')) {
27     $bar->add(
28       action => [
29         $type eq 'pdf' ? $::locale->text('PDF export') : $::locale->text('CSV export'),
30         submit => [ '#report_generator_form', { $key => "${value}report_generator_export_as_${type}" } ],
31       ],
32       action => [
33         $::locale->text('Back'),
34         submit => [ '#report_generator_form', { $key => "${value}report_generator_back" } ],
35       ],
36     );
37   }
38 }
39
40 sub action_report_generator_export_as_pdf {
41   my ($self) = @_;
42
43   delete $::form->{action_report_generator_export_as_pdf};
44
45   if ($::form->{report_generator_pdf_options_set}) {
46     my $saved_form = save_form();
47
48     $self->report_generator_do('PDF');
49
50     if ($::form->{report_generator_printed}) {
51       restore_form($saved_form);
52       $::form->{MESSAGE} = $::locale->text('The list has been printed.');
53       $self->report_generator_do('HTML');
54     }
55
56     return;
57   }
58
59   my @form_values = $::form->flatten_variables(grep { ($_ ne 'login') && ($_ ne 'password') } keys %{ $::form });
60
61   $::form->get_lists('printers' => 'ALL_PRINTERS');
62   map { $_->{selected} = $::myconfig{default_printer_id} == $_->{id} } @{ $::form->{ALL_PRINTERS} };
63
64   $::form->{copies} = max $::myconfig{copies} * 1, 1;
65   $::form->{title} = $::locale->text('PDF export -- options');
66
67   _setup_action_bar($self, 'pdf'); # Sub not exported, therefore don't call via object.
68
69   $::form->header;
70   print $::form->parse_html_template('report_generator/pdf_export_options', {
71     'HIDDEN'               => \@form_values,
72     'ALLOW_FONT_SELECTION' => SL::ReportGenerator->check_for_pdf_api, });
73 }
74
75 sub action_report_generator_export_as_csv {
76   my ($self) = @_;
77
78   delete $::form->{action_report_generator_export_as_csv};
79
80   if ($::form->{report_generator_csv_options_set}) {
81     $self->report_generator_do('CSV');
82     return;
83   }
84
85   my @form_values = $::form->flatten_variables(grep { ($_ ne 'login') && ($_ ne 'password') } keys %{ $::form });
86
87   $::form->{title} = $::locale->text('CSV export -- options');
88
89   _setup_action_bar($self, 'csv'); # Sub not exported, therefore don't call via object.
90
91   $::form->header;
92   print $::form->parse_html_template('report_generator/csv_export_options', { 'HIDDEN' => \@form_values });
93 }
94
95 sub action_report_generator_back {
96   $_[0]->report_generator_do('HTML');
97 }
98
99 sub report_generator_do {
100   my ($self, $format)  = @_;
101
102   my $nextsub = $::form->{report_generator_nextsub};
103   if (!$nextsub) {
104     $::form->error($::locale->text('report_generator_nextsub is not defined.'));
105   }
106
107   foreach my $key (split m/ +/, $::form->{report_generator_variable_list}) {
108     $::form->{$key} = $::form->{"report_generator_hidden_${key}"};
109   }
110
111   $::form->{report_generator_output_format} = $format;
112
113   delete @{$::form}{map { "report_generator_$_" } qw(nextsub variable_list)};
114
115   $self->_run_action($nextsub);
116 }
117
118 sub report_generator_list_objects {
119   my ($self, %params) = @_;
120
121   croak "Parameter 'objects' must exist and be an array reference"                if                      ref($params{objects}) ne 'ARRAY';
122   croak "Parameter 'report' must exist and be an instance of SL::ReportGenerator" if                      ref($params{report})  ne 'SL::ReportGenerator';
123   croak "Parameter 'options', if exists, must be a hash reference"                if $params{options} && (ref($params{options}) ne 'HASH');
124   $params{layout} //= 1;
125
126   my $column_defs = $params{report}->{columns};
127   my @columns     = $params{report}->get_visible_columns('HTML');
128
129   for my $obj (@{ $params{objects} || [] }) {
130     my %data;
131
132     if (blessed($obj) && $obj->isa('SL::Controller::Helper::ReportGenerator::ControlRow::Base')) {
133       $obj->set_data($params{report});
134       next;
135
136     } else {
137       %data = map {
138         my $def = $column_defs->{$_};
139         my $tmp;
140         $tmp->{raw_data} = $def->{raw_data} ? $def->{raw_data}->($obj) : '';
141         $tmp->{data}     = $def->{sub}      ? $def->{sub}->($obj)
142                          : $obj->can($_)    ? $obj->$_
143                          :                    $obj->{$_};
144         $tmp->{link}     = $def->{obj_link} ? $def->{obj_link}->($obj) : '';
145         $_ => $tmp;
146       } @columns;
147     }
148
149     $params{data_callback}->(\%data) if $params{data_callback};
150
151     $params{report}->add_data(\%data);
152   }
153
154   my %options            = %{ $params{options} || {} };
155   $options{action_bar} //= $params{action_bar} // 1;
156
157   if ($params{layout}) {
158     return $params{report}->generate_with_headers(%options);
159   } else {
160     my $html = $params{report}->generate_html_content(action_bar => 0, %options);
161     $self->render(\$html , { layout => 0, process => 0 });
162   }
163 }
164
165 1;
166 __END__
167
168 =pod
169
170 =encoding utf8
171
172 =head1 NAME
173
174 SL::Controller::Helper::ReportGenerator - Mixin for controllers that
175 use the L<SL::ReportGenerator> class
176
177 =head1 SYNOPSIS
178
179   package SL::Controller::Unicorn;
180
181   use SL::Controller::Helper::ReportGenerator;
182
183   sub action_list {
184     my ($self) = @_;
185
186     # Set up the report generator instance. In this example this is
187     # hidden in "prepare_report".
188     my $report = $self->prepare_report;
189
190     # Get objects from database.
191     my $orders = SL::DB::Manager::Order->get_all(...);
192
193     # Let report generator create the output.
194     $self->report_generator_list_objects(
195       report  => $report,
196       objects => $orders,
197     );
198   }
199
200 =head1 FUNCTIONS
201
202 =over 4
203
204 =item C<action_report_generator_back>
205
206 This is the controller action that's called from the one of the report
207 generator's 'export options' pages when the user clicks on the 'back'
208 button.
209
210 It is never called from a controller manually and should just work
211 as-is.
212
213 =item C<action_report_generator_export_as_csv>
214
215 This is the controller action that's called from the generated report
216 when the user wants to export as CSV. First the CSV export options are
217 shown and afterwards the CSV file is generated and offered for
218 download.
219
220 It is never called from a controller manually and should just work
221 as-is.
222
223 =item C<action_report_generator_export_as_pdf>
224
225 This is the controller action that's called from the generated report
226 when the user wants to export as PDF. First the PDF export options are
227 shown and afterwards the PDF file is generated and offered for
228 download.
229
230 It is never called from a controller manually and should just work
231 as-is.
232
233 =item C<report_generator_do>
234
235 This is a common function that's called from
236 L<action_report_generator_back>,
237 L<action_report_generator_export_as_csv> and
238 L<action_report_generator_export_as_pdf>. It handles common options
239 and report generation after options have been set.
240
241 It is never called from a controller manually and should just work
242 as-is.
243
244 =item C<report_generator_list_objects %params>
245
246 Iterates over all objects, creates the actual rows of data, hands them
247 over to the report generator and lets the report generator create the
248 output.
249
250 C<%params> can contain the following values:
251
252 =over 2
253
254 =item C<report>
255
256 Mandatory. An instance of L<SL::ReportGenerator> that has been set up
257 already (column definitions, title, sort handling etc).
258
259 =item C<objects>
260
261 Mandatory. An array reference of RDBO models to output.
262
263 An element of the array can also be an instance of a control row, i.e.
264 an instance of a class derived from
265 C<SL::Controller::Helper::ReportGenerator::ControlRow::Base>.
266 See also:
267
268 L<SL::Controller::Helper::ReportGenerator::ControlRow>
269 L<SL::Controller::Helper::ReportGenerator::ControlRow::*>
270
271 =item C<data_callback>
272
273 Optional. A callback handler (code reference) that gets called for
274 each row before it is passed to the report generator. The row passed
275 will be the handler's first and only argument (a hash reference). It's
276 the same hash reference that's passed to
277 L<SL::ReportGenrator/add_data>.
278
279 =item C<options>
280
281 An optional hash reference that's passed verbatim to the function
282 L<SL::ReportGenerator/generate_with_headers>.
283
284 =item C<action_bar>
285
286 If the buttons for exporting PDF and/or CSV variants are included in
287 the action bar. Otherwise they're rendered at the bottom of the page.
288
289 The value can be either a specific action bar instance or simply 1 in
290 which case the default action bar is used:
291 C<$::request-E<gt>layout-E<gt>get('actionbar')>.
292
293 =back
294
295 =back
296
297 =head1 BUGS
298
299 Nothing here yet.
300
301 =head1 AUTHOR
302
303 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
304
305 =cut