Eine Report-Klasse geschrieben, der die Ergebnisse von Datenbankabfragen übergeben...
authorMoritz Bunkus <m.bunkus@linet-services.de>
Wed, 13 Jun 2007 09:52:05 +0000 (09:52 +0000)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Wed, 13 Jun 2007 09:52:05 +0000 (09:52 +0000)
Dazu werden einige neue Perl-Module (Text::CSV_XS und IO::Wrap) sowie zwei weitere Hilfsprogramme (html2ps und Ghostscript) benötigt, deren Pfade über die lx-erp.conf eingestellt werden müssen.

SL/InstallationCheck.pm
SL/ReportGenerator.pm [new file with mode: 0644]
bin/mozilla/report_generator.pl [new file with mode: 0644]
doc/INSTALL
doc/UPGRADE
locale/de/all
lx-erp.conf

index 74d3c30..3977bb4 100644 (file)
@@ -13,6 +13,8 @@ use vars qw(@required_modules);
   { "name" => "Text::Iconv", "url" => "http://search.cpan.org/~mpiotr/" },
   { "name" => "Time::HiRes", "url" => "http://search.cpan.org/~jhi/" },
   { "name" => "YAML", "url" => "http://search.cpan.org/~ingy/" },
+  { "name" => "IO::Wrap", "url" => "http://search.cpan.org/~dskoll/IO-stringy-2.110/" },
+  { "name" => "Text::CSV_XS", "url" => "http://search.cpan.org/~hmbrand/Text-CSV_XS-0.29/" },
   );
 
 sub module_available {
diff --git a/SL/ReportGenerator.pm b/SL/ReportGenerator.pm
new file mode 100644 (file)
index 0000000..35a3d83
--- /dev/null
@@ -0,0 +1,400 @@
+package SL::ReportGenerator;
+
+use IO::Wrap;
+use Text::CSV_XS;
+
+use SL::Form;
+
+sub new {
+  my $type = shift;
+
+  my $self = { };
+
+  $self->{myconfig} = shift;
+  $self->{form}     = shift;
+
+  $self->{data}     = [];
+  $self->{options}  = {
+    'std_column_visibility' => 0,
+    'output_format'         => 'HTML',
+    'allow_pdf_export'      => 1,
+    'allow_csv_export'      => 1,
+    'pdf_export'            => {
+      'paper_size'          => 'A4',
+      'orientation'         => 'landscape',
+      'font_size'           => '10',
+      'margin_top'          => 1.5,
+      'margin_left'         => 1.5,
+      'margin_bottom'       => 1.5,
+      'margin_right'        => 1.5,
+      'number'              => 1,
+    },
+    'csv_export'            => {
+      'quote_char'          => '"',
+      'sep_char'            => ';',
+      'escape_char'         => '"',
+      'eol_style'           => 'DOS',
+      'headers'             => 1,
+    },
+  };
+  $self->{export}   = {
+    'nextsub'       => '',
+    'variable_list' => '',
+  };
+
+  $self->set_options(@_) if (@_);
+
+  return bless $self, $type;
+}
+
+sub set_columns {
+  my $self    = shift;
+  my %columns = @_;
+
+  $self->{columns} = \%columns;
+
+  foreach my $column (values %{ $self->{columns} }) {
+    $column->{visible} = $self->{options}->{std_column_visibility} unless defined $column->{visible};
+  }
+
+  $self->set_column_order(sort keys %{ $self->{columns} });
+}
+
+sub set_column_order {
+  my $self    = shift;
+
+  my $order   = 0;
+  my %columns = map { $order++; ($_, $order) } @_;
+
+  foreach my $column (sort keys %{ $self->{columns} }) {
+    next if $columns{$column};
+
+    $order++;
+    $columns{$column} = $order;
+  }
+
+  $self->{column_order} = [ sort { $columns{$a} <=> $columns{$b} } keys %columns ];
+}
+
+sub set_sort_indicator {
+  my $self = shift;
+
+  $self->{options}->{sort_indicator_column}    = shift;
+  $self->{options}->{sort_indicator_direction} = shift;
+}
+
+sub add_data {
+  my $self = shift;
+
+  while (my $arg = shift) {
+    if ('ARRAY' eq ref $arg) {
+      push @{ $self->{data} }, $arg;
+
+    } elsif ('HASH' eq ref $arg) {
+      push @{ $self->{data} }, [ $arg ];
+
+    } else {
+      $self->{form}->error('Incorrect usage -- expecting hash or array ref');
+    }
+  }
+}
+
+sub clear_data {
+  my $self = shift;
+
+  $self->{data} = [];
+}
+
+sub set_options {
+  my $self    = shift;
+  my %options = @_;
+
+  map { $self->{options}->{$_} = $options{$_} } keys %options;
+}
+
+sub set_options_from_form {
+  my $self     = shift;
+
+  my $form     = $self->{form};
+  my $myconfig = $self->{myconfig};
+
+  foreach my $key (qw(output_format)) {
+    my $full_key = "report_generator_${key}";
+    $self->{options}->{$key} = $form->{$full_key} if (defined $form->{$full_key});
+  }
+
+  foreach my $format (qw(pdf csv)) {
+    my $opts = $self->{options}->{"${format}_export"};
+    foreach my $key (keys %{ $opts }) {
+      my $full_key = "report_generator_${format}_options_${key}";
+      $opts->{$key} = $key =~ /^margin/ ? $form->parse_amount($myconfig, $form->{$full_key}) : $form->{$full_key};
+    }
+  }
+}
+
+sub set_export_options {
+  my $self        = shift;
+
+  $self->{export} = {
+    'nextsub'       => shift,
+    'variable_list' => join(" ", @_),
+  };
+}
+
+sub generate_content {
+  my $self   = shift;
+  my $format = lc $self->{options}->{output_format};
+
+  if (!$self->{columns}) {
+    $self->{form}->error('Incorrect usage -- no columns specified');
+  }
+
+  if ($format eq 'html') {
+    return $self->generate_html_content();
+
+  } elsif ($format eq 'csv') {
+    return $self->generate_csv_content();
+
+  } elsif ($format eq 'pdf') {
+    return $self->generate_pdf_content();
+
+  } else {
+    $self->{form}->error('Incorrect usage -- unknown format (supported are HTML, CSV, PDF)');
+  }
+}
+
+sub generate_with_headers {
+  my $self   = shift;
+  my $format = lc $self->{options}->{output_format};
+
+  if (!$self->{columns}) {
+    $self->{form}->error('Incorrect usage -- no columns specified');
+  }
+
+  my $filename =  $self->{options}->{attachment_basename} || 'report';
+  $filename    =~ s|.*\\||;
+  $filename    =~ s|.*/||;
+
+  if ($format eq 'html') {
+    $self->{form}->{title} = $self->{title};
+    $self->{form}->header();
+    print $self->generate_html_content();
+
+  } elsif ($format eq 'csv') {
+    print qq|content-type: text/plain\n\n|;
+#     print qq|content-disposition: attachment; filename=${filename}.csv\n\n|;
+    $self->generate_csv_content();
+
+  } elsif ($format eq 'pdf') {
+    print qq|content-type: application/pdf\n|;
+    print qq|content-disposition: attachment; filename=${filename}.pdf\n\n|;
+    $self->generate_pdf_content();
+
+  } else {
+    $self->{form}->error('Incorrect usage -- unknown format (supported are HTML, CSV, PDF)');
+  }
+}
+
+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} };
+}
+
+sub prepare_html_content {
+  my $self = shift;
+
+  my ($column, $name, @column_headers);
+
+  my $opts            = $self->{options};
+  my @visible_columns = $self->get_visible_columns('HTML');
+
+  foreach $name (@visible_columns) {
+    $column = $self->{columns}->{$name};
+
+    my $header = {
+      'name'                     => $name,
+      'link'                     => $column->{link},
+      'text'                     => $column->{text},
+      'show_sort_indicator'      => $name eq $opts->{sort_indicator_column},
+      'sort_indicator_direction' => $opts->{sort_indicator_direction},
+    };
+
+    push @column_headers, $header;
+  }
+
+  my ($outer_idx, $inner_idx) = (0, 0);
+  my @rows;
+
+  foreach my $row_set (@{ $self->{data} }) {
+    $outer_idx++;
+
+    foreach my $row (@{ $row_set }) {
+      $inner_idx++;
+
+      my $row_data = {
+        'COLUMNS'       => [ map { $row->{$_} } @visible_columns ],
+        'outer_idx'     => $outer_idx,
+        'outer_idx_odd' => $outer_idx % 2,
+        'inner_idx'     => $inner_idx,
+      };
+
+      push @rows, $row_data;
+    }
+  }
+
+  my @export_variables;
+  foreach my $key (split m/ +/, $self->{export}->{variable_list}) {
+    push @export_variables, { 'key' => $key, 'value' => $self->{form}->{$key} };
+  }
+
+  my $allow_pdf_export = $opts->{allow_pdf_export} && (-x $main::html2ps_bin) && (-x $main::ghostscript_bin);
+
+  my $variables = {
+    'TITLE'                => $opts->{title},
+    'TOP_INFO_TEXT'        => $opts->{top_info_text},
+    'RAW_TOP_INFO_TEXT'    => $opts->{raw_top_info_text},
+    'BOTTOM_INFO_TEXT'     => $opts->{bottom_info_text},
+    '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},
+    '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},
+  };
+
+  return $variables;
+}
+
+sub generate_html_content {
+  my $self      = shift;
+  my $variables = $self->prepare_html_content();
+
+  return $self->{form}->parse_html_template('report_generator/html_report', $variables);
+}
+
+sub verify_paper_size {
+  my $self                 = shift;
+  my $requested_paper_size = lc shift;
+  my $default_paper_size   = shift;
+
+  my %allowed_paper_sizes  = map { $_ => 1 } qw(a3 a4 letter legal);
+
+  return $allowed_paper_sizes{$requested_paper_size} ? $requested_paper_size : $default_paper_size;
+}
+
+sub generate_pdf_content {
+  my $self      = shift;
+  my $variables = $self->prepare_html_content();
+  my $form      = $self->{form};
+  my $myconfig  = $self->{myconfig};
+  my $opt       = $self->{options}->{pdf_export};
+
+  my $opt_number     = $opt->{number}                     ? 'number : 1'    : '';
+  my $opt_landscape  = $opt->{orientation} eq 'landscape' ? 'landscape : 1' : '';
+
+  my $opt_paper_size = $self->verify_paper_size($opt->{paper_size}, 'a4');
+
+  my $html2ps_config = <<"END"
+\@html2ps {
+  option {
+    titlepage: 0;
+    hyphenate: 0;
+    colour: 1;
+    ${opt_landscape};
+    ${opt_number};
+  }
+  paper {
+    type: ${opt_paper_size};
+  }
+  break-table: 1;
+}
+
+\@page {
+  margin-top:    $opt->{margin_top}cm;
+  margin-left:   $opt->{margin_left}cm;
+  margin-bottom: $opt->{margin_bottom}cm;
+  margin-right:  $opt->{margin_right}cm;
+}
+
+BODY {
+  font-family: Helvetica;
+  font-size:   $opt->{font_size}pt;
+}
+
+END
+  ;
+
+  my $cfg_file_name = Common::tmpname() . '-html2ps-config';
+  my $cfg_file      = IO::File->new($cfg_file_name, 'w') || $form->error($locale->text('Could not write the html2ps config file.'));
+
+  $cfg_file->print($html2ps_config);
+  $cfg_file->close();
+
+  my $html_file_name = Common::tmpname() . '.html';
+  my $html_file      = IO::File->new($html_file_name, 'w');
+
+  if (!$html_file) {
+    unlink $cfg_file_name;
+    $form->error($locale->text('Could not write the temporary HTML file.'));
+  }
+
+  $html_file->print($form->parse_html_template('report_generator/pdf_report', $variables));
+  $html_file->close();
+
+  my $gs = IO::File->new("\"${main::html2ps_bin}\" -f \"${cfg_file_name}\" \"${html_file_name}\" | " .
+                         "\"${main::ghostscript_bin}\" -q -dSAFER -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sPAPERSIZE=${opt_paper_size} -sOutputFile=- -c .setpdfwrite - |");
+  if ($gs) {
+    while (my $line = <$gs>) {
+      print $line;
+    }
+    $gs->close();
+    unlink $cfg_file_name, $html_file_name;
+
+  } else {
+    unlink $cfg_file_name, $html_file_name;
+    $form->error($locale->text('Could not spawn html2ps or GhostScript.'));
+  }
+}
+
+sub generate_csv_content {
+  my $self = shift;
+
+  my %valid_sep_chars    = (';' => ';', ',' => ',', ':' => ':', 'TAB' => "\t");
+  my %valid_escape_chars = ('"' => 1, "'" => 1);
+  my %valid_quote_chars  = ('"' => 1, "'" => 1);
+
+  my $opts        = $self->{options}->{csv_export};
+  my $eol         = $opts->{eol_style} eq 'DOS'               ? "\r\n"                              : "\n";
+  my $sep_char    = $valid_sep_chars{$opts->{sep_char}}       ? $valid_sep_chars{$opts->{sep_char}} : ';';
+  my $escape_char = $valid_escape_chars{$opts->{escape_char}} ? $opts->{escape_char}                : '"';
+  my $quote_char  = $valid_quote_chars{$opts->{quote_char}}   ? $opts->{quote_char}                 : '"';
+
+  $escape_char    = $quote_char if ($opts->{escape_char} eq 'QUOTE_CHAR');
+
+  my $csv = Text::CSV_XS->new({ 'binary'      => 1,
+                                'sep_char'    => $sep_char,
+                                'escape_char' => $escape_char,
+                                'quote_char'  => $quote_char,
+                                'eol'         => $eol, });
+
+  my $stdout          = wraphandle(\*STDOUT);
+  my @visible_columns = $self->get_visible_columns('CSV');
+
+  if ($opts->{headers}) {
+    $csv->print($stdout, [ map { $self->{columns}->{$_}->{text} } @visible_columns ]);
+  }
+
+  foreach my $row_set (@{ $self->{data} }) {
+    foreach my $row (@{ $row_set }) {
+      $csv->print($stdout, [ map { $row->{$_}->{data} } @visible_columns ]);
+    }
+  }
+}
+
+1;
diff --git a/bin/mozilla/report_generator.pl b/bin/mozilla/report_generator.pl
new file mode 100644 (file)
index 0000000..6c76678
--- /dev/null
@@ -0,0 +1,80 @@
+#=====================================================================
+# LX-Office ERP
+# Copyright (C) 2004
+# Based on SQL-Ledger Version 2.1.9
+# Web http://www.lx-office.org
+######################################################################
+#
+# Stuff that can be used from other modules
+#
+######################################################################
+
+use SL::Form;
+use SL::Common;
+use SL::MoreCommon;
+use SL::ReportGenerator;
+
+sub export_as_pdf {
+  $lxdebug->enter_sub();
+
+  if ($form->{report_generator_pdf_options_set}) {
+    report_generator_do('PDF');
+    $lxdebug->leave_sub();
+    return;
+  }
+
+  my @form_values;
+  map { push @form_values, { 'key' => $_, 'value' => $form->{$_} } } keys %{ $form };
+
+  $form->{title} = $locale->text('PDF export -- options');
+  $form->header();
+  print $form->parse_html_template('report_generator/pdf_export_options',
+                                   { 'HIDDEN'         => \@form_values,
+                                     'default_margin' => $form->format_amount(\%myconfig, 1.5) });
+
+  $lxdebug->leave_sub();
+}
+
+sub export_as_csv {
+  $lxdebug->enter_sub();
+
+  if ($form->{report_generator_csv_options_set}) {
+    report_generator_do('CSV');
+    $lxdebug->leave_sub();
+    return;
+  }
+
+  my @form_values;
+  map { push @form_values, { 'key' => $_, 'value' => $form->{$_} } } keys %{ $form };
+
+  $form->{title} = $locale->text('CSV export -- options');
+  $form->header();
+  print $form->parse_html_template('report_generator/csv_export_options', { 'HIDDEN' => \@form_values });
+
+  $lxdebug->leave_sub();
+}
+
+sub report_generator_do {
+  $lxdebug->enter_sub();
+
+  my $format  = shift;
+
+  my $nextsub = $form->{report_generator_nextsub};
+  if (!$nextsub) {
+    $form->error($locale->text('report_generator_nextsub is not defined.'));
+  }
+
+  foreach my $key (split m/ +/, $form->{report_generator_variable_list}) {
+    $form->{$key} = $form->{"report_generator_hidden_${key}"};
+  }
+
+  $form->{report_generator_output_format} = $format;
+
+  delete @{$form}{map { "report_generator_$_" } qw(nextsub variable_list)};
+
+  call_sub($nextsub);
+
+  $lxdebug->leave_sub();
+}
+
+1;
index 1a0da65..0d82341 100644 (file)
@@ -31,16 +31,18 @@ sind:
 * Class::Accessor
 * Archive::Zip
 * Text::Iconv
+* Text::CSV_XS
+* IO::Wrap (aus dem Paket IO::Stringy)
 * YAML
 
 Diese Pakete können bei den unterschiedlichen Distributionen anders heißen. 
-(Debian: apache, postgresql, libdbi-perl, libdbd-pg-perl,  libpgperl, libhtml-template-perl, libclass-accessor-perl, libarchive-zip-perl, libtext-iconv-perl, libyaml-perl)
-(Fedora: httpd, postgresql-server, perl-DBI, perl-DBD-Pg) 
-(SuSE: apache2, postgresql-server,  perl-DBI, perl-DBD-Pg)
+(Debian: apache, postgresql, libdbi-perl, libdbd-pg-perl,  libpgperl, libhtml-template-perl, libclass-accessor-perl, libarchive-zip-perl, libtext-iconv-perl, libyaml-perl, libtext-csv-perl, libio-stringy-perl)
+(Fedora: httpd, postgresql-server, perl-DBI, perl-DBD-Pg)
+(SuSE: apache2, postgresql-server,  perl-DBI, perl-DBD-Pg, perl-Archive-Zip, perl-Class-Accessor, perl-Text-Iconv, perl-Text-CSV_XS, perl-HTML-Template, perl-IO-stringy)
 
 
 Da Perl-CGI-Ajax nicht als Paket für Distributionen bereit steht, muß es mit der CPAN-Shell installiert werden.
-Leider ist dazu nicht jeder in der Lage. LxO liefert daher das Paket im CGI-Verzeichnis mit. Das sollte als Fall-Back greifen.
+Leider gibt es Fälle, in denen das nicht möglich oder praktikabel ist. LxO liefert daher das Paket im CGI-Verzeichnis mit. Das sollte als Fall-Back greifen.
 
 
 Die PostgreSQL Konfiguration muß angepasst werden.
index 3358ee7..c0c53cb 100644 (file)
@@ -5,16 +5,17 @@ Wichtige Hinweise zum Upgrade von 
 ** BITTE FERTIGEN SIE VOR DEM UPGRADE EIN BACKUP IHRER DATENBANK(EN) AN! **
 
 
-Upgrade von v2.4.0 auf 2.4.1 sowie von 2.4.1 auf 2.4.2
-======================================================
+Upgrade von v2.4.0 und neuer auf v2.6.0
+=======================================
 
-Ein Upgrade von v2.4.0 auf v2.4.1 oder von v2.4.1 auf v2.4.2 besteht
-aus zwei Teilen: den Dateien (einfaches Entpacken und Kopieren in das
+Ein Upgrade von v2.4.0 oder neuer auf v2.6.0 aus zwei Teilen: den
+Dateien (einfaches Entpacken und Kopieren in das
 Installationsverzeichnis genügen) sowie dem Datenbankupgrade.
 
 Bitte beachten Sie auch die Liste der benötigten Perl-Module am Anfang
 der Datei "doc/INSTALL". Besonders nach einem Upgrade auf 2.4.2 muss
-sichergestellt werden, dass das Modul "YAML" installiert ist.
+sichergestellt werden, dass das Modul "YAML" installiert ist. v2.6.0
+benötigt zusätzlich die Module "Text::CSV_XS" und "IO::Wrap".
 
 Das Datenbankupgrade wird automatisch gestartet, wenn sich der erste
 Benutzer nach dem Upgrade der Dateien an Lx-Office anmeldet.
@@ -22,8 +23,8 @@ Benutzer nach dem Upgrade der Dateien an Lx-Office anmeldet.
 ** BITTE FERTIGEN SIE VOR DEM UPGRADE EIN BACKUP IHRER DATENBANK(EN) AN! **
 
 Anders als beim Upgrade auf 2.4.0 handelt es bei den Datenbankupgrades
-auf 2.4.1 und 2.4.2 nur um automatisch ablaufende Scripte, die keine
-Benutzereingaben erfordern.
+auf Versionen nach 2.4.0 nur um automatisch ablaufende Scripte, die
+keine Benutzereingaben erfordern.
 
 
 Upgrade von v2.2.0 bis 2.2.2 auf 2.4.0
index 3c57907..64c9a9a 100644 (file)
@@ -200,6 +200,7 @@ aktualisieren wollen?',
   'Body:'                       => 'Text:',
   'Books are open'              => 'Die Bücher sind geöffnet.',
   'Both'                        => 'Sowohl als auch',
+  'Bottom'                      => 'Unten',
   'Bought'                      => 'Gekauft',
   'Buchungsdatum'               => 'Buchungsdatum',
   'Buchungsgruppe'              => 'Buchungsgruppe',
@@ -212,6 +213,7 @@ aktualisieren wollen?',
   'Business saved!'             => 'Firma gespeichert.',
   'C'                           => 'G',
   'CANCELED'                    => 'Storniert',
+  'CSV export -- options'       => 'CSV-Export -- Optionen',
   'Calculate'                   => 'Berechnen',
   'Cancel Accounts Payables Transaction' => 'Kreditorenbuchung stornieren',
   'Cancel Accounts Receivables Transaction' => 'Debitorenbuchung stornieren',
@@ -286,8 +288,11 @@ aktualisieren wollen?',
   'Could not print dunning.'    => 'Die Mahnungen konnten nicht gedruckt werden.',
   'Could not rename %s to %s. Reason: %s' => 'Die Datei &quot;%s&quot; konnte nicht in &quot;%s&quot; umbenannt werden. Grund: %s',
   'Could not spawn ghostscript.' => 'Die Anwendung "ghostscript" konnte nicht gestartet werden.',
+  'Could not spawn html2ps or GhostScript.' => 'html2ps oder GhostScript konnte nicht gestartet werden.',
   'Could not spawn the printer command.' => 'Die Druckanwendung konnte nicht gestartet werden.',
   'Could not update prices!'    => 'Preise konnten nicht aktualisiert werden!',
+  'Could not write the html2ps config file.' => 'Die tempor&auml;re html2ps-Konfigurationsdatei konnte nicht geschrieben werden.',
+  'Could not write the temporary HTML file.' => 'Eine tempor&auml;re HTML-Datei konnte nicht geschrieben werden.',
   'Country'                     => 'Land',
   'Create Buchungsgruppen'      => 'Buchungsgruppe erfassen',
   'Create Chart of Accounts'    => 'Kontenplan anlegen',
@@ -473,6 +478,7 @@ aktualisieren wollen?',
   'Error'                       => 'Fehler',
   'Error in database control file \'%s\': %s' => 'Fehler in Datenbankupgradekontrolldatei \'%s\': %s',
   'Error!'                      => 'Fehler!',
+  'Escape character'            => 'Escape-Zeichen',
   'Exch'                        => 'Wechselkurs.',
   'Exchangerate'                => 'Wechselkurs',
   'Exchangerate Difference'     => 'Wechselkursunterschied',
@@ -490,6 +496,8 @@ aktualisieren wollen?',
   'Expiring in x month(s)'      => 'Die in x Monat(en) ablaufen',
   'Export Buchungsdaten'        => 'Export Buchungsdaten',
   'Export Stammdaten'           => 'Export Stammdaten',
+  'Export as CSV'               => 'Als CSV exportieren',
+  'Export as PDF'               => 'Als PDF exportieren',
   'Extended'                    => 'Gesamt',
   'Extension Of Time'           => 'Dauerfristverlängerung',
   'Factor'                      => 'Faktor',
@@ -501,6 +509,7 @@ aktualisieren wollen?',
   'File locked!'                => 'Datei gesperrt!',
   'Files created by Lx-Office\'s &quot;Backup Dataset&quot; function are such files.' => 'Dateien, die von Lx-Office\' Funktion &quot;Datenbank sichern&quot; erstellt wurden, erf&uuml;llen diese Kriterien.',
   'Folgekonto'                  => 'Folgekonto',
+  'Font size'                   => 'Schriftgr&ouml;&szlig;e',
   'For each unit there\'s either no or exactly one base unit. If you chose a base unit then you also have to chose a factor. That way the new unit will be defined as a multiple of the base unit. The base unit must be the &quot;smaller&quot; one. A factor may not be less than 1. Therefore you may define &quot;kg&quot; with the base unit &quot;g&quot; and a factor of &quot;1&quot;, but not the other way round.' => 'Einheiten haben entweder keine oder genau eine Basiseinheit, von der sie ein Vielfaches sind. Wenn Sie eine Basiseinheit ausw&auml;hlen, dann m&uuml;ssen Sie auch einen Faktor eingeben. Sie m&uuml;ssen Einheiten als ein Vielfaches einer kleineren Einheit eingeben. So ist die Definition von &quot;kg&quot; mit der Basiseinheit &quot;g&quot; und dem Faktor 1000 zul&auml;ssig, die Definition von &quot;g&quot; mit der Basiseinheit &quot;kg&quot; und dem Faktor &quot;0,001&quot; hingegen nicht.',
   'Foreign Exchange Gain'       => 'Wechselkurserträge',
   'Foreign Exchange Loss'       => 'Wechselkursaufwendungen',
@@ -550,6 +559,7 @@ aktualisieren wollen?',
   'In-line'                     => 'im Text',
   'Inactive'                    => 'Inaktiv',
   'Include Exchangerate Difference' => 'Wechselkursunterschied einbeziehen',
+  'Include column headings'     => 'Spalten&uuml;berschriften erzeugen',
   'Include in Report'           => 'In Bericht aufnehmen',
   'Include in drop-down menus'  => 'In Aufklappmenü aufnehmen',
   'Income Statement'            => 'GuV',
@@ -612,6 +622,7 @@ aktualisieren wollen?',
   'Kundennummer'                => 'Kundennummer',
   'L'                           => 'L',
   'LaTeX Templates'             => 'LaTeX-Vorlagen',
+  'Landscape'                   => 'Querformat',
   'Language'                    => 'Sprache',
   'Language Values'             => 'Sprachübersetzungen',
   'Language deleted!'           => 'Sprache gelöscht!',
@@ -632,6 +643,7 @@ aktualisieren wollen?',
   'Last Vendor Number'          => 'Letzte Lieferantennummer',
   'Lead'                        => 'Kundenquelle',
   'Leave host and port field empty unless you want to make a remote connection.' => 'F&uuml;r lokale Verbindungen "Rechner" und "Port" freilassen.',
+  'Left'                        => 'Links',
   'Liability'                   => 'Passiva/Mittelherkunft',
   'License'                     => 'Lizenz',
   'License key'                 => 'Lizenzschlüssel',
@@ -639,6 +651,7 @@ aktualisieren wollen?',
   'Licenses'                    => 'Lizenzen',
   'Lieferungen'                 => 'Lieferungen',
   'Line Total'                  => 'Zeilensumme',
+  'Line endings'                => 'Zeilenumbr&uuml;che',
   'List Accounting Groups'      => 'Buchungsgruppen anzeigen',
   'List Accounts'               => 'Konten anzeigen',
   'List Businesses'             => 'Kunden-/Lieferantentypen anzeigen',
@@ -651,6 +664,7 @@ aktualisieren wollen?',
   'List Pricegroups'            => 'Preisgruppen anzeigen',
   'List Printer'                => 'Drucker anzeigen',
   'List Transactions'           => 'Buchungsliste',
+  'List export'                 => 'Listenexport',
   'Load draft'                  => 'Entwurf laden',
   'Local Tax Office Preferences' => 'Angaben zum Finanzamt',
   'Lock System'                 => 'System sperren',
@@ -672,6 +686,7 @@ aktualisieren wollen?',
   'Mandantennummer'             => 'Mandantennummer',
   'Mar'                         => 'März',
   'March'                       => 'März',
+  'Margins'                     => 'Seitenr&auml;nder',
   'Mark as paid?'               => 'Als bezahlt markieren?',
   'Marked as paid'              => 'Als bezahlt markiert',
   'Marked entries printed!'     => 'Markierte Einträge wurden gedruckt!',
@@ -749,6 +764,7 @@ aktualisieren wollen?',
   'Number Format'               => 'Zahlenformat',
   'Number missing in Row'       => 'Nummer fehlt in Zeile',
   'Number of copies'            => 'Anzahl Kopien',
+  'Number pages'                => 'Seiten nummerieren',
   'O'                           => 'O',
   'OBE-Export erfolgreich!'     => 'OBE-Export erfolgreich!',
   'Obsolete'                    => 'Ungültig',
@@ -772,6 +788,7 @@ aktualisieren wollen?',
   'Order deleted!'              => 'Auftrag gelöscht!',
   'Ordered'                     => 'Vom Kunde bestellt',
   'Orders'                      => 'Aufträge',
+  'Orientation'                 => 'Seitenformat',
   'Orphaned'                    => 'Nie benutzt',
   'Out of balance transaction!' => 'Buchung ist nicht ausgeglichen!',
   'Out of balance!'             => 'Summen stimmen nicht berein!',
@@ -781,6 +798,7 @@ aktualisieren wollen?',
   'PAYMENT POSTED'              => 'Rechung gebucht',
   'PDF'                         => 'PDF',
   'PDF (OpenDocument/OASIS)'    => 'PDF (OpenDocument/OASIS)',
+  'PDF export -- options'       => 'PDF-Export -- Optionen',
   'POSTED'                      => 'Gebucht',
   'POSTED AS NEW'               => 'Als neu gebucht',
   'PRINTED'                     => 'Gedruckt',
@@ -833,6 +851,7 @@ aktualisieren wollen?',
   'Please seletct the dataset you want to delete:' => 'Bitte w&auml;hlen Sie die zu l&ouml;schende Datenbank aus:',
   'Plural'                      => 'Plural',
   'Port'                        => 'Port',
+  'Portrait'                    => 'Hochformat',
   'Post'                        => 'Buchen',
   'Post Payment'                => 'Zahlung buchen',
   'Postscript'                  => 'Postscript',
@@ -897,6 +916,7 @@ aktualisieren wollen?',
   'Quotation Number missing!'   => 'Angebotsnummer fehlt!',
   'Quotation deleted!'          => 'Angebot wurde gelöscht.',
   'Quotations'                  => 'Angebote',
+  'Quote chararacter'           => 'Anf&uuml;hrungszeichen',
   'Quoted'                      => 'Angeboten',
   'RFQ'                         => 'Anfrage',
   'RFQ Number'                  => 'Anfragenummer',
@@ -932,6 +952,7 @@ aktualisieren wollen?',
   'Revenue Account'             => 'Erlöskonto',
   'Revenues EU with UStId'      => 'Erl&ouml;se EU m. UStId',
   'Revenues EU without UStId'   => 'Erl&ouml;se EU o. UStId',
+  'Right'                       => 'Rechts',
   'SAVED'                       => 'Gespeichert',
   'SAVED FOR DUNNING'           => 'Gespeichert',
   'SCREENED'                    => 'Angezeigt',
@@ -943,6 +964,7 @@ aktualisieren wollen?',
   'Sales invoice number'        => 'Ausgangsrechnungsnummer',
   'Salesman'                    => 'Verkäufer/in',
   'Salesperson'                 => 'Verkäufer',
+  'Same as the quote character' => 'Wie Anf&uuml;hrungszeichen',
   'Sat. Fax'                    => 'Sat. Fax',
   'Sat. Phone'                  => 'Sat. Tel.',
   'Save'                        => 'Speichern',
@@ -976,6 +998,7 @@ aktualisieren wollen?',
   'Sell Price'                  => 'Verkaufspreis',
   'Send the backup via Email'   => 'Die Sicherungsdatei per Email verschicken',
   'Sep'                         => 'Sep',
+  'Separator chararacter'       => 'Feldtrennzeichen',
   'September'                   => 'September',
   'Serial No.'                  => 'Seriennummer',
   'Serial Number'               => 'Seriennummer',
@@ -1100,6 +1123,7 @@ aktualisieren wollen?',
   'The restoration process has started. Here\'s the output of the &quot;pg_restore&quot; command:' => 'Der Wiederherstellungsprozess wurde gestartet. Hier ist die Ausgabe des &quot;pg_restore&quot;-Programmes:',
   'The restoration process is complete. Please review &quot;pg_restore&quot;\'s output to find out if the restoration was successful.' => 'Die Wiederherstellung ist abgeschlossen. Bitte sehen Sie sich die Ausgabe von &quot;pg_restore&quot; an, um festzustellen, ob die Wiederherstellung erfolgreich war.',
   'The second way is to use Perl\'s CPAN module and let it download and install the module for you.' => 'Die zweite Variante besteht darin, Perls CPAN-Modul zu benutzen und es das Modul f&uuml;r Sie installieren zu lassen.',
+  'The tabulator character'     => 'Das Tabulator-Symbol',
   'The third way is to download the module from the above mentioned URL and to install the module manually following the installations instructions contained in the source archive.' => 'Die dritte Variante besteht darin, das Paket von der oben genannten URL herunterzuladen und es manuell zu installieren. Beachten Sie dabei die im Paket enthaltenen Installationsanweisungen.',
   'The unit has been saved.'    => 'Die Einheit wurde gespeichert.',
   'The unit in row %d has been deleted in the meantime.' => 'Die Einheit in Zeile %d ist in der Zwischentzeit gel&ouml;scht worden.',
@@ -1120,6 +1144,7 @@ aktualisieren wollen?',
   'To'                          => 'An',
   'To (time)'                   => 'Bis',
   'To add a user to a group edit a name, change the login name and save. A new user with the same variables will then be saved under the new login name.' => 'Um einer Gruppe einen neuen Benutzer hinzuzuf&uuml;gen, &auml;ndern und speichern Sie am einfachsten einen bestehen den Zugriffsnamen. Unter dem neuen Namen wird dann ein Benutzer mit denselben Einstellungen angelegt.',
+  'Top'                         => 'Oben',
   'Top (CSS)'                   => 'Oben (mit CSS)',
   'Top (Javascript)'            => 'Oben (mit Javascript)',
   'Top 100'                     => 'Top 100',
@@ -1289,6 +1314,7 @@ aktualisieren wollen?',
   'proforma'                    => 'Proforma',
   'purchase_order'              => 'Auftrag',
   'quarter'                     => 'Vierteljährliche (quartalsweise) Abgabe',
+  'report_generator_nextsub is not defined.' => 'report_generator_nextsub ist nicht definiert.',
   'request_quotation'           => 'Angebotsanforderung',
   'reset'                       => 'zurücksetzen',
   's'                           => 's',
index 9b4413e..d44ef2f 100644 (file)
@@ -67,6 +67,12 @@ $xvfb_bin = "/usr/bin/Xvfb";
 $dbcharset = "ISO-8859-15";
 
 
+# Pfad zu 'html2ps' zum Export von Listenansichten als PDF
+$html2ps_bin = "/usr/bin/html2ps";
+$ghostscript_bin = "/usr/bin/gs";
+
+
+
 # Datenbankbackups werden mit dem externen Programm "pg_dump" erledigt.
 # Wenn es nicht im aktuellen Pfad vorhanden ist, so muss hier der vollständige
 # Pfad eingetragen werden. Wenn die Variable auf "DISABLED" gesetzt wird,