X-Git-Url: http://wagnertech.de/gitweb/gitweb.cgi/kivitendo-erp.git/blobdiff_plain/f0ce00ebc34b16381f25c9ce75d36521bd046e1f..d0779d015d862645b9f63140f7e7af2596238c8e:/SL/ReportGenerator.pm diff --git a/SL/ReportGenerator.pm b/SL/ReportGenerator.pm index 4a48dfd84..29e2d9480 100644 --- a/SL/ReportGenerator.pm +++ b/SL/ReportGenerator.pm @@ -3,6 +3,7 @@ package SL::ReportGenerator; use IO::Wrap; use List::Util qw(max); use Text::CSV_XS; +use Text::Iconv; use SL::Form; @@ -37,7 +38,7 @@ sub new { 'quote_char' => '"', 'sep_char' => ';', 'escape_char' => '"', - 'eol_style' => 'DOS', + 'eol_style' => 'Unix', 'headers' => 1, }, }; @@ -46,9 +47,38 @@ sub new { 'variable_list' => '', }; + $self->{data_present} = 0; + + bless $self, $type; + $self->set_options(@_) if (@_); - return bless $self, $type; + $self->_init_escaped_strings_map(); + + return $self; +} + +sub _init_escaped_strings_map { + my $self = shift; + + $self->{escaped_strings_map} = { + 'ä' => 'ä', + 'ö' => 'ö', + 'ü' => 'ü', + 'Ä' => 'Ä', + 'Ö' => 'Ö', + 'Ü' => 'Ü', + 'ß' => 'ß', + '>' => '>', + '<' => '<', + '"' => '"', + }; + + my $iconv = $main::locale->{iconv_iso8859}; + + if ($iconv) { + map { $self->{escaped_strings_map}->{$_} = $iconv->convert($self->{escaped_strings_map}->{$_}) } keys %{ $self->{escaped_strings_map} }; + } } sub set_columns { @@ -93,18 +123,35 @@ sub add_data { my $last_row_set; while (my $arg = shift) { + my $row_set; + if ('ARRAY' eq ref $arg) { - push @{ $self->{data} }, $arg; - $last_row_set = $arg; + $row_set = $arg; } elsif ('HASH' eq ref $arg) { - my $row_set = [ $arg ]; - push @{ $self->{data} }, $row_set; - $last_row_set = $row_set; + $row_set = [ $arg ]; } else { $self->{form}->error('Incorrect usage -- expecting hash or array ref'); } + + my @columns_with_default_alignment = grep { defined $self->{columns}->{$_}->{align} } keys %{ $self->{columns} }; + + foreach my $row (@{ $row_set }) { + foreach my $column (@columns_with_default_alignment) { + $row->{$column} ||= { }; + $row->{$column}->{align} = $self->{columns}->{$column}->{align} unless (defined $row->{$column}->{align}); + } + + foreach my $field (qw(data link)) { + map { $row->{$_}->{$field} = [ $row->{$_}->{$field} ] if (ref $row->{$_}->{$field} ne 'ARRAY') } keys %{ $row }; + } + } + + push @{ $self->{data} }, $row_set; + $last_row_set = $row_set; + + $self->{data_present} = 1; } return $last_row_set; @@ -116,10 +163,18 @@ sub add_separator { push @{ $self->{data} }, { 'type' => 'separator' }; } +sub add_control { + my $self = shift; + my $data = shift; + + push @{ $self->{data} }, $data; +} + sub clear_data { my $self = shift; - $self->{data} = []; + $self->{data} = []; + $self->{data_present} = 0; } sub set_options { @@ -158,6 +213,15 @@ sub set_export_options { }; } +sub get_attachment_basename { + my $self = shift; + my $filename = $self->{options}->{attachment_basename} || 'report'; + $filename =~ s|.*\\||; + $filename =~ s|.*/||; + + return $filename; +} + sub generate_with_headers { my $self = shift; my $format = lc $self->{options}->{output_format}; @@ -167,10 +231,6 @@ sub generate_with_headers { $form->error('Incorrect usage -- no columns specified'); } - my $filename = $self->{options}->{attachment_basename} || 'report'; - $filename =~ s|.*\\||; - $filename =~ s|.*/||; - if ($format eq 'html') { my $title = $form->{title}; $form->{title} = $self->{title} if ($self->{title}); @@ -180,6 +240,7 @@ sub generate_with_headers { print $self->generate_html_content(); } elsif ($format eq 'csv') { + my $filename = $self->get_attachment_basename(); print qq|content-type: text/csv\n|; print qq|content-disposition: attachment; filename=${filename}.csv\n\n|; $self->generate_csv_content(); @@ -196,7 +257,7 @@ sub get_visible_columns { my $self = shift; my $format = shift; - return grep { my $c = $self->{columns}->{$_}; $c && $c->{visible} && (($c->{visible} == 1) || ($c->{visible} =~ /${format}/i)) } @{ $self->{column_order} }; + return grep { my $c = $self->{columns}->{$_}; $c && $c->{visible} && (($c->{visible} == 1) || ($c->{visible} =~ /\Q${format}\E/i)) } @{ $self->{column_order} }; } sub html_format { @@ -233,18 +294,33 @@ sub prepare_html_content { } my ($outer_idx, $inner_idx) = (0, 0); + my $next_border_top; my @rows; foreach my $row_set (@{ $self->{data} }) { if ('HASH' eq ref $row_set) { + if ($row_set->{type} eq 'separator') { + if (! scalar @rows) { + $next_border_top = 1; + } else { + $rows[-1]->{BORDER_BOTTOM} = 1; + } + + next; + } + my $row_data = { - 'IS_CONTROL' => 1, - 'IS_SEPARATOR' => $row_set->{type} eq 'separator', - 'NUM_COLUMNS' => scalar @visible_columns, + 'IS_CONTROL' => 1, + 'IS_COLSPAN_DATA' => $row_set->{type} eq 'colspan_data', + 'NUM_COLUMNS' => scalar @visible_columns, + 'BORDER_TOP' => $next_border_top, + 'data' => $row_set->{data}, }; push @rows, $row_data; + $next_border_top = 0; + next; } @@ -253,16 +329,36 @@ sub prepare_html_content { foreach my $row (@{ $row_set }) { $inner_idx++; - map { $row->{$_}->{data} = $self->html_format($row->{$_}->{data}) } @visible_columns; + foreach my $col_name (@visible_columns) { + my $col = $row->{$col_name}; + $col->{CELL_ROWS} = [ ]; + foreach my $i (0 .. scalar(@{ $col->{data} }) - 1) { + push @{ $col->{CELL_ROWS} }, { + 'data' => $self->html_format($col->{data}->[$i]), + 'link' => $col->{link}->[$i], + }; + } + + # Force at least a   to be displayed so that browsers + # will format the table cell (e.g. borders etc). + if (!scalar @{ $col->{CELL_ROWS} }) { + push @{ $col->{CELL_ROWS} }, { 'data' => ' ' }; + } elsif ((1 == scalar @{ $col->{CELL_ROWS} }) && !$col->{CELL_ROWS}->[0]->{data}) { + $col->{CELL_ROWS}->[0]->{data} = ' '; + } + } my $row_data = { 'COLUMNS' => [ map { $row->{$_} } @visible_columns ], 'outer_idx' => $outer_idx, 'outer_idx_odd' => $outer_idx % 2, 'inner_idx' => $inner_idx, + 'BORDER_TOP' => $next_border_top, }; push @rows, $row_data; + + $next_border_top = 0; } } @@ -281,13 +377,14 @@ sub prepare_html_content { 'RAW_BOTTOM_INFO_TEXT' => $opts->{raw_bottom_info_text}, 'ALLOW_PDF_EXPORT' => $allow_pdf_export, 'ALLOW_CSV_EXPORT' => $opts->{allow_csv_export}, - 'SHOW_EXPORT_BUTTONS' => $allow_pdf_export || $opts->{allow_csv_export}, + 'SHOW_EXPORT_BUTTONS' => ($allow_pdf_export || $opts->{allow_csv_export}) && $self->{data_present}, 'COLUMN_HEADERS' => \@column_headers, 'NUM_COLUMNS' => scalar @column_headers, 'ROWS' => \@rows, 'EXPORT_VARIABLES' => \@export_variables, 'EXPORT_VARIABLE_LIST' => $self->{export}->{variable_list}, 'EXPORT_NEXTSUB' => $self->{export}->{nextsub}, + 'DATA_PRESENT' => $self->{data_present}, }; return $variables; @@ -385,6 +482,7 @@ END my $content; if (!$printer_command) { + my $filename = $self->get_attachment_basename(); print qq|content-type: application/pdf\n|; print qq|content-disposition: attachment; filename=${filename}.pdf\n\n|; @@ -420,6 +518,19 @@ END } } +sub unescape_string { + my $self = shift; + my $text = shift; + + foreach my $key (keys %{ $self->{escaped_strings_map} }) { + $text =~ s/\Q$key\E/$self->{escaped_strings_map}->{$key}/g; + } + + $text =~ s/\Q&\E/&/g; + + return $text; +} + sub generate_csv_content { my $self = shift; @@ -445,13 +556,17 @@ sub generate_csv_content { my @visible_columns = $self->get_visible_columns('CSV'); if ($opts->{headers}) { - $csv->print($stdout, [ map { $self->{columns}->{$_}->{text} } @visible_columns ]); + $csv->print($stdout, [ map { $self->unescape_string($self->{columns}->{$_}->{text}) } @visible_columns ]); } foreach my $row_set (@{ $self->{data} }) { next if ('ARRAY' ne ref $row_set); foreach my $row (@{ $row_set }) { - $csv->print($stdout, [ map { $row->{$_}->{data} } @visible_columns ]); + my @data; + foreach my $col (@visible_columns) { + push @data, join($eol, map { s/\r?\n/$eol/g; $_ } @{ $row->{$col}->{data} }); + } + $csv->print($stdout, \@data); } } }