"alle" E-Mail-Adressen per Anhaken als Empfänger hinzufügen können
[kivitendo-erp.git] / SL / ReportGenerator.pm
index 6fc50e9..275e2e3 100644 (file)
@@ -2,11 +2,14 @@ package SL::ReportGenerator;
 
 use Data::Dumper;
 use List::Util qw(max);
+use Scalar::Util qw(blessed);
 use Text::CSV_XS;
 #use PDF::API2;    # these two eat up to .75s on startup. only load them if we actually need them
 #use PDF::Table;
 
 use strict;
+use SL::Helper::GlAttachments qw(append_gl_pdf_attachments);
+use SL::Helper::CreatePDF     qw(merge_pdfs);
 
 # Cause locales.pl to parse these files:
 # parse_html_template('report_generator/html_report')
@@ -47,6 +50,7 @@ sub new {
       'escape_char'         => '"',
       'eol_style'           => 'Unix',
       'headers'             => 1,
+      'encoding'            => 'UTF-8',
     },
   };
   $self->{export}   = {
@@ -161,7 +165,9 @@ sub set_options {
 
   while (my ($key, $value) = each %options) {
     if ($key eq 'pdf_export') {
-      map { $self->{options}->{pdf_export}->{$_} = $value->{$_} } keys %{ $value };
+      $self->{options}->{pdf_export}->{$_} = $value->{$_} for keys %{ $value };
+    } elsif ($key eq 'csv_export') {
+      $self->{options}->{csv_export}->{$_} = $value->{$_} for keys %{ $value };
     } else {
       $self->{options}->{$key} = $value;
     }
@@ -229,12 +235,13 @@ sub generate_with_headers {
   }
 
   if ($format eq 'html') {
+    my $content    = $self->generate_html_content(%params);
     my $title      = $form->{title};
     $form->{title} = $self->{title} if ($self->{title});
     $form->header(no_layout => $params{no_layout});
     $form->{title} = $title;
 
-    print $self->generate_html_content();
+    print $content;
 
   } elsif ($format eq 'csv') {
     # FIXME: don't do mini http in here
@@ -272,7 +279,7 @@ sub html_format {
 }
 
 sub prepare_html_content {
-  my $self = shift;
+  my ($self, %params) = @_;
 
   my ($column, $name, @column_headers);
 
@@ -287,6 +294,7 @@ sub prepare_html_content {
       'align'                    => $column->{align},
       'link'                     => $column->{link},
       'text'                     => $column->{text},
+      'raw_header_data'          => $column->{raw_header_data},
       'show_sort_indicator'      => $name eq $opts->{sort_indicator_column},
       'sort_indicator_direction' => $opts->{sort_indicator_direction},
     };
@@ -403,14 +411,63 @@ sub prepare_html_content {
     'DATA_PRESENT'         => $self->{data_present},
     'CONTROLLER_DISPATCH'  => $opts->{controller_class},
     'TABLE_CLASS'          => $opts->{table_class},
+    'SKIP_BUTTONS'         => !!$params{action_bar},
   };
 
   return $variables;
 }
 
+sub create_action_bar_actions {
+  my ($self, $variables) = @_;
+
+  my @actions;
+  foreach my $type (qw(pdf csv)) {
+    next unless $variables->{"ALLOW_" . uc($type) . "_EXPORT"};
+
+    my $key   = $variables->{CONTROLLER_DISPATCH} ? 'action' : 'report_generator_dispatch_to';
+    my $value = "report_generator_export_as_${type}";
+    $value    = $variables->{CONTROLLER_DISPATCH} . "/${value}" if $variables->{CONTROLLER_DISPATCH};
+
+    push @actions, action => [
+      $type eq 'pdf' ? $::locale->text('PDF export') : $::locale->text('CSV export'),
+      submit => [ '#report_generator_form', { $key => $value } ],
+    ];
+  }
+
+  if (scalar(@actions) > 1) {
+    @actions = (
+      combobox => [
+        action => [ $::locale->text('Export') ],
+        @actions,
+      ],
+    );
+  }
+
+  return @actions;
+}
+
+sub setup_action_bar {
+  my ($self, $variables, %params) = @_;
+
+  my @actions = $self->create_action_bar_actions($variables);
+
+  if ($params{action_bar_setup_hook}) {
+    $params{action_bar_setup_hook}->(@actions);
+
+  } elsif (@actions) {
+    my $action_bar = blessed($params{action_bar}) ? $params{action_bar} : ($::request->layout->get('actionbar'))[0];
+    $action_bar->add(@actions);
+  }
+}
+
 sub generate_html_content {
-  my $self      = shift;
-  my $variables = $self->prepare_html_content();
+  my ($self, %params) = @_;
+
+  $params{action_bar} //= 1;
+
+  my $variables = $self->prepare_html_content(%params);
+
+  $self->setup_action_bar($variables, %params) if $params{action_bar};
 
   my $stuff  = $self->{form}->parse_html_template($self->{options}->{html_template}, $variables);
   return $stuff;
@@ -429,6 +486,7 @@ sub generate_pdf_content {
   };
 
   my $self       = shift;
+  my %params     = @_;
   my $variables  = $self->prepare_html_content();
   my $form       = $self->{form};
   my $myconfig   = $self->{myconfig};
@@ -497,7 +555,9 @@ sub generate_pdf_content {
 
         foreach (0 .. $num_columns - 1) {
           push @{ $cell_props_row }, { 'background_color' => '#666666',
-                                       'font_color'       => '#ffffff',
+               #  BUG PDF:Table  -> 0.9.12:
+               # font_color is used in next row, so dont set font_color
+               #                       'font_color'       => '#ffffff',
                                        'colspan'          => $_ == 0 ? -1 : undef, };
         }
       }
@@ -654,13 +714,22 @@ sub generate_pdf_content {
 
   my $content = $pdf->stringify();
 
+  $main::lxdebug->message(LXDebug->DEBUG2(),"addattachments ?? =".$form->{report_generator_addattachments}." GL=".$form->{GL});
+  if ($form->{report_generator_addattachments} && $form->{GL}) {
+    $content = $self->append_gl_pdf_attachments($form,$content);
+  }
+
+  # 1. check if we return the report as binary pdf
+  if ($params{want_binary_pdf}) {
+    return $content;
+  }
+  # 2. check if we want and can directly print the report
   my $printer_command;
   if ($pdfopts->{print} && $pdfopts->{printer_id}) {
     $form->{printer_id} = $pdfopts->{printer_id};
     $form->get_printer_code($myconfig);
     $printer_command = $form->{printer_command};
   }
-
   if ($printer_command) {
     $self->_print_content('printer_command' => $printer_command,
                           'content'         => $content,
@@ -668,6 +737,7 @@ sub generate_pdf_content {
     $form->{report_generator_printed} = 1;
 
   } else {
+  # 3. default: redirect http with file attached
     my $filename = $self->get_attachment_basename();
 
     print qq|content-type: application/pdf\n|;
@@ -702,10 +772,10 @@ sub _print_content {
 }
 
 sub _handle_quoting_and_encoding {
-  my ($self, $text, $do_unquote) = @_;
+  my ($self, $text, $do_unquote, $encoding) = @_;
 
   $text = $main::locale->unquote_special_chars('HTML', $text) if $do_unquote;
-  $text = Encode::encode('UTF-8', $text);
+  $text = Encode::encode($encoding || 'UTF-8', $text);
 
   return $text;
 }
@@ -744,7 +814,7 @@ sub _generate_csv_content {
 
   if ($opts->{headers}) {
     if (!$self->{custom_headers}) {
-      $csv->print($stdout, [ map { $self->_handle_quoting_and_encoding($self->{columns}->{$_}->{text}, 1) } @visible_columns ]);
+      $csv->print($stdout, [ map { $self->_handle_quoting_and_encoding($self->{columns}->{$_}->{text}, 1, $opts->{encoding}) } @visible_columns ]);
 
     } else {
       foreach my $row (@{ $self->{custom_headers} }) {
@@ -752,7 +822,7 @@ sub _generate_csv_content {
 
         foreach my $col (@{ $row }) {
           my $num_output = ($col->{colspan} && ($col->{colspan} > 1)) ? $col->{colspan} : 1;
-          push @{ $fields }, ($self->_handle_quoting_and_encoding($col->{text}, 1)) x $num_output;
+          push @{ $fields }, ($self->_handle_quoting_and_encoding($col->{text}, 1, $opts->{encoding})) x $num_output;
         }
 
         $csv->print($stdout, $fields);
@@ -774,7 +844,7 @@ sub _generate_csv_content {
         my $num_output = ($row->{$col}{colspan} && ($row->{$col}->{colspan} > 1)) ? $row->{$col}->{colspan} : 1;
         $skip_next     = $num_output - 1;
 
-        push @data, join($eol, map { s/\r?\n/$eol/g; $self->_handle_quoting_and_encoding($_, 0) } @{ $row->{$col}->{data} });
+        push @data, join($eol, map { s/\r?\n/$eol/g; $self->_handle_quoting_and_encoding($_, 0, $opts->{encoding}) } @{ $row->{$col}->{data} });
         push @data, ('') x $skip_next if ($skip_next);
       }
 
@@ -819,7 +889,7 @@ Then there are too many results, you need pagination, you want to print or expor
 
 The ReportGenerator class was designed because this exact scenario happened about half a dozen times in kivitendo.
 It's purpose is to manage all those formating, culling, sorting, and templating.
-Which makes it almost as complicated to use as doing the work for yourself.
+Which makes it almost as complicated to use as doing the work by yourself.
 
 =head1 FUNCTIONS
 
@@ -839,16 +909,21 @@ Sets the order of columns. Any columns not present here are appended in alphabet
 
 =item set_sort_indicator $column,$direction
 
-Sets sorting ot the table by specifying a column and a direction, where the direction will be evaluated to ascending if true.
-Note that this is only for displaying. The data has to be presented already sorted.
+Sets sorting of the table by specifying a column and a direction, where the direction will be evaluated to ascending if true.
+Note that this is only for displaying. The data has to have already been sorted when it was added.
 
 =item add_data \@data
 
 =item add_data \%data
 
-Adds data to the report. A given hash_ref is interpreted as a single line of data, every array_ref as a collection of lines.
-Every line will be expected to be in a kay => value format. Note that the rows have to be already sorted.
-ReportGenerator does only colum sorting on its own, and provides links to sorting and visual cue as to which column was sorted by.
+Adds data to the report. A given hash_ref is interpreted as a single line of
+data, every array_ref as a collection of lines.  Every line will be expected to
+be in a key => value format. Note that the rows have to already have been
+sorted.
+
+The ReportGenerator is only able to display pre-sorted data and to indicate by
+which column and in which direction the data has been sorted via visual clues
+in the column headers. It also provides links to invert the sort direction.
 
 =item add_separator
 
@@ -857,12 +932,12 @@ Adds a separator line to the report.
 =item add_control \%data
 
 Adds a control element to the data. Control elements are an experimental feature to add functionality to a report the regular data cannot.
-Every control element needs to set IS_CONTROL_DATA, in order to be recongnized by the template.
+Every control element needs to set IS_CONTROL_DATA, in order to be recognized by the template.
 Currently the only control element is a colspan element, which can be used as a mini header further down the report.
 
 =item clear_data
 
-Deletes all data filled into the report, but keeps options set.
+Deletes all data added to the report, but keeps options set.
 
 =item set_options %options
 
@@ -896,7 +971,7 @@ Escapes HTML characters in $value and substitutes newlines with '<br>'. Returns
 =item prepare_html_content $column,$name,@column_headers
 
 Parses the data, and sets internal data needed for certain output format. Must be called once before the template is invoked.
-Should not be called extrenally, since all render and generate functions invoke it anyway.
+Should not be called externally, since all render and generate functions invoke it anyway.
 
 =item generate_html_content
 
@@ -906,6 +981,11 @@ The html generation function. Is invoked by generate_with_headers.
 
 The PDF generation function. It is invoked by generate_with_headers and renders the PDF with the PDF::API2 library.
 
+If the param want_binary_pdf is set, the binary pdf stream will be returned.
+If $pdfopts->{print} && $pdfopts->{printer_id} are set, the pdf will be printed (output is directed to print command).
+
+Otherwise and the default a html form with a downloadable file is returned.
+
 =item generate_csv_content
 
 The CSV generation function. Uses XS_CSV to parse the information into csv.
@@ -1020,6 +1100,10 @@ End of line style. Default is Unix.
 
 Include headers? Default is yes.
 
+=item encoding
+
+Character encoding. Default is UTF-8.
+
 =back
 
 =head1 SEE ALO