Bugfix für 2275 Verkäuferinformationen im Druck kommen aus der Authdatenbank
[kivitendo-erp.git] / SL / Form.pm
index 8ad572c..9a6bca2 100644 (file)
@@ -1,4 +1,4 @@
-#====================================================================
+#========= ===========================================================
 # LX-Office ERP
 # Copyright (C) 2004
 # Based on SQL-Ledger Version 2.1.9
 # LX-Office ERP
 # Copyright (C) 2004
 # Based on SQL-Ledger Version 2.1.9
 
 package Form;
 
 
 package Form;
 
+use Carp;
 use Data::Dumper;
 
 use CGI;
 use Data::Dumper;
 
 use CGI;
-use CGI::Ajax;
 use Cwd;
 use Encode;
 use File::Copy;
 use Cwd;
 use Encode;
 use File::Copy;
@@ -52,15 +52,26 @@ use SL::AM;
 use SL::Common;
 use SL::CVar;
 use SL::DB;
 use SL::Common;
 use SL::CVar;
 use SL::DB;
+use SL::DBConnect;
 use SL::DBUtils;
 use SL::DBUtils;
+use SL::DB::Customer;
+use SL::DB::Default;
+use SL::DB::PaymentTerm;
+use SL::DB::Vendor;
 use SL::DO;
 use SL::IC;
 use SL::IS;
 use SL::DO;
 use SL::IC;
 use SL::IS;
+use SL::Layout::Dispatcher;
+use SL::Locale;
 use SL::Mailer;
 use SL::Menu;
 use SL::Mailer;
 use SL::Menu;
+use SL::MoreCommon qw(uri_encode uri_decode);
 use SL::OE;
 use SL::OE;
+use SL::PrefixedNumber;
+use SL::Request;
 use SL::Template;
 use SL::User;
 use SL::Template;
 use SL::User;
+use SL::X;
 use Template;
 use URI;
 use List::Util qw(first max min sum);
 use Template;
 use URI;
 use List::Util qw(first max min sum);
@@ -80,163 +91,15 @@ sub disconnect_standard_dbh {
   undef $standard_dbh;
 }
 
   undef $standard_dbh;
 }
 
-sub _store_value {
-  $main::lxdebug->enter_sub(2);
-
-  my $self  = shift;
-  my $key   = shift;
-  my $value = shift;
-
-  my @tokens = split /((?:\[\+?\])?(?:\.|$))/, $key;
-
-  my $curr;
-
-  if (scalar @tokens) {
-     $curr = \ $self->{ shift @tokens };
-  }
-
-  while (@tokens) {
-    my $sep = shift @tokens;
-    my $key = shift @tokens;
-
-    $curr = \ $$curr->[++$#$$curr], next if $sep eq '[]';
-    $curr = \ $$curr->[max 0, $#$$curr]  if $sep eq '[].';
-    $curr = \ $$curr->[++$#$$curr]       if $sep eq '[+].';
-    $curr = \ $$curr->{$key}
-  }
-
-  $$curr = $value;
-
-  $main::lxdebug->leave_sub(2);
-
-  return $curr;
-}
-
-sub _input_to_hash {
-  $main::lxdebug->enter_sub(2);
-
-  my $self  = shift;
-  my $input = shift;
-
-  my @pairs = split(/&/, $input);
-
-  foreach (@pairs) {
-    my ($key, $value) = split(/=/, $_, 2);
-    $self->_store_value($self->unescape($key), $self->unescape($value)) if ($key);
-  }
-
-  $main::lxdebug->leave_sub(2);
-}
-
-sub _request_to_hash {
-  $main::lxdebug->enter_sub(2);
-
-  my $self  = shift;
-  my $input = shift;
-
-  if (!$ENV{'CONTENT_TYPE'}
-      || ($ENV{'CONTENT_TYPE'} !~ /multipart\/form-data\s*;\s*boundary\s*=\s*(.+)$/)) {
-
-    $self->_input_to_hash($input);
-
-    $main::lxdebug->leave_sub(2);
-    return;
-  }
-
-  my ($name, $filename, $headers_done, $content_type, $boundary_found, $need_cr, $previous);
-
-  my $boundary = '--' . $1;
-
-  foreach my $line (split m/\n/, $input) {
-    last if (($line eq "${boundary}--") || ($line eq "${boundary}--\r"));
-
-    if (($line eq $boundary) || ($line eq "$boundary\r")) {
-      ${ $previous } =~ s|\r?\n$|| if $previous;
-
-      undef $previous;
-      undef $filename;
-
-      $headers_done   = 0;
-      $content_type   = "text/plain";
-      $boundary_found = 1;
-      $need_cr        = 0;
-
-      next;
-    }
-
-    next unless $boundary_found;
-
-    if (!$headers_done) {
-      $line =~ s/[\r\n]*$//;
-
-      if (!$line) {
-        $headers_done = 1;
-        next;
-      }
-
-      if ($line =~ m|^content-disposition\s*:.*?form-data\s*;|i) {
-        if ($line =~ m|filename\s*=\s*"(.*?)"|i) {
-          $filename = $1;
-          substr $line, $-[0], $+[0] - $-[0], "";
-        }
-
-        if ($line =~ m|name\s*=\s*"(.*?)"|i) {
-          $name = $1;
-          substr $line, $-[0], $+[0] - $-[0], "";
-        }
-
-        $previous         = $self->_store_value($name, '') if ($name);
-        $self->{FILENAME} = $filename if ($filename);
-
-        next;
-      }
-
-      if ($line =~ m|^content-type\s*:\s*(.*?)$|i) {
-        $content_type = $1;
-      }
-
-      next;
-    }
-
-    next unless $previous;
-
-    ${ $previous } .= "${line}\n";
-  }
-
-  ${ $previous } =~ s|\r?\n$|| if $previous;
-
-  $main::lxdebug->leave_sub(2);
-}
-
-sub _recode_recursively {
-  $main::lxdebug->enter_sub();
-  my ($iconv, $param) = @_;
+sub read_version {
+  my ($self) = @_;
 
 
-  if (any { ref $param eq $_ } qw(Form HASH)) {
-    foreach my $key (keys %{ $param }) {
-      if (!ref $param->{$key}) {
-        # Workaround for a bug: converting $param->{$key} directly
-        # leads to 'undef'. I don't know why. Converting a copy works,
-        # though.
-        $param->{$key} = $iconv->convert("" . $param->{$key});
-      } else {
-        _recode_recursively($iconv, $param->{$key});
-      }
-    }
+  open VERSION_FILE, "VERSION";                 # New but flexible code reads version from VERSION-file
+  my $version =  <VERSION_FILE>;
+  $version    =~ s/[^0-9A-Za-z\.\_\-]//g; # only allow numbers, letters, points, underscores and dashes. Prevents injecting of malicious code.
+  close VERSION_FILE;
 
 
-  } elsif (ref $param eq 'ARRAY') {
-    foreach my $idx (0 .. scalar(@{ $param }) - 1) {
-      if (!ref $param->[$idx]) {
-        # Workaround for a bug: converting $param->[$idx] directly
-        # leads to 'undef'. I don't know why. Converting a copy works,
-        # though.
-        $param->[$idx] = $iconv->convert("" . $param->[$idx]);
-      } else {
-        _recode_recursively($iconv, $param->[$idx]);
-      }
-    }
-  }
-  $main::lxdebug->leave_sub();
+  return $version;
 }
 
 sub new {
 }
 
 sub new {
@@ -246,6 +109,7 @@ sub new {
 
   my $self = {};
 
 
   my $self = {};
 
+  no warnings 'once';
   if ($LXDebug::watch_form) {
     require SL::Watchdog;
     tie %{ $self }, 'SL::Watchdog';
   if ($LXDebug::watch_form) {
     require SL::Watchdog;
     tie %{ $self }, 'SL::Watchdog';
@@ -253,34 +117,18 @@ sub new {
 
   bless $self, $type;
 
 
   bless $self, $type;
 
-  $self->_input_to_hash($ENV{QUERY_STRING}) if $ENV{QUERY_STRING};
-  $self->_input_to_hash($ARGV[0])           if @ARGV && $ARGV[0];
-
-  if ($ENV{CONTENT_LENGTH}) {
-    my $content;
-    read STDIN, $content, $ENV{CONTENT_LENGTH};
-    $self->_request_to_hash($content);
-  }
-
-  my $db_charset   = $::lx_office_conf{system}->{dbcharset};
-  $db_charset    ||= Common::DEFAULT_CHARSET;
-
-  my $encoding     = $self->{INPUT_ENCODING} || $db_charset;
-  delete $self->{INPUT_ENCODING};
-
-  _recode_recursively(SL::Iconv->new($encoding, $db_charset), $self);
-
-  #$self->{version} =  "2.6.1";                 # Old hardcoded but secure style
-  open VERSION_FILE, "VERSION";                 # New but flexible code reads version from VERSION-file
-  $self->{version} =  <VERSION_FILE>;
-  close VERSION_FILE;
-  $self->{version}  =~ s/[^0-9A-Za-z\.\_\-]//g; # only allow numbers, letters, points, underscores and dashes. Prevents injecting of malicious code.
+  $self->{version} = $self->read_version;
 
   $main::lxdebug->leave_sub();
 
   return $self;
 }
 
 
   $main::lxdebug->leave_sub();
 
   return $self;
 }
 
+sub read_cgi_input {
+  my ($self) = @_;
+  SL::Request::read_cgi_input($self);
+}
+
 sub _flatten_variables_rec {
   $main::lxdebug->enter_sub(2);
 
 sub _flatten_variables_rec {
   $main::lxdebug->enter_sub(2);
 
@@ -303,9 +151,15 @@ sub _flatten_variables_rec {
     foreach my $idx (0 .. scalar @{ $curr->{$key} } - 1) {
       my $first_array_entry = 1;
 
     foreach my $idx (0 .. scalar @{ $curr->{$key} } - 1) {
       my $first_array_entry = 1;
 
-      foreach my $hash_key (sort keys %{ $curr->{$key}->[$idx] }) {
-        push @result, $self->_flatten_variables_rec($curr->{$key}->[$idx], $prefix . $key . ($first_array_entry ? '[+].' : '[].'), $hash_key);
-        $first_array_entry = 0;
+      my $element = $curr->{$key}[$idx];
+
+      if ('HASH' eq ref $element) {
+        foreach my $hash_key (sort keys %{ $element }) {
+          push @result, $self->_flatten_variables_rec($element, $prefix . $key . ($first_array_entry ? '[+].' : '[].'), $hash_key);
+          $first_array_entry = 0;
+        }
+      } else {
+        @result = ({ 'key' => $prefix . $key . ($first_array_entry ? '[+]' : '[]'), 'value' => $element });
       }
     }
   }
       }
     }
   }
@@ -380,32 +234,15 @@ sub dumper {
 }
 
 sub escape {
 }
 
 sub escape {
-  $main::lxdebug->enter_sub(2);
-
   my ($self, $str) = @_;
 
   my ($self, $str) = @_;
 
-  $str =  Encode::encode('utf-8-strict', $str) if $::locale->is_utf8;
-  $str =~ s/([^a-zA-Z0-9_.:-])/sprintf("%%%02x", ord($1))/ge;
-
-  $main::lxdebug->leave_sub(2);
-
-  return $str;
+  return uri_encode($str);
 }
 
 sub unescape {
 }
 
 sub unescape {
-  $main::lxdebug->enter_sub(2);
-
   my ($self, $str) = @_;
 
   my ($self, $str) = @_;
 
-  $str =~ tr/+/ /;
-  $str =~ s/\\$//;
-
-  $str =~ s/%([0-9a-fA-Z]{2})/pack("c",hex($1))/eg;
-  $str =  Encode::decode('utf-8-strict', $str) if $::locale->is_utf8;
-
-  $main::lxdebug->leave_sub(2);
-
-  return $str;
+  return uri_decode($str);
 }
 
 sub quote {
 }
 
 sub quote {
@@ -439,11 +276,11 @@ sub hide_form {
   my $self = shift;
 
   if (@_) {
   my $self = shift;
 
   if (@_) {
-    map({ print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n"); } @_);
+    map({ print($::request->{cgi}->hidden("-name" => $_, "-default" => $self->{$_}) . "\n"); } @_);
   } else {
     for (sort keys %$self) {
       next if (($_ eq "header") || (ref($self->{$_}) ne ""));
   } else {
     for (sort keys %$self) {
       next if (($_ eq "header") || (ref($self->{$_}) ne ""));
-      print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n");
+      print($::request->{cgi}->hidden("-name" => $_, "-default" => $self->{$_}) . "\n");
     }
   }
   $main::lxdebug->leave_sub();
     }
   }
   $main::lxdebug->leave_sub();
@@ -451,7 +288,7 @@ sub hide_form {
 
 sub throw_on_error {
   my ($self, $code) = @_;
 
 sub throw_on_error {
   my ($self, $code) = @_;
-  local $self->{__ERROR_HANDLER} = sub { die({ error => $_[0] }) };
+  local $self->{__ERROR_HANDLER} = sub { die SL::X::FormError->new($_[0]) };
   $code->();
 }
 
   $code->();
 }
 
@@ -483,35 +320,13 @@ sub info {
   my ($self, $msg) = @_;
 
   if ($ENV{HTTP_USER_AGENT}) {
   my ($self, $msg) = @_;
 
   if ($ENV{HTTP_USER_AGENT}) {
-    $msg =~ s/\n/<br>/g;
-
-    if (!$self->{header}) {
-      $self->header;
-      print qq|<body>|;
-    }
-
-    print qq|
-    <p class="message_ok"><b>$msg</b></p>
-
-    <script type="text/javascript">
-    <!--
-    // If JavaScript is enabled, the whole thing will be reloaded.
-    // The reason is: When one changes his menu setup (HTML / XUL / CSS ...)
-    // it now loads the correct code into the browser instead of do nothing.
-    setTimeout("top.frames.location.href='login.pl'",500);
-    //-->
-    </script>
-
-</body>
-    |;
+    $self->header;
+    print $self->parse_html_template('generic/form_info', { message => $msg });
 
 
+  } elsif ($self->{info_function}) {
+    &{ $self->{info_function} }($msg);
   } else {
   } else {
-
-    if ($self->{info_function}) {
-      &{ $self->{info_function} }($msg);
-    } else {
-      print "$msg\n";
-    }
+    print "$msg\n";
   }
 
   $main::lxdebug->leave_sub();
   }
 
   $main::lxdebug->leave_sub();
@@ -563,9 +378,10 @@ sub _get_request_uri {
   my $self = shift;
 
   return URI->new($ENV{HTTP_REFERER})->canonical() if $ENV{HTTP_X_FORWARDED_FOR};
   my $self = shift;
 
   return URI->new($ENV{HTTP_REFERER})->canonical() if $ENV{HTTP_X_FORWARDED_FOR};
+  return URI->new                                  if !$ENV{REQUEST_URI}; # for testing
 
   my $scheme =  $ENV{HTTPS} && (lc $ENV{HTTPS} eq 'on') ? 'https' : 'http';
 
   my $scheme =  $ENV{HTTPS} && (lc $ENV{HTTPS} eq 'on') ? 'https' : 'http';
-  my $port   =  $ENV{SERVER_PORT} || '';
+  my $port   =  $ENV{SERVER_PORT};
   $port      =  undef if (($scheme eq 'http' ) && ($port == 80))
                       || (($scheme eq 'https') && ($port == 443));
 
   $port      =  undef if (($scheme eq 'http' ) && ($port == 80))
                       || (($scheme eq 'https') && ($port == 443));
 
@@ -599,8 +415,7 @@ sub create_http_response {
   my $self     = shift;
   my %params   = @_;
 
   my $self     = shift;
   my %params   = @_;
 
-  my $cgi      = $main::cgi;
-  $cgi       ||= CGI->new('');
+  my $cgi      = $::request->{cgi};
 
   my $session_cookie;
   if (defined $main::auth) {
 
   my $session_cookie;
   if (defined $main::auth) {
@@ -623,6 +438,8 @@ sub create_http_response {
   $cgi_params{'-charset'} = $params{charset} if ($params{charset});
   $cgi_params{'-cookie'}  = $session_cookie  if ($session_cookie);
 
   $cgi_params{'-charset'} = $params{charset} if ($params{charset});
   $cgi_params{'-cookie'}  = $session_cookie  if ($session_cookie);
 
+  map { $cgi_params{'-' . $_} = $params{$_} if exists $params{$_} } qw(content_disposition content_length);
+
   my $output = $cgi->header(%cgi_params);
 
   $main::lxdebug->leave_sub();
   my $output = $cgi->header(%cgi_params);
 
   $main::lxdebug->leave_sub();
@@ -630,20 +447,37 @@ sub create_http_response {
   return $output;
 }
 
   return $output;
 }
 
-
 sub header {
   $::lxdebug->enter_sub;
 
 sub header {
   $::lxdebug->enter_sub;
 
-  # extra code is currently only used by menuv3 and menuv4 to set their css.
-  # it is strongly deprecated, and will be changed in a future version.
-  my ($self, $extra_code) = @_;
-  my $db_charset = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
+  my ($self, %params) = @_;
   my @header;
 
   $::lxdebug->leave_sub and return if !$ENV{HTTP_USER_AGENT} || $self->{header}++;
 
   my @header;
 
   $::lxdebug->leave_sub and return if !$ENV{HTTP_USER_AGENT} || $self->{header}++;
 
+  if ($params{no_layout}) {
+    $::request->{layout} = SL::Layout::Dispatcher->new(style => 'none');
+  }
+
+  my $layout = $::request->{layout};
+
+  # standard css for all
+  # this should gradually move to the layouts that need it
+  $layout->use_stylesheet("$_.css") for qw(
+    main menu list_accounts jquery.autocomplete
+    jquery.multiselect2side
+    ui-lightness/jquery-ui
+    jquery-ui.custom
+  );
+
+  $layout->use_javascript("$_.js") for (qw(
+    jquery jquery-ui jquery.cookie jquery.checkall jquery.download
+    jquery/jquery.form client_js
+    common part_selection switchmenuframe autocomplete_part
+  ), "jquery/ui/i18n/jquery.ui.datepicker-$::myconfig{countrycode}");
+
   $self->{favicon} ||= "favicon.ico";
   $self->{favicon} ||= "favicon.ico";
-  $self->{titlebar}  = "$self->{title} - $self->{titlebar}" if $self->{title};
+  $self->{titlebar} = join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->{version} if $self->{title} || !$self->{titlebar};
 
   # build includes
   if ($self->{refresh_url} || $self->{refresh_time}) {
 
   # build includes
   if ($self->{refresh_url} || $self->{refresh_time}) {
@@ -652,78 +486,68 @@ sub header {
     push @header, "<meta http-equiv='refresh' content='$refresh_time;$refresh_url'>";
   }
 
     push @header, "<meta http-equiv='refresh' content='$refresh_time;$refresh_url'>";
   }
 
-  push @header, "<link rel='stylesheet' href='css/$_' type='text/css' title='Lx-Office stylesheet'>"
-    for grep { -f "css/$_" } apply { s|.*/|| } $self->{stylesheet}, $self->{stylesheets};
+  my $auto_reload_resources_param = $layout->auto_reload_resources_param;
 
 
-  push @header, "<style type='text/css'>\@page { size:landscape; }</style>" if $self->{landscape};
-  push @header, "<link rel='shortcut icon' href='$self->{favicon}' type='image/x-icon'>" if -f $self->{favicon};
-  push @header, '<script type="text/javascript" src="js/jquery.js"></script>',
-                '<script type="text/javascript" src="js/common.js"></script>',
-                '<style type="text/css">@import url(js/jscalendar/calendar-win2k-1.css);</style>',
-                '<script type="text/javascript" src="js/jscalendar/calendar.js"></script>',
-                '<script type="text/javascript" src="js/jscalendar/lang/calendar-de.js"></script>',
-                '<script type="text/javascript" src="js/jscalendar/calendar-setup.js"></script>',
-                '<script type="text/javascript" src="js/part_selection.js"></script>';
+  push @header, map { qq|<link rel="stylesheet" href="${_}${auto_reload_resources_param}" type="text/css" title="Stylesheet">| } $layout->stylesheets;
+  push @header, "<style type='text/css'>\@page { size:landscape; }</style> "                     if $self->{landscape};
+  push @header, "<link rel='shortcut icon' href='$self->{favicon}' type='image/x-icon'>"         if -f $self->{favicon};
+  push @header, map { qq|<script type="text/javascript" src="${_}${auto_reload_resources_param}"></script>| }                    $layout->javascripts;
   push @header, $self->{javascript} if $self->{javascript};
   push @header, map { $_->show_javascript } @{ $self->{AJAX} || [] };
   push @header, $self->{javascript} if $self->{javascript};
   push @header, map { $_->show_javascript } @{ $self->{AJAX} || [] };
-  push @header, "<script type='text/javascript'>function fokus(){ document.$self->{fokus}.focus(); }</script>" if $self->{fokus};
-  push @header, sprintf "<script type='text/javascript'>top.document.title='%s';</script>",
-    join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->{version} if $self->{title};
-
-  # if there is a title, we put some JavaScript in to the page, wich writes a
-  # meaningful title-tag for our frameset.
-  my $title_hack = '';
-  if ($self->{title}) {
-    $title_hack = qq|
-    <script type="text/javascript">
-    <!--
-      // Write a meaningful title-tag for our frameset.
-      top.document.title="| . $self->{"title"} . qq| - | . $self->{"login"} . qq| - | . $::myconfig{dbname} . qq| - V| . $self->{"version"} . qq|";
-    //-->
-    </script>|;
-  }
+
+  my  %doctypes = (
+    strict       => qq|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|,
+    transitional => qq|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|,
+    frameset     => qq|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|,
+    html5        => qq|<!DOCTYPE html>|,
+  );
 
   # output
 
   # output
-  print $self->create_http_response(content_type => 'text/html', charset => $db_charset);
-  print "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>\n"
-    if  $ENV{'HTTP_USER_AGENT'} =~ m/MSIE\s+\d/; # Other browsers may choke on menu scripts with DOCTYPE.
+  print $self->create_http_response(content_type => 'text/html', charset => 'UTF-8');
+  print $doctypes{$params{doctype} || 'transitional'}, $/;
   print <<EOT;
 <html>
  <head>
   print <<EOT;
 <html>
  <head>
-  <meta http-equiv="Content-Type" content="text/html; charset=$db_charset">
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <title>$self->{titlebar}</title>
 EOT
   print "  $_\n" for @header;
   print <<EOT;
   <title>$self->{titlebar}</title>
 EOT
   print "  $_\n" for @header;
   print <<EOT;
-  <link rel="stylesheet" href="css/jquery.autocomplete.css" type="text/css" />
-  <meta name="robots" content="noindex,nofollow" />
-  <link rel="stylesheet" type="text/css" href="css/tabcontent.css" />
-  <script type="text/javascript" src="js/tabcontent.js">
-
-  /***********************************************
-   * Tab Content script v2.2- © Dynamic Drive DHTML code library (www.dynamicdrive.com)
-   * This notice MUST stay intact for legal use
-   * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
-   ***********************************************/
-
-  </script>
-  $extra_code
-  $title_hack
+  <meta name="robots" content="noindex,nofollow">
  </head>
  </head>
+ <body>
 
 EOT
 
 EOT
+  print $::request->{layout}->pre_content;
+  print $::request->{layout}->start_content;
+
+  $layout->header_done;
 
   $::lxdebug->leave_sub;
 }
 
 
   $::lxdebug->leave_sub;
 }
 
+sub footer {
+  return unless $::request->{layout}->need_footer;
+
+  print $::request->{layout}->end_content;
+  print $::request->{layout}->post_content;
+
+  if (my @inline_scripts = $::request->{layout}->javascripts_inline) {
+    print "<script type='text/javascript'>@inline_scripts</script>\n";
+  }
+
+  print <<EOL
+ </body>
+</html>
+EOL
+}
+
 sub ajax_response_header {
   $main::lxdebug->enter_sub();
 
   my ($self) = @_;
 
 sub ajax_response_header {
   $main::lxdebug->enter_sub();
 
   my ($self) = @_;
 
-  my $db_charset = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
-  my $cgi        = $main::cgi || CGI->new('');
-  my $output     = $cgi->header('-charset' => $db_charset);
+  my $output = $::request->{cgi}->header('-charset' => 'UTF-8');
 
   $main::lxdebug->leave_sub();
 
 
   $main::lxdebug->leave_sub();
 
@@ -737,18 +561,17 @@ sub redirect_header {
   my $base_uri = $self->_get_request_uri;
   my $new_uri  = URI->new_abs($new_url, $base_uri);
 
   my $base_uri = $self->_get_request_uri;
   my $new_uri  = URI->new_abs($new_url, $base_uri);
 
-  die "Headers already sent" if $::self->{header};
+  die "Headers already sent" if $self->{header};
   $self->{header} = 1;
 
   $self->{header} = 1;
 
-  my $cgi = $main::cgi || CGI->new('');
-  return $cgi->redirect($new_uri);
+  return $::request->{cgi}->redirect($new_uri);
 }
 
 sub set_standard_title {
   $::lxdebug->enter_sub;
   my $self = shift;
 
 }
 
 sub set_standard_title {
   $::lxdebug->enter_sub;
   my $self = shift;
 
-  $self->{titlebar}  = "Lx-Office " . $::locale->text('Version') . " $self->{version}";
+  $self->{titlebar}  = "kivitendo " . $::locale->text('Version') . " $self->{version}";
   $self->{titlebar} .= "- $::myconfig{name}"   if $::myconfig{name};
   $self->{titlebar} .= "- $::myconfig{dbname}" if $::myconfig{name};
 
   $self->{titlebar} .= "- $::myconfig{name}"   if $::myconfig{name};
   $self->{titlebar} .= "- $::myconfig{dbname}" if $::myconfig{name};
 
@@ -769,13 +592,6 @@ sub _prepare_html_template {
   $language = "de" unless ($language);
 
   if (-f "templates/webpages/${file}.html") {
   $language = "de" unless ($language);
 
   if (-f "templates/webpages/${file}.html") {
-    if ((-f ".developer") && ((stat("templates/webpages/${file}.html"))[9] > (stat("locale/${language}/all"))[9])) {
-      my $info = "Developer information: templates/webpages/${file}.html is newer than the translation file locale/${language}/all.\n" .
-        "Please re-run 'locales.pl' in 'locale/${language}'.";
-      print(qq|<pre>$info</pre>|);
-      ::end_of_request();
-    }
-
     $file = "templates/webpages/${file}.html";
 
   } else {
     $file = "templates/webpages/${file}.html";
 
   } else {
@@ -803,19 +619,10 @@ sub _prepare_html_template {
     map { $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys %::myconfig;
   }
 
     map { $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys %::myconfig;
   }
 
-  $additional_params->{"conf_dbcharset"}              = $::lx_office_conf{system}->{dbcharset};
-  $additional_params->{"conf_webdav"}                 = $::lx_office_conf{system}->{webdav};
-  $additional_params->{"conf_lizenzen"}               = $::lx_office_conf{system}->{lizenzen};
-  $additional_params->{"conf_latex_templates"}        = $::lx_office_conf{print_templates}->{latex};
-  $additional_params->{"conf_opendocument_templates"} = $::lx_office_conf{print_templates}->{opendocument};
-  $additional_params->{"conf_vertreter"}              = $::lx_office_conf{system}->{vertreter};
-  $additional_params->{"conf_show_best_before"}       = $::lx_office_conf{features}->{show_best_before};
-  $additional_params->{"conf_parts_image_css"}        = $::lx_office_conf{features}->{parts_image_css};
-  $additional_params->{"conf_parts_listing_images"}   = $::lx_office_conf{features}->{parts_listing_images};
-  $additional_params->{"conf_parts_show_image"}       = $::lx_office_conf{features}->{parts_show_image};
+  $additional_params->{INSTANCE_CONF} = $::instance_conf;
 
 
-  if (%main::debug_options) {
-    map { $additional_params->{'DEBUG_' . uc($_)} = $main::debug_options{$_} } keys %main::debug_options;
+  if (my $debug_options = $::lx_office_conf{debug}{options}) {
+    map { $additional_params->{'DEBUG_' . uc($_)} = $debug_options->{$_} } keys %$debug_options;
   }
 
   if ($main::auth && $main::auth->{RIGHTS} && $main::auth->{RIGHTS}->{$self->{login}}) {
   }
 
   if ($main::auth && $main::auth->{RIGHTS} && $main::auth->{RIGHTS}->{$self->{login}}) {
@@ -852,8 +659,10 @@ sub parse_html_template {
 sub init_template {
   my $self = shift;
 
 sub init_template {
   my $self = shift;
 
-  return if $self->template;
+  return $self->template if $self->template;
 
 
+  # Force scripts/locales.pl to pick up the exception handling template.
+  # parse_html_template('generic/exception')
   return $self->template(Template->new({
      'INTERPOLATE'  => 0,
      'EVAL_PERL'    => 0,
   return $self->template(Template->new({
      'INTERPOLATE'  => 0,
      'EVAL_PERL'    => 0,
@@ -863,6 +672,7 @@ sub init_template {
      'INCLUDE_PATH' => '.:templates/webpages',
      'COMPILE_EXT'  => '.tcc',
      'COMPILE_DIR'  => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
      'INCLUDE_PATH' => '.:templates/webpages',
      'COMPILE_EXT'  => '.tcc',
      'COMPILE_DIR'  => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
+     'ERROR'        => 'templates/webpages/generic/exception.html',
   })) || die;
 }
 
   })) || die;
 }
 
@@ -883,6 +693,14 @@ sub show_generic_error {
     return;
   }
 
     return;
   }
 
+  if ($::request->is_ajax) {
+    $::lxdebug->message(0, "trying to render AJAX response...");
+    SL::ClientJS->new
+      ->error($error)
+      ->render(SL::Controller::Base->new);
+    ::end_of_request();
+  }
+
   my $add_params = {
     'title_error' => $params{title},
     'label_error' => $error,
   my $add_params = {
     'title_error' => $params{title},
     'label_error' => $error,
@@ -934,52 +752,15 @@ sub show_generic_information {
   ::end_of_request();
 }
 
   ::end_of_request();
 }
 
-# write Trigger JavaScript-Code ($qty = quantity of Triggers)
-# changed it to accept an arbitrary number of triggers - sschoeling
-sub write_trigger {
-  $main::lxdebug->enter_sub();
-
-  my $self     = shift;
-  my $myconfig = shift;
-  my $qty      = shift;
-
-  # set dateform for jsscript
-  # default
-  my %dateformats = (
-    "dd.mm.yy" => "%d.%m.%Y",
-    "dd-mm-yy" => "%d-%m-%Y",
-    "dd/mm/yy" => "%d/%m/%Y",
-    "mm/dd/yy" => "%m/%d/%Y",
-    "mm-dd-yy" => "%m-%d-%Y",
-    "yyyy-mm-dd" => "%Y-%m-%d",
-    );
+sub _store_redirect_info_in_session {
+  my ($self) = @_;
 
 
-  my $ifFormat = defined($dateformats{$myconfig->{"dateformat"}}) ?
-    $dateformats{$myconfig->{"dateformat"}} : "%d.%m.%Y";
-
-  my @triggers;
-  while ($#_ >= 2) {
-    push @triggers, qq|
-       Calendar.setup(
-      {
-      inputField : "| . (shift) . qq|",
-      ifFormat :"$ifFormat",
-      align : "| .  (shift) . qq|",
-      button : "| . (shift) . qq|"
-      }
-      );
-       |;
-  }
-  my $jsscript = qq|
-       <script type="text/javascript">
-       <!--| . join("", @triggers) . qq|//-->
-        </script>
-        |;
+  return unless $self->{callback} =~ m:^ ( [^\?/]+ \.pl ) \? (.+) :x;
 
 
-  $main::lxdebug->leave_sub();
-
-  return $jsscript;
-}    #end sub write_trigger
+  my ($controller, $params) = ($1, $2);
+  my $form                  = { map { map { $self->unescape($_) } split /=/, $_, 2 } split m/\&/, $params };
+  $self->{callback}         = "${controller}?RESTORE_FORM_FROM_SESSION_ID=" . $::auth->save_form_in_session(form => $form);
+}
 
 sub redirect {
   $main::lxdebug->enter_sub();
 
 sub redirect {
   $main::lxdebug->enter_sub();
@@ -987,17 +768,14 @@ sub redirect {
   my ($self, $msg) = @_;
 
   if (!$self->{callback}) {
   my ($self, $msg) = @_;
 
   if (!$self->{callback}) {
-
     $self->info($msg);
     $self->info($msg);
-    ::end_of_request();
-  }
 
 
-#  my ($script, $argv) = split(/\?/, $self->{callback}, 2);
-#  $script =~ s|.*/||;
-#  $script =~ s|[^a-zA-Z0-9_\.]||g;
-#  exec("perl", "$script", $argv);
+  } else {
+    $self->_store_redirect_info_in_session;
+    print $::form->redirect_header($self->{callback});
+  }
 
 
-  print $::form->redirect_header($self->{callback});
+  ::end_of_request();
 
   $main::lxdebug->leave_sub();
 }
 
   $main::lxdebug->leave_sub();
 }
@@ -1017,37 +795,30 @@ sub format_amount {
   $main::lxdebug->enter_sub(2);
 
   my ($self, $myconfig, $amount, $places, $dash) = @_;
   $main::lxdebug->enter_sub(2);
 
   my ($self, $myconfig, $amount, $places, $dash) = @_;
+  $amount ||= 0;
+  $dash   ||= '';
+  my $neg = $amount < 0;
+  my $force_places = defined $places && $places >= 0;
 
 
-  if ($amount eq "") {
-    $amount = 0;
-  }
-
-  # Hey watch out! The amount can be an exponential term like 1.13686837721616e-13
-
-  my $neg = ($amount =~ s/^-//);
-  my $exp = ($amount =~ m/[e]/) ? 1 : 0;
+  $amount = $self->round_amount($amount, abs $places) if $force_places;
+  $amount = sprintf "%.*f", ($force_places ? $places : 10), abs $amount; # 6 is default for %fa
 
 
-  if (defined($places) && ($places ne '')) {
-    if (not $exp) {
-      if ($places < 0) {
-        $amount *= 1;
-        $places *= -1;
+  # before the sprintf amount was a number, afterwards it's a string. because of the dynamic nature of perl
+  # this is easy to confuse, so keep in mind: before this comment no s///, m//, concat or other strong ops on
+  # $amount. after this comment no +,-,*,/,abs. it will only introduce subtle bugs.
 
 
-        my ($actual_places) = ($amount =~ /\.(\d+)/);
-        $actual_places = length($actual_places);
-        $places = $actual_places > $places ? $actual_places : $places;
-      }
-    }
-    $amount = $self->round_amount($amount, $places);
-  }
+  $amount =~ s/0*$// unless defined $places && $places == 0;             # cull trailing 0s
 
   my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars
 
   my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars
-  my @p = split(/\./, $amount); # split amount at decimal point
-
-  $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters
+  my @p = split(/\./, $amount);                                          # split amount at decimal point
 
 
+  $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1];                             # add 1,000 delimiters
   $amount = $p[0];
   $amount = $p[0];
-  $amount .= $d[0].$p[1].(0 x ($places - length $p[1])) if ($places || $p[1] ne '');
+  if ($places || $p[1]) {
+    $amount .= $d[0]
+            .  ( $p[1] || '' )
+            .  (0 x (abs($places || 0) - length ($p[1]||'')));           # pad the fraction
+  }
 
   $amount = do {
     ($dash =~ /-/)    ? ($neg ? "($amount)"                            : "$amount" )                              :
 
   $amount = do {
     ($dash =~ /-/)    ? ($neg ? "($amount)"                            : "$amount" )                              :
@@ -1055,7 +826,6 @@ sub format_amount {
                         ($neg ? "-$amount"                             : "$amount" )                              ;
   };
 
                         ($neg ? "-$amount"                             : "$amount" )                              ;
   };
 
-
   $main::lxdebug->leave_sub(2);
   return $amount;
 }
   $main::lxdebug->leave_sub(2);
   return $amount;
 }
@@ -1079,8 +849,7 @@ sub format_amount_units {
     return '';
   }
 
     return '';
   }
 
-  AM->retrieve_all_units();
-  my $all_units        = $main::all_units;
+  my $all_units        = AM->retrieve_all_units;
 
   if (('' eq ref $conv_units) && ($conv_units =~ /convertible/)) {
     $conv_units = AM->convertible_units($all_units, $part_unit_name, $conv_units eq 'convertible_not_smaller');
 
   if (('' eq ref $conv_units) && ($conv_units =~ /convertible/)) {
     $conv_units = AM->convertible_units($all_units, $part_unit_name, $conv_units eq 'convertible_not_smaller');
@@ -1151,10 +920,15 @@ sub parse_amount {
 
   my ($self, $myconfig, $amount) = @_;
 
 
   my ($self, $myconfig, $amount) = @_;
 
+  if (!defined($amount) || ($amount eq '')) {
+    $main::lxdebug->leave_sub(2);
+    return 0;
+  }
+
   if (   ($myconfig->{numberformat} eq '1.000,00')
       || ($myconfig->{numberformat} eq '1000,00')) {
     $amount =~ s/\.//g;
   if (   ($myconfig->{numberformat} eq '1.000,00')
       || ($myconfig->{numberformat} eq '1000,00')) {
     $amount =~ s/\.//g;
-    $amount =~ s/,/\./;
+    $amount =~ s/,/\./g;
   }
 
   if ($myconfig->{numberformat} eq "1'000.00") {
   }
 
   if ($myconfig->{numberformat} eq "1'000.00") {
@@ -1165,7 +939,9 @@ sub parse_amount {
 
   $main::lxdebug->leave_sub(2);
 
 
   $main::lxdebug->leave_sub(2);
 
-  return ($amount * 1);
+  # Make sure no code wich is not a math expression ends up in eval().
+  return 0 unless $amount =~ /^ [\s \d \( \) \- \+ \* \/ \. ]* $/x;
+  return scalar(eval($amount)) * 1 ;
 }
 
 sub round_amount {
 }
 
 sub round_amount {
@@ -1193,10 +969,11 @@ sub parse_template {
   $main::lxdebug->enter_sub();
 
   my ($self, $myconfig) = @_;
   $main::lxdebug->enter_sub();
 
   my ($self, $myconfig) = @_;
-  my $out;
+  my ($out, $out_mode);
 
   local (*IN, *OUT);
 
 
   local (*IN, *OUT);
 
+  my $defaults  = SL::DB::Default->get;
   my $userspath = $::lx_office_conf{paths}->{userspath};
 
   $self->{"cwd"} = getcwd();
   my $userspath = $::lx_office_conf{paths}->{userspath};
 
   $self->{"cwd"} = getcwd();
@@ -1210,7 +987,6 @@ sub parse_template {
     $ext_for_format = $self->{"format"} =~ m/pdf/ ? 'pdf' : 'odt';
 
   } elsif ($self->{"format"} =~ /(postscript|pdf)/i) {
     $ext_for_format = $self->{"format"} =~ m/pdf/ ? 'pdf' : 'odt';
 
   } elsif ($self->{"format"} =~ /(postscript|pdf)/i) {
-    $ENV{"TEXINPUTS"} = ".:" . getcwd() . "/" . $myconfig->{"templates"} . ":" . $ENV{"TEXINPUTS"};
     $template_type    = 'LaTeX';
     $ext_for_format   = 'pdf';
 
     $template_type    = 'LaTeX';
     $ext_for_format   = 'pdf';
 
@@ -1249,54 +1025,67 @@ sub parse_template {
   $self->{"notes"} = $self->{ $self->{"formname"} . "notes" };
 
   if (!$self->{employee_id}) {
   $self->{"notes"} = $self->{ $self->{"formname"} . "notes" };
 
   if (!$self->{employee_id}) {
-    map { $self->{"employee_${_}"} = $myconfig->{$_}; } qw(email tel fax name signature company address businessnumber co_ustid taxnumber duns);
+    $self->{"employee_${_}"} = $myconfig->{$_} for qw(email tel fax name signature);
+    $self->{"employee_${_}"} = $defaults->$_   for qw(address businessnumber co_ustid company duns sepa_creditor_id taxnumber);
   }
 
   }
 
-  map { $self->{"${_}"} = $myconfig->{$_}; } qw(co_ustid);
-  map { $self->{"myconfig_${_}"} = $myconfig->{$_} } grep { $_ ne 'dbpasswd' } keys %{ $myconfig };
+  $self->{"myconfig_${_}"} = $myconfig->{$_} for grep { $_ ne 'dbpasswd' } keys %{ $myconfig };
+  $self->{$_}              = $defaults->$_   for qw(co_ustid);
+  $self->{"myconfig_${_}"} = $defaults->$_   for qw(address businessnumber co_ustid company duns sepa_creditor_id taxnumber);
+  $self->{AUTH}            = $::auth;
+  $self->{INSTANCE_CONF}   = $::instance_conf;
+  $self->{LOCALE}          = $::locale;
+  $self->{LXCONFIG}        = $::lx_office_conf;
+  $self->{LXDEBUG}         = $::lxdebug;
+  $self->{MYCONFIG}        = \%::myconfig;
 
   $self->{copies} = 1 if (($self->{copies} *= 1) <= 0);
 
   # OUT is used for the media, screen, printer, email
   # for postscript we store a copy in a temporary file
 
   $self->{copies} = 1 if (($self->{copies} *= 1) <= 0);
 
   # OUT is used for the media, screen, printer, email
   # for postscript we store a copy in a temporary file
-  my $fileid = time;
-  my $prepend_userspath;
-
-  if (!$self->{tmpfile}) {
-    $self->{tmpfile}   = "${fileid}.$self->{IN}";
-    $prepend_userspath = 1;
-  }
-
-  $prepend_userspath = 1 if substr($self->{tmpfile}, 0, length $userspath) eq $userspath;
-
-  $self->{tmpfile} =~ s|.*/||;
-  $self->{tmpfile} =~ s/[^a-zA-Z0-9\._\ \-]//g;
-  $self->{tmpfile} = "$userspath/$self->{tmpfile}" if $prepend_userspath;
+  my ($temp_fh, $suffix);
+  $suffix =  $self->{IN};
+  $suffix =~ s/.*\.//;
+  ($temp_fh, $self->{tmpfile}) = File::Temp::tempfile(
+    'kivitendo-printXXXXXX',
+    SUFFIX => '.' . ($suffix || 'tex'),
+    DIR    => $userspath,
+    UNLINK => ($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})? 0 : 1,
+  );
+  close $temp_fh;
+  (undef, undef, $self->{template_meta}{tmpfile}) = File::Spec->splitpath( $self->{tmpfile} );
 
 
-  if ($template->uses_temp_file() || $self->{media} eq 'email') {
-    $out = $self->{OUT};
-    $self->{OUT} = ">$self->{tmpfile}";
-  }
+  $out              = $self->{OUT};
+  $out_mode         = $self->{OUT_MODE} || '>';
+  $self->{OUT}      = "$self->{tmpfile}";
+  $self->{OUT_MODE} = '>';
 
   my $result;
 
   my $result;
+  my $command_formatter = sub {
+    my ($out_mode, $out) = @_;
+    return $out_mode eq '|-' ? SL::Template::create(type => 'ShellCommand', form => $self)->parse($out) : $out;
+  };
 
   if ($self->{OUT}) {
 
   if ($self->{OUT}) {
-    open OUT, "$self->{OUT}" or $self->error("$self->{OUT} : $!");
-    $result = $template->parse(*OUT);
-    close OUT;
-
+    $self->{OUT} = $command_formatter->($self->{OUT_MODE}, $self->{OUT});
+    open(OUT, $self->{OUT_MODE}, $self->{OUT}) or $self->error("error on opening $self->{OUT} with mode $self->{OUT_MODE} : $!");
   } else {
   } else {
+    *OUT = ($::dispatcher->get_standard_filehandles)[1];
     $self->header;
     $self->header;
-    $result = $template->parse(*STDOUT);
   }
 
   }
 
-  if (!$result) {
+  if (!$template->parse(*OUT)) {
     $self->cleanup();
     $self->error("$self->{IN} : " . $template->get_error());
   }
 
     $self->cleanup();
     $self->error("$self->{IN} : " . $template->get_error());
   }
 
+  close OUT if $self->{OUT};
+
+  my $copy_to_webdav = $::instance_conf->get_webdav && $::instance_conf->get_webdav_documents && !$self->{preview} && $self->{tmpdir} && $self->{tmpfile} && $self->{type};
+
   if ($self->{media} eq 'file') {
     copy(join('/', $self->{cwd}, $userspath, $self->{tmpfile}), $out =~ m|^/| ? $out : join('/', $self->{cwd}, $out)) if $template->uses_temp_file;
   if ($self->{media} eq 'file') {
     copy(join('/', $self->{cwd}, $userspath, $self->{tmpfile}), $out =~ m|^/| ? $out : join('/', $self->{cwd}, $out)) if $template->uses_temp_file;
+    Common::copy_file_to_webdav_folder($self)                                                                         if $copy_to_webdav;
     $self->cleanup;
     chdir("$self->{cwd}");
 
     $self->cleanup;
     chdir("$self->{cwd}");
 
@@ -1305,94 +1094,96 @@ sub parse_template {
     return;
   }
 
     return;
   }
 
-  if ($template->uses_temp_file() || $self->{media} eq 'email') {
-
-    if ($self->{media} eq 'email') {
+  Common::copy_file_to_webdav_folder($self) if $copy_to_webdav;
 
 
-      my $mail = new Mailer;
+  if ($self->{media} eq 'email') {
 
 
-      map { $mail->{$_} = $self->{$_} }
-        qw(cc bcc subject message version format);
-      $mail->{charset} = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
-      $mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
-      $mail->{from}   = qq|"$myconfig->{name}" <$myconfig->{email}>|;
-      $mail->{fileid} = "$fileid.";
-      $myconfig->{signature} =~ s/\r//g;
+    my $mail = new Mailer;
 
 
-      # if we send html or plain text inline
-      if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
-        $mail->{contenttype} = "text/html";
+    map { $mail->{$_} = $self->{$_} }
+      qw(cc bcc subject message version format);
+    $mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
+    $mail->{from}   = qq|"$myconfig->{name}" <$myconfig->{email}>|;
+    $mail->{fileid} = time() . '.' . $$ . '.';
+    $myconfig->{signature} =~ s/\r//g;
 
 
-        $mail->{message}       =~ s/\r//g;
-        $mail->{message}       =~ s/\n/<br>\n/g;
-        $myconfig->{signature} =~ s/\n/<br>\n/g;
-        $mail->{message} .= "<br>\n-- <br>\n$myconfig->{signature}\n<br>";
-
-        open(IN, $self->{tmpfile})
-          or $self->error($self->cleanup . "$self->{tmpfile} : $!");
-        while (<IN>) {
-          $mail->{message} .= $_;
-        }
+    # if we send html or plain text inline
+    if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
+      $mail->{contenttype}    =  "text/html";
+      $mail->{message}        =~ s/\r//g;
+      $mail->{message}        =~ s/\n/<br>\n/g;
+      $myconfig->{signature}  =~ s/\n/<br>\n/g;
+      $mail->{message}       .=  "<br>\n-- <br>\n$myconfig->{signature}\n<br>";
 
 
-        close(IN);
-
-      } else {
-
-        if (!$self->{"do_not_attach"}) {
-          my $attachment_name  =  $self->{attachment_filename} || $self->{tmpfile};
-          $attachment_name     =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
-          $mail->{attachments} =  [{ "filename" => $self->{tmpfile},
-                                     "name"     => $attachment_name }];
-        }
+      open(IN, "<", $self->{tmpfile})
+        or $self->error($self->cleanup . "$self->{tmpfile} : $!");
+      $mail->{message} .= $_ while <IN>;
+      close(IN);
 
 
-        $mail->{message}  =~ s/\r//g;
-        $mail->{message} .=  "\n-- \n$myconfig->{signature}";
+    } else {
 
 
+      if (!$self->{"do_not_attach"}) {
+        my $attachment_name  =  $self->{attachment_filename} || $self->{tmpfile};
+        $attachment_name     =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
+        $mail->{attachments} =  [{ "filename" => $self->{tmpfile},
+                                   "name"     => $attachment_name }];
       }
 
       }
 
-      my $err = $mail->send();
-      $self->error($self->cleanup . "$err") if ($err);
+      $mail->{message}  =~ s/\r//g;
+      $mail->{message} .=  "\n-- \n$myconfig->{signature}";
 
 
-    } else {
+    }
 
 
-      $self->{OUT} = $out;
+    my $err = $mail->send();
+    $self->error($self->cleanup . "$err") if ($err);
 
 
-      my $numbytes = (-s $self->{tmpfile});
-      open(IN, $self->{tmpfile})
-        or $self->error($self->cleanup . "$self->{tmpfile} : $!");
-      binmode IN;
+  } else {
 
 
-      $self->{copies} = 1 unless $self->{media} eq 'printer';
+    $self->{OUT}      = $out;
+    $self->{OUT_MODE} = $out_mode;
 
 
-      chdir("$self->{cwd}");
-      #print(STDERR "Kopien $self->{copies}\n");
-      #print(STDERR "OUT $self->{OUT}\n");
-      for my $i (1 .. $self->{copies}) {
-        if ($self->{OUT}) {
-          open OUT, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
-          print OUT while <IN>;
-          close OUT;
-          seek IN, 0, 0;
+    my $numbytes = (-s $self->{tmpfile});
+    open(IN, "<", $self->{tmpfile})
+      or $self->error($self->cleanup . "$self->{tmpfile} : $!");
+    binmode IN;
 
 
-        } else {
-          $self->{attachment_filename} = ($self->{attachment_filename})
-                                       ? $self->{attachment_filename}
-                                       : $self->generate_attachment_filename();
+    $self->{copies} = 1 unless $self->{media} eq 'printer';
 
 
-          # launch application
-          print qq|Content-Type: | . $template->get_mime_type() . qq|
-Content-Disposition: attachment; filename="$self->{attachment_filename}"
-Content-Length: $numbytes
+    chdir("$self->{cwd}");
+    #print(STDERR "Kopien $self->{copies}\n");
+    #print(STDERR "OUT $self->{OUT}\n");
+    for my $i (1 .. $self->{copies}) {
+      if ($self->{OUT}) {
+        $self->{OUT} = $command_formatter->($self->{OUT_MODE}, $self->{OUT});
 
 
-|;
+        open  OUT, $self->{OUT_MODE}, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
+        print OUT $_ while <IN>;
+        close OUT;
+        seek  IN, 0, 0;
 
 
-          $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
+      } else {
+        my %headers = ('-type'       => $template->get_mime_type,
+                       '-connection' => 'close',
+                       '-charset'    => 'UTF-8');
+
+        $self->{attachment_filename} ||= $self->generate_attachment_filename;
+
+        if ($self->{attachment_filename}) {
+          %headers = (
+            %headers,
+            '-attachment'     => $self->{attachment_filename},
+            '-content-length' => $numbytes,
+            '-charset'        => '',
+          );
         }
         }
-      }
 
 
-      close(IN);
+        print $::request->cgi->header(%headers);
+
+        $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
+      }
     }
 
     }
 
+    close(IN);
   }
 
   $self->cleanup;
   }
 
   $self->cleanup;
@@ -1407,6 +1198,9 @@ sub get_formname_translation {
 
   $formname ||= $self->{formname};
 
 
   $formname ||= $self->{formname};
 
+  $self->{recipient_locale} ||=  Locale->lang_to_locale($self->{language});
+  local $::locale = Locale->new($self->{recipient_locale});
+
   my %formname_translations = (
     bin_list                => $main::locale->text('Bin List'),
     credit_note             => $main::locale->text('Credit Note'),
   my %formname_translations = (
     bin_list                => $main::locale->text('Bin List'),
     credit_note             => $main::locale->text('Credit Note'),
@@ -1424,7 +1218,7 @@ sub get_formname_translation {
   );
 
   $main::lxdebug->leave_sub();
   );
 
   $main::lxdebug->leave_sub();
-  return $formname_translations{$formname}
+  return $formname_translations{$formname};
 }
 
 sub get_number_prefix_for_type {
 }
 
 sub get_number_prefix_for_type {
@@ -1460,11 +1254,14 @@ sub generate_attachment_filename {
   $main::lxdebug->enter_sub();
   my ($self) = @_;
 
   $main::lxdebug->enter_sub();
   my ($self) = @_;
 
+  $self->{recipient_locale} ||=  Locale->lang_to_locale($self->{language});
+  my $recipient_locale = Locale->new($self->{recipient_locale});
+
   my $attachment_filename = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
   my $prefix              = $self->get_number_prefix_for_type();
 
   if ($self->{preview} && (first { $self->{type} eq $_ } qw(invoice credit_note))) {
   my $attachment_filename = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
   my $prefix              = $self->get_number_prefix_for_type();
 
   if ($self->{preview} && (first { $self->{type} eq $_ } qw(invoice credit_note))) {
-    $attachment_filename .= ' (' . $main::locale->text('Preview') . ')' . $self->get_extension_for_format();
+    $attachment_filename .= ' (' . $recipient_locale->text('Preview') . ')' . $self->get_extension_for_format();
 
   } elsif ($attachment_filename && $self->{"${prefix}number"}) {
     $attachment_filename .=  "_" . $self->{"${prefix}number"} . $self->get_extension_for_format();
 
   } elsif ($attachment_filename && $self->{"${prefix}number"}) {
     $attachment_filename .=  "_" . $self->{"${prefix}number"} . $self->get_extension_for_format();
@@ -1498,12 +1295,17 @@ sub generate_email_subject {
 sub cleanup {
   $main::lxdebug->enter_sub();
 
 sub cleanup {
   $main::lxdebug->enter_sub();
 
-  my $self = shift;
+  my ($self, $application) = @_;
+
+  my $error_code = $?;
 
   chdir("$self->{tmpdir}");
 
   my @err = ();
 
   chdir("$self->{tmpdir}");
 
   my @err = ();
-  if (-f "$self->{tmpfile}.err") {
+  if ((-1 == $error_code) || (127 == (($error_code) >> 8))) {
+    push @err, $::locale->text('The application "#1" was not found on the system.', $application || 'pdflatex') . ' ' . $::locale->text('Please contact your administrator.');
+
+  } elsif (-f "$self->{tmpfile}.err") {
     open(FH, "$self->{tmpfile}.err");
     @err = <FH>;
     close(FH);
     open(FH, "$self->{tmpfile}.err");
     @err = <FH>;
     close(FH);
@@ -1560,22 +1362,13 @@ sub datetonum {
 
 # Database routines used throughout
 
 
 # Database routines used throughout
 
-sub _dbconnect_options {
-  my $self    = shift;
-  my $options = { pg_enable_utf8 => $::locale->is_utf8,
-                  @_ };
-
-  return $options;
-}
-
 sub dbconnect {
   $main::lxdebug->enter_sub(2);
 
   my ($self, $myconfig) = @_;
 
   # connect to database
 sub dbconnect {
   $main::lxdebug->enter_sub(2);
 
   my ($self, $myconfig) = @_;
 
   # connect to database
-  my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options)
-    or $self->dberror;
+  my $dbh = SL::DBConnect->connect or $self->dberror;
 
   # set db options
   if ($myconfig->{dboptions}) {
 
   # set db options
   if ($myconfig->{dboptions}) {
@@ -1593,8 +1386,7 @@ sub dbconnect_noauto {
   my ($self, $myconfig) = @_;
 
   # connect to database
   my ($self, $myconfig) = @_;
 
   # connect to database
-  my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options(AutoCommit => 0))
-    or $self->dberror;
+  my $dbh = SL::DBConnect->connect(SL::DBConnect->get_connect_args(AutoCommit => 0)) or $self->dberror;
 
   # set db options
   if ($myconfig->{dboptions}) {
 
   # set db options
   if ($myconfig->{dboptions}) {
@@ -1624,14 +1416,39 @@ sub get_standard_dbh {
   return $standard_dbh;
 }
 
   return $standard_dbh;
 }
 
+sub set_standard_dbh {
+  my ($self, $dbh) = @_;
+  my $old_dbh      = $standard_dbh;
+  $standard_dbh    = $dbh;
+
+  return $old_dbh;
+}
+
 sub date_closed {
   $main::lxdebug->enter_sub();
 
   my ($self, $date, $myconfig) = @_;
 sub date_closed {
   $main::lxdebug->enter_sub();
 
   my ($self, $date, $myconfig) = @_;
-  my $dbh = $self->dbconnect($myconfig);
+  my $dbh = $self->get_standard_dbh;
 
   my $query = "SELECT 1 FROM defaults WHERE ? < closedto";
 
   my $query = "SELECT 1 FROM defaults WHERE ? < closedto";
-  my $sth = prepare_execute_query($self, $dbh, $query, $date);
+  my $sth = prepare_execute_query($self, $dbh, $query, conv_date($date));
+
+  # Falls $date = '' - Fehlermeldung aus der Datenbank. Ich denke,
+  # es ist sicher ein conv_date vorher IMMER auszuführen.
+  # Testfälle ohne definiertes closedto:
+  #   Leere Datumseingabe i.O.
+  #     SELECT 1 FROM defaults WHERE '' < closedto
+  #   normale Zahlungsbuchung über Rechnungsmaske i.O.
+  #     SELECT 1 FROM defaults WHERE '10.05.2011' < closedto
+  # Testfälle mit definiertem closedto (30.04.2011):
+  #  Leere Datumseingabe i.O.
+  #   SELECT 1 FROM defaults WHERE '' < closedto
+  # normale Buchung im geschloßenem Zeitraum i.O.
+  #   SELECT 1 FROM defaults WHERE '21.04.2011' < closedto
+  #     Fehlermeldung: Es können keine Zahlungen für abgeschlossene Bücher gebucht werden!
+  # normale Buchung in aktiver Buchungsperiode i.O.
+  #   SELECT 1 FROM defaults WHERE '01.05.2011' < closedto
+
   my ($closed) = $sth->fetchrow_array;
 
   $main::lxdebug->leave_sub();
   my ($closed) = $sth->fetchrow_array;
 
   $main::lxdebug->leave_sub();
@@ -1639,6 +1456,24 @@ sub date_closed {
   return $closed;
 }
 
   return $closed;
 }
 
+# prevents bookings to the to far away future
+sub date_max_future {
+  $main::lxdebug->enter_sub();
+
+  my ($self, $date, $myconfig) = @_;
+  my $dbh = $self->get_standard_dbh;
+
+  my $query = "SELECT 1 FROM defaults WHERE ? - current_date > max_future_booking_interval";
+  my $sth = prepare_execute_query($self, $dbh, $query, conv_date($date));
+
+  my ($max_future_booking_interval) = $sth->fetchrow_array;
+
+  $main::lxdebug->leave_sub();
+
+  return $max_future_booking_interval;
+}
+
+
 sub update_balance {
   $main::lxdebug->enter_sub();
 
 sub update_balance {
   $main::lxdebug->enter_sub();
 
@@ -1672,19 +1507,17 @@ sub update_exchangerate {
     $main::lxdebug->leave_sub();
     return;
   }
     $main::lxdebug->leave_sub();
     return;
   }
-  $query = qq|SELECT curr FROM defaults|;
-
-  my ($currency) = selectrow_query($self, $dbh, $query);
-  my ($defaultcurrency) = split m/:/, $currency;
+  $query = qq|SELECT name AS curr FROM currencies WHERE id=(SELECT currency_id FROM defaults)|;
 
 
+  my ($defaultcurrency) = selectrow_query($self, $dbh, $query);
 
   if ($curr eq $defaultcurrency) {
     $main::lxdebug->leave_sub();
     return;
   }
 
 
   if ($curr eq $defaultcurrency) {
     $main::lxdebug->leave_sub();
     return;
   }
 
-  $query = qq|SELECT e.curr FROM exchangerate e
-                 WHERE e.curr = ? AND e.transdate = ?
+  $query = qq|SELECT e.currency_id FROM exchangerate e
+                 WHERE e.currency_id = (SELECT cu.id FROM currencies cu WHERE cu.name=?) AND e.transdate = ?
                  FOR UPDATE|;
   my $sth = prepare_execute_query($self, $dbh, $query, $curr, $transdate);
 
                  FOR UPDATE|;
   my $sth = prepare_execute_query($self, $dbh, $query, $curr, $transdate);
 
@@ -1710,12 +1543,12 @@ sub update_exchangerate {
   if ($sth->fetchrow_array) {
     $query = qq|UPDATE exchangerate
                 SET $set
   if ($sth->fetchrow_array) {
     $query = qq|UPDATE exchangerate
                 SET $set
-                WHERE curr = ?
+                WHERE currency_id = (SELECT id FROM currencies WHERE name = ?)
                 AND transdate = ?|;
 
   } else {
                 AND transdate = ?|;
 
   } else {
-    $query = qq|INSERT INTO exchangerate (curr, buy, sell, transdate)
-                VALUES (?, $buy, $sell, ?)|;
+    $query = qq|INSERT INTO exchangerate (currency_id, buy, sell, transdate)
+                VALUES ((SELECT id FROM currencies WHERE name = ?), $buy, $sell, ?)|;
   }
   $sth->finish;
   do_query($self, $dbh, $query, $curr, $transdate);
   }
   $sth->finish;
   do_query($self, $dbh, $query, $curr, $transdate);
@@ -1750,23 +1583,22 @@ sub get_exchangerate {
   my ($self, $dbh, $curr, $transdate, $fld) = @_;
   my ($query);
 
   my ($self, $dbh, $curr, $transdate, $fld) = @_;
   my ($query);
 
-  unless ($transdate) {
+  unless ($transdate && $curr) {
     $main::lxdebug->leave_sub();
     return 1;
   }
 
     $main::lxdebug->leave_sub();
     return 1;
   }
 
-  $query = qq|SELECT curr FROM defaults|;
+  $query = qq|SELECT name AS curr FROM currencies WHERE id = (SELECT currency_id FROM defaults)|;
 
 
-  my ($currency) = selectrow_query($self, $dbh, $query);
-  my ($defaultcurrency) = split m/:/, $currency;
+  my ($defaultcurrency) = selectrow_query($self, $dbh, $query);
 
 
-  if ($currency eq $defaultcurrency) {
+  if ($curr eq $defaultcurrency) {
     $main::lxdebug->leave_sub();
     return 1;
   }
 
   $query = qq|SELECT e.$fld FROM exchangerate e
     $main::lxdebug->leave_sub();
     return 1;
   }
 
   $query = qq|SELECT e.$fld FROM exchangerate e
-                 WHERE e.curr = ? AND e.transdate = ?|;
+                 WHERE e.currency_id = (SELECT id FROM currencies WHERE name = ?) AND e.transdate = ?|;
   my ($exchangerate) = selectrow_query($self, $dbh, $query, $curr, $transdate);
 
 
   my ($exchangerate) = selectrow_query($self, $dbh, $query, $curr, $transdate);
 
 
@@ -1799,7 +1631,7 @@ sub check_exchangerate {
 
   my $dbh   = $self->get_standard_dbh($myconfig);
   my $query = qq|SELECT e.$fld FROM exchangerate e
 
   my $dbh   = $self->get_standard_dbh($myconfig);
   my $query = qq|SELECT e.$fld FROM exchangerate e
-                 WHERE e.curr = ? AND e.transdate = ?|;
+                 WHERE e.currency_id = (SELECT id FROM currencies WHERE name = ?) AND e.transdate = ?|;
 
   my ($exchangerate) = selectrow_query($self, $dbh, $query, $currency, $transdate);
 
 
   my ($exchangerate) = selectrow_query($self, $dbh, $query, $currency, $transdate);
 
@@ -1815,10 +1647,8 @@ sub get_all_currencies {
   my $myconfig = shift || \%::myconfig;
   my $dbh      = $self->get_standard_dbh($myconfig);
 
   my $myconfig = shift || \%::myconfig;
   my $dbh      = $self->get_standard_dbh($myconfig);
 
-  my $query = qq|SELECT curr FROM defaults|;
-
-  my ($curr)     = selectrow_query($self, $dbh, $query);
-  my @currencies = grep { $_ } map { s/\s//g; $_ } split m/:/, $curr;
+  my $query = qq|SELECT name FROM currencies|;
+  my @currencies = map { $_->{name} } selectall_hashref_query($self, $dbh, $query);
 
   $main::lxdebug->leave_sub();
 
 
   $main::lxdebug->leave_sub();
 
@@ -1829,11 +1659,14 @@ sub get_default_currency {
   $main::lxdebug->enter_sub();
 
   my ($self, $myconfig) = @_;
   $main::lxdebug->enter_sub();
 
   my ($self, $myconfig) = @_;
-  my @currencies        = $self->get_all_currencies($myconfig);
+  my $dbh      = $self->get_standard_dbh($myconfig);
+  my $query = qq|SELECT name AS curr FROM currencies WHERE id = (SELECT currency_id FROM defaults)|;
+
+  my ($defaultcurrency) = selectrow_query($self, $dbh, $query);
 
   $main::lxdebug->leave_sub();
 
 
   $main::lxdebug->leave_sub();
 
-  return $currencies[0];
+  return $defaultcurrency;
 }
 
 sub set_payment_options {
 }
 
 sub set_payment_options {
@@ -1846,12 +1679,12 @@ sub set_payment_options {
   my $dbh = $self->get_standard_dbh($myconfig);
 
   my $query =
   my $dbh = $self->get_standard_dbh($myconfig);
 
   my $query =
-    qq|SELECT p.terms_netto, p.terms_skonto, p.percent_skonto, p.description_long | .
+    qq|SELECT p.terms_netto, p.terms_skonto, p.percent_skonto, p.description_long , p.description | .
     qq|FROM payment_terms p | .
     qq|WHERE p.id = ?|;
 
   ($self->{terms_netto}, $self->{terms_skonto}, $self->{percent_skonto},
     qq|FROM payment_terms p | .
     qq|WHERE p.id = ?|;
 
   ($self->{terms_netto}, $self->{terms_skonto}, $self->{percent_skonto},
-   $self->{payment_terms}) =
+   $self->{payment_terms}, $self->{payment_description}) =
      selectrow_query($self, $dbh, $query, $self->{payment_id});
 
   if ($transdate eq "") {
      selectrow_query($self, $dbh, $query, $self->{payment_id});
 
   if ($transdate eq "") {
@@ -1883,10 +1716,9 @@ sub set_payment_options {
     $amounts{invtotal} = $self->{invtotal};
     $amounts{total}    = $self->{total};
   }
     $amounts{invtotal} = $self->{invtotal};
     $amounts{total}    = $self->{total};
   }
-  $amounts{skonto_in_percent} = 100.0 * $self->{percent_skonto};
-
   map { $amounts{$_} = $self->parse_amount($myconfig, $amounts{$_}) } keys %amounts;
 
   map { $amounts{$_} = $self->parse_amount($myconfig, $amounts{$_}) } keys %amounts;
 
+  $amounts{skonto_in_percent}  = 100.0 * $self->{percent_skonto};
   $amounts{skonto_amount}      = $amounts{invtotal} * $self->{percent_skonto};
   $amounts{invtotal_wo_skonto} = $amounts{invtotal} * (1 - $self->{percent_skonto});
   $amounts{total_wo_skonto}    = $amounts{total}    * (1 - $self->{percent_skonto});
   $amounts{skonto_amount}      = $amounts{invtotal} * $self->{percent_skonto};
   $amounts{invtotal_wo_skonto} = $amounts{invtotal} * (1 - $self->{percent_skonto});
   $amounts{total_wo_skonto}    = $amounts{total}    * (1 - $self->{percent_skonto});
@@ -1898,10 +1730,12 @@ sub set_payment_options {
 
   if ($self->{"language_id"}) {
     $query =
 
   if ($self->{"language_id"}) {
     $query =
-      qq|SELECT t.description_long, l.output_numberformat, l.output_dateformat, l.output_longdates | .
-      qq|FROM translation_payment_terms t | .
+      qq|SELECT t.translation, l.output_numberformat, l.output_dateformat, l.output_longdates | .
+      qq|FROM generic_translations t | .
       qq|LEFT JOIN language l ON t.language_id = l.id | .
       qq|LEFT JOIN language l ON t.language_id = l.id | .
-      qq|WHERE (t.language_id = ?) AND (t.payment_terms_id = ?)|;
+      qq|WHERE (t.language_id = ?)
+           AND (t.translation_id = ?)
+           AND (t.translation_type = 'SL::DB::PaymentTerm/description_long')|;
     my ($description_long, $output_numberformat, $output_dateformat,
       $output_longdates) =
       selectrow_query($self, $dbh, $query,
     my ($description_long, $output_numberformat, $output_dateformat,
       $output_longdates) =
       selectrow_query($self, $dbh, $query,
@@ -2079,6 +1913,7 @@ sub get_employee_data {
 
   my $self     = shift;
   my %params   = @_;
 
   my $self     = shift;
   my %params   = @_;
+  my $defaults = SL::DB::Default->get;
 
   Common::check_params(\%params, qw(prefix));
   Common::check_params_x(\%params, qw(id));
 
   Common::check_params(\%params, qw(prefix));
   Common::check_params_x(\%params, qw(id));
@@ -2091,16 +1926,24 @@ sub get_employee_data {
   my $myconfig = \%main::myconfig;
   my $dbh      = $params{dbh} || $self->get_standard_dbh($myconfig);
 
   my $myconfig = \%main::myconfig;
   my $dbh      = $params{dbh} || $self->get_standard_dbh($myconfig);
 
-  my ($login)  = selectrow_query($self, $dbh, qq|SELECT login FROM employee WHERE id = ?|, conv_i($params{id}));
+  my ($login, $deleted)  = selectrow_query($self, $dbh, qq|SELECT login,deleted FROM employee WHERE id = ?|, conv_i($params{id}));
 
   if ($login) {
 
   if ($login) {
-    my $user = User->new($login);
-    map { $self->{$params{prefix} . "_${_}"} = $user->{$_}; } qw(address businessnumber co_ustid company duns email fax name signature taxnumber tel);
-
+    # login already fetched and still the same client (mandant) | same for both cases (delete|!delete)
     $self->{$params{prefix} . '_login'}   = $login;
     $self->{$params{prefix} . '_login'}   = $login;
-    $self->{$params{prefix} . '_name'}  ||= $login;
-  }
+    $self->{$params{prefix} . "_${_}"}    = $defaults->$_ for qw(address businessnumber co_ustid company duns taxnumber);
 
 
+    if (!$deleted) {
+      # get employee data from auth.user_config
+      my $user = User->new(login => $login);
+      $self->{$params{prefix} . "_${_}"} = $user->{$_} for qw(email fax name signature tel);
+    } else {
+      # get saved employee data from employee
+      my $employee = SL::DB::Manager::Employee->find_by(id => conv_i($params{id}));
+      $self->{$params{prefix} . "_${_}"} = $employee->{"deleted_$_"} for qw(email fax signature tel);
+      $self->{$params{prefix} . "_name"} = $employee->name;
+    }
+ }
   $main::lxdebug->leave_sub();
 }
 
   $main::lxdebug->leave_sub();
 }
 
@@ -2109,11 +1952,12 @@ sub get_duedate {
 
   my ($self, $myconfig, $reference_date) = @_;
 
 
   my ($self, $myconfig, $reference_date) = @_;
 
-  $reference_date = $reference_date ? conv_dateq($reference_date) . '::DATE' : 'current_date';
+  my $terms   = $self->{payment_id}  ? SL::DB::PaymentTerm->new(id => $self->{payment_id}) ->load
+              : $self->{customer_id} ? SL::DB::Customer   ->new(id => $self->{customer_id})->load->payment
+              : $self->{vendor_id}   ? SL::DB::Vendor     ->new(id => $self->{vendor_id})  ->load->payment
+              :                        croak("Missing field in \$::form: payment_id, customer_id or vendor_id");
 
 
-  my $dbh         = $self->get_standard_dbh($myconfig);
-  my $query       = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
-  my ($duedate)   = selectrow_query($self, $dbh, $query, $self->{payment_id});
+  my $duedate = $terms ? $terms->calc_date(reference_date => $reference_date)->to_kivitendo : undef;
 
   $main::lxdebug->leave_sub();
 
 
   $main::lxdebug->leave_sub();
 
@@ -2264,19 +2108,19 @@ sub _get_taxcharts {
   if (ref $params eq 'HASH') {
     $key = $params->{key} if ($params->{key});
     if ($params->{module} eq 'AR') {
   if (ref $params eq 'HASH') {
     $key = $params->{key} if ($params->{key});
     if ($params->{module} eq 'AR') {
-      push @where, 'taxkey NOT IN (8, 9, 18, 19)';
+      push @where, 'chart_categories ~ \'[ACILQ]\'';
 
     } elsif ($params->{module} eq 'AP') {
 
     } elsif ($params->{module} eq 'AP') {
-      push @where, 'taxkey NOT IN (1, 2, 3, 12, 13)';
+      push @where, 'chart_categories ~ \'[ACELQ]\'';
     }
 
   } elsif ($params) {
     $key = $params;
   }
 
     }
 
   } elsif ($params) {
     $key = $params;
   }
 
-  my $where = ' WHERE ' . join(' AND ', map { "($_)" } @where) if (@where);
+  my $where = @where ? ' WHERE ' . join(' AND ', map { "($_)" } @where) : '';
 
 
-  my $query = qq|SELECT * FROM tax $where ORDER BY taxkey|;
+  my $query = qq|SELECT * FROM tax $where ORDER BY taxkey, rate|;
 
   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
 
   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
@@ -2300,10 +2144,22 @@ sub _get_taxzones {
 sub _get_employees {
   $main::lxdebug->enter_sub();
 
 sub _get_employees {
   $main::lxdebug->enter_sub();
 
-  my ($self, $dbh, $default_key, $key) = @_;
+  my ($self, $dbh, $params) = @_;
 
 
-  $key = $default_key unless ($key);
-  $self->{$key} = selectall_hashref_query($self, $dbh, qq|SELECT * FROM employee ORDER BY lower(name)|);
+  my $deleted = 0;
+
+  my $key;
+  if (ref $params eq 'HASH') {
+    $key     = $params->{key};
+    $deleted = $params->{deleted};
+
+  } else {
+    $key = $params;
+  }
+
+  $key     ||= "all_employees";
+  my $filter = $deleted ? '' : 'WHERE NOT COALESCE(deleted, FALSE)';
+  $self->{$key} = selectall_hashref_query($self, $dbh, qq|SELECT * FROM employee $filter ORDER BY lower(name)|);
 
   $main::lxdebug->leave_sub();
 }
 
   $main::lxdebug->leave_sub();
 }
@@ -2361,9 +2217,7 @@ $main::lxdebug->enter_sub();
 
   $key = "all_currencies" unless ($key);
 
 
   $key = "all_currencies" unless ($key);
 
-  my $query = qq|SELECT curr AS currency FROM defaults|;
-
-  $self->{$key} = [split(/\:/ , selectfirst_hashref_query($self, $dbh, $query)->{currency})];
+  $self->{$key} = [$self->get_all_currencies()];
 
   $main::lxdebug->leave_sub();
 }
 
   $main::lxdebug->leave_sub();
 }
@@ -2375,7 +2229,7 @@ $main::lxdebug->enter_sub();
 
   $key = "all_payments" unless ($key);
 
 
   $key = "all_payments" unless ($key);
 
-  my $query = qq|SELECT * FROM payment_terms ORDER BY id|;
+  my $query = qq|SELECT * FROM payment_terms ORDER BY sortkey|;
 
   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
 
   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
@@ -2389,7 +2243,7 @@ sub _get_customers {
 
   my $options        = ref $key eq 'HASH' ? $key : { key => $key };
   $options->{key}  ||= "all_customers";
 
   my $options        = ref $key eq 'HASH' ? $key : { key => $key };
   $options->{key}  ||= "all_customers";
-  my $limit_clause   = "LIMIT $options->{limit}" if $options->{limit};
+  my $limit_clause   = $options->{limit} ? "LIMIT $options->{limit}" : '';
 
   my @where;
   push @where, qq|business_id IN (SELECT id FROM business WHERE salesman)| if  $options->{business_is_salesman};
 
   my @where;
   push @where, qq|business_id IN (SELECT id FROM business WHERE salesman)| if  $options->{business_is_salesman};
@@ -2453,7 +2307,8 @@ sub _get_warehouses {
   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
   if ($bins_key) {
   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
   if ($bins_key) {
-    $query = qq|SELECT id, description FROM bin WHERE warehouse_id = ?|;
+    $query = qq|SELECT id, description FROM bin WHERE warehouse_id = ?
+                ORDER BY description|;
     my $sth = prepare_query($self, $dbh, $query);
 
     foreach my $warehouse (@{ $self->{$key} }) {
     my $sth = prepare_query($self, $dbh, $query);
 
     foreach my $warehouse (@{ $self->{$key} }) {
@@ -2544,7 +2399,7 @@ sub get_lists {
   }
 
   if ($params{"employees"}) {
   }
 
   if ($params{"employees"}) {
-    $self->_get_employees($dbh, "all_employees", $params{"employees"});
+    $self->_get_employees($dbh, $params{"employees"});
   }
 
   if ($params{"salesmen"}) {
   }
 
   if ($params{"salesmen"}) {
@@ -2650,7 +2505,7 @@ sub get_name {
   return scalar(@{ $self->{name_list} });
 }
 
   return scalar(@{ $self->{name_list} });
 }
 
-# the selection sub is used in the AR, AP, IS, IR and OE module
+# the selection sub is used in the AR, AP, IS, IR, DO and OE module
 #
 sub all_vc {
   $main::lxdebug->enter_sub();
 #
 sub all_vc {
   $main::lxdebug->enter_sub();
@@ -2662,13 +2517,17 @@ sub all_vc {
 
   $table = $table eq "customer" ? "customer" : "vendor";
 
 
   $table = $table eq "customer" ? "customer" : "vendor";
 
-  my $query = qq|SELECT count(*) FROM $table|;
+  # build selection list
+  # Hotfix für Bug 1837 - Besser wäre es alte Buchungsbelege
+  # OHNE Auswahlliste (reines Textfeld) zu laden. Hilft aber auch
+  # nicht für veränderbare Belege (oe, do, ...)
+  my $obsolete = $self->{id} ? '' : "WHERE NOT obsolete";
+  my $query = qq|SELECT count(*) FROM $table $obsolete|;
   my ($count) = selectrow_query($self, $dbh, $query);
 
   my ($count) = selectrow_query($self, $dbh, $query);
 
-  # build selection list
   if ($count <= $myconfig->{vclimit}) {
     $query = qq|SELECT id, name, salesman_id
   if ($count <= $myconfig->{vclimit}) {
     $query = qq|SELECT id, name, salesman_id
-                FROM $table WHERE NOT obsolete
+                FROM $table $obsolete
                 ORDER BY name|;
     $self->{"all_$table"} = selectall_hashref_query($self, $dbh, $query);
   }
                 ORDER BY name|;
     $self->{"all_$table"} = selectall_hashref_query($self, $dbh, $query);
   }
@@ -2679,7 +2538,8 @@ sub all_vc {
   # setup sales contacts
   $query = qq|SELECT e.id, e.name
               FROM employee e
   # setup sales contacts
   $query = qq|SELECT e.id, e.name
               FROM employee e
-              WHERE (e.sales = '1') AND (NOT e.id = ?)|;
+              WHERE (e.sales = '1') AND (NOT e.id = ?)
+              ORDER BY name|;
   $self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
 
   # this is for self
   $self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
 
   # this is for self
@@ -2687,23 +2547,10 @@ sub all_vc {
        { id   => $self->{employee_id},
          name => $self->{employee} });
 
        { id   => $self->{employee_id},
          name => $self->{employee} });
 
-  # sort the whole thing
-  @{ $self->{all_employees} } =
-    sort { $a->{name} cmp $b->{name} } @{ $self->{all_employees} };
-
-  if ($module eq 'AR') {
-
     # prepare query for departments
     # prepare query for departments
-    $query = qq|SELECT id, description
-                FROM department
-                WHERE role = 'P'
-                ORDER BY description|;
-
-  } else {
     $query = qq|SELECT id, description
                 FROM department
                 ORDER BY description|;
     $query = qq|SELECT id, description
                 FROM department
                 ORDER BY description|;
-  }
 
   $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
 
 
   $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
 
@@ -2774,15 +2621,9 @@ sub all_departments {
   my ($self, $myconfig, $table) = @_;
 
   my $dbh = $self->get_standard_dbh($myconfig);
   my ($self, $myconfig, $table) = @_;
 
   my $dbh = $self->get_standard_dbh($myconfig);
-  my $where;
-
-  if ($table eq 'customer') {
-    $where = "WHERE role = 'P' ";
-  }
 
   my $query = qq|SELECT id, description
                  FROM department
 
   my $query = qq|SELECT id, description
                  FROM department
-                 $where
                  ORDER BY description|;
   $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
 
                  ORDER BY description|;
   $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
 
@@ -2822,11 +2663,28 @@ sub create_links {
     }
 
     # now get the account numbers
     }
 
     # now get the account numbers
-    $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
-                FROM chart c, taxkeys tk
-                WHERE (c.link LIKE ?) AND (c.id = tk.chart_id) AND tk.id =
-                  (SELECT id FROM taxkeys WHERE (taxkeys.chart_id = c.id) AND (startdate <= $transdate) ORDER BY startdate DESC LIMIT 1)
-                ORDER BY c.accno|;
+#    $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
+#                FROM chart c, taxkeys tk
+#                WHERE (c.link LIKE ?) AND (c.id = tk.chart_id) AND tk.id =
+#                  (SELECT id FROM taxkeys WHERE (taxkeys.chart_id = c.id) AND (startdate <= $transdate) ORDER BY startdate DESC LIMIT 1)
+#                ORDER BY c.accno|;
+
+#  same query as above, but without expensive subquery for each row. about 80% faster
+    $query = qq|
+      SELECT c.accno, c.description, c.link, c.taxkey_id, tk2.tax_id
+        FROM chart c
+        -- find newest entries in taxkeys
+        INNER JOIN (
+          SELECT chart_id, MAX(startdate) AS startdate
+          FROM taxkeys
+          WHERE (startdate <= $transdate)
+          GROUP BY chart_id
+        ) tk ON (c.id = tk.chart_id)
+        -- and load all of those entries
+        INNER JOIN taxkeys tk2
+           ON (tk.chart_id = tk2.chart_id AND tk.startdate = tk2.startdate)
+       WHERE (c.link LIKE ?)
+      ORDER BY c.accno|;
 
     $sth = $dbh->prepare($query);
 
 
     $sth = $dbh->prepare($query);
 
@@ -2863,13 +2721,17 @@ sub create_links {
     $self->{TAX} = selectall_hashref_query($self, $dbh, $query);
   }
 
     $self->{TAX} = selectall_hashref_query($self, $dbh, $query);
   }
 
+  my $extra_columns = '';
+  $extra_columns   .= 'a.direct_debit, ' if ($module eq 'AR') || ($module eq 'AP');
+
   if ($self->{id}) {
     $query =
       qq|SELECT
            a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid,
   if ($self->{id}) {
     $query =
       qq|SELECT
            a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid,
-           a.duedate, a.ordnumber, a.taxincluded, a.curr AS currency, a.notes,
+           a.duedate, a.ordnumber, a.taxincluded, (SELECT cu.name FROM currencies cu WHERE cu.id=a.currency_id) AS currency, a.notes,
            a.intnotes, a.department_id, a.amount AS oldinvtotal,
            a.paid AS oldtotalpaid, a.employee_id, a.gldate, a.type,
            a.intnotes, a.department_id, a.amount AS oldinvtotal,
            a.paid AS oldtotalpaid, a.employee_id, a.gldate, a.type,
+           a.globalproject_id, ${extra_columns}
            c.name AS $table,
            d.description AS department,
            e.name AS employee
            c.name AS $table,
            d.description AS department,
            e.name AS employee
@@ -2926,20 +2788,13 @@ sub create_links {
     $query =
       qq|SELECT
            c.accno, c.description,
     $query =
       qq|SELECT
            c.accno, c.description,
-           a.source, a.amount, a.memo, a.transdate, a.cleared, a.project_id, a.taxkey,
+           a.acc_trans_id, a.source, a.amount, a.memo, a.transdate, a.gldate, a.cleared, a.project_id, a.taxkey,
            p.projectnumber,
            t.rate, t.id
          FROM acc_trans a
          LEFT JOIN chart c ON (c.id = a.chart_id)
          LEFT JOIN project p ON (p.id = a.project_id)
            p.projectnumber,
            t.rate, t.id
          FROM acc_trans a
          LEFT JOIN chart c ON (c.id = a.chart_id)
          LEFT JOIN project p ON (p.id = a.project_id)
-         LEFT JOIN tax t ON (t.id= (SELECT tk.tax_id FROM taxkeys tk
-                                    WHERE (tk.taxkey_id=a.taxkey) AND
-                                      ((CASE WHEN a.chart_id IN (SELECT chart_id FROM taxkeys WHERE taxkey_id = a.taxkey)
-                                        THEN tk.chart_id = a.chart_id
-                                        ELSE 1 = 1
-                                        END)
-                                       OR (c.link='%tax%')) AND
-                                      (startdate <= a.transdate) ORDER BY startdate DESC LIMIT 1))
+         LEFT JOIN tax t ON (t.id= a.tax_id)
          WHERE a.trans_id = ?
          AND a.fx_transaction = '0'
          ORDER BY a.acc_trans_id, a.transdate|;
          WHERE a.trans_id = ?
          AND a.fx_transaction = '0'
          ORDER BY a.acc_trans_id, a.transdate|;
@@ -2967,9 +2822,11 @@ sub create_links {
     }
 
     $sth->finish;
     }
 
     $sth->finish;
+    #check das:
     $query =
       qq|SELECT
     $query =
       qq|SELECT
-           d.curr AS currencies, d.closedto, d.revtrans,
+           d.closedto, d.revtrans,
+           (SELECT cu.name FROM currencies cu WHERE cu.id=d.currency_id) AS defaultcurrency,
            (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
            (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
          FROM defaults d|;
            (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
            (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
          FROM defaults d|;
@@ -2981,7 +2838,8 @@ sub create_links {
     # get date
     $query =
        qq|SELECT
     # get date
     $query =
        qq|SELECT
-            current_date AS transdate, d.curr AS currencies, d.closedto, d.revtrans,
+            current_date AS transdate, d.closedto, d.revtrans,
+            (SELECT cu.name FROM currencies cu WHERE cu.id=d.currency_id) AS defaultcurrency,
             (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
             (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
           FROM defaults d|;
             (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
             (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
           FROM defaults d|;
@@ -2991,7 +2849,7 @@ sub create_links {
     if ($self->{"$self->{vc}_id"}) {
 
       # only setup currency
     if ($self->{"$self->{vc}_id"}) {
 
       # only setup currency
-      ($self->{currency}) = split(/:/, $self->{currencies});
+      ($self->{currency}) = $self->{defaultcurrency} if !$self->{currency};
 
     } else {
 
 
     } else {
 
@@ -3016,17 +2874,17 @@ sub lastname_used {
   my ($arap, $where);
 
   $table         = $table eq "customer" ? "customer" : "vendor";
   my ($arap, $where);
 
   $table         = $table eq "customer" ? "customer" : "vendor";
-  my %column_map = ("a.curr"                  => "currency",
-                    "a.${table}_id"           => "${table}_id",
+  my %column_map = ("a.${table}_id"           => "${table}_id",
                     "a.department_id"         => "department_id",
                     "d.description"           => "department",
                     "ct.name"                 => $table,
                     "a.department_id"         => "department_id",
                     "d.description"           => "department",
                     "ct.name"                 => $table,
+                    "cu.name"                 => "currency",
                     "current_date + ct.terms" => "duedate",
     );
 
   if ($self->{type} =~ /delivery_order/) {
     $arap  = 'delivery_orders';
                     "current_date + ct.terms" => "duedate",
     );
 
   if ($self->{type} =~ /delivery_order/) {
     $arap  = 'delivery_orders';
-    delete $column_map{"a.curr"};
+    delete $column_map{"cu.currency"};
 
   } elsif ($self->{type} =~ /_order/) {
     $arap  = 'oe';
 
   } elsif ($self->{type} =~ /_order/) {
     $arap  = 'oe';
@@ -3055,6 +2913,7 @@ sub lastname_used {
                         FROM $arap a
                         LEFT JOIN $table     ct ON (a.${table}_id = ct.id)
                         LEFT JOIN department d  ON (a.department_id = d.id)
                         FROM $arap a
                         LEFT JOIN $table     ct ON (a.${table}_id = ct.id)
                         LEFT JOIN department d  ON (a.department_id = d.id)
+                        LEFT JOIN currencies cu ON (cu.id=ct.currency_id)
                         WHERE a.id = ?|;
   my $ref          = selectfirst_hashref_query($self, $dbh, $query, $trans_id);
 
                         WHERE a.id = ?|;
   my $ref          = selectfirst_hashref_query($self, $dbh, $query, $trans_id);
 
@@ -3339,15 +3198,8 @@ sub update_defaults {
   my ($var) = $sth->fetchrow_array;
   $sth->finish;
 
   my ($var) = $sth->fetchrow_array;
   $sth->finish;
 
-  if ($var =~ m/\d+$/) {
-    my $new_var  = (substr $var, $-[0]) * 1 + 1;
-    my $len_diff = length($var) - $-[0] - length($new_var);
-    $var         = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
-
-  } else {
-    $var = $var . '1';
-  }
-
+  $var   = 0 if !defined($var) || ($var eq '');
+  $var   = SL::PrefixedNumber->new(number => $var)->get_next;
   $query = qq|UPDATE defaults SET $fld = ?|;
   do_query($self, $dbh, $query, $var);
 
   $query = qq|UPDATE defaults SET $fld = ?|;
   do_query($self, $dbh, $query, $var);
 
@@ -3536,17 +3388,24 @@ sub restore_vars {
 sub prepare_for_printing {
   my ($self) = @_;
 
 sub prepare_for_printing {
   my ($self) = @_;
 
-  $self->{templates} ||= $::myconfig{templates};
+  my $defaults         = SL::DB::Default->get;
+
+  $self->{templates} ||= $defaults->templates;
   $self->{formname}  ||= $self->{type};
   $self->{media}     ||= 'email';
 
   die "'media' other than 'email', 'file', 'printer' is not supported yet" unless $self->{media} =~ m/^(?:email|file|printer)$/;
 
   $self->{formname}  ||= $self->{type};
   $self->{media}     ||= 'email';
 
   die "'media' other than 'email', 'file', 'printer' is not supported yet" unless $self->{media} =~ m/^(?:email|file|printer)$/;
 
+  # Several fields that used to reside in %::myconfig (stored in
+  # auth.user_config) are now stored in defaults. Copy them over for
+  # compatibility.
+  $self->{$_} = $defaults->$_ for qw(company address taxnumber co_ustid duns sepa_creditor_id);
+
   # set shipto from billto unless set
   my $has_shipto = any { $self->{"shipto$_"} } qw(name street zipcode city country contact);
   if (!$has_shipto && ($self->{type} =~ m/^(?:purchase_order|request_quotation)$/)) {
   # set shipto from billto unless set
   my $has_shipto = any { $self->{"shipto$_"} } qw(name street zipcode city country contact);
   if (!$has_shipto && ($self->{type} =~ m/^(?:purchase_order|request_quotation)$/)) {
-    $self->{shiptoname}   = $::myconfig{company};
-    $self->{shiptostreet} = $::myconfig{address};
+    $self->{shiptoname}   = $defaults->company;
+    $self->{shiptostreet} = $defaults->address;
   }
 
   my $language = $self->{language} ? '_' . $self->{language} : '';
   }
 
   my $language = $self->{language} ? '_' . $self->{language} : '';
@@ -3564,7 +3423,7 @@ sub prepare_for_printing {
   IC->retrieve_accounts(\%::myconfig, $self, map { $_ => $self->{"id_$_"} } 1 .. $self->{rowcount});
 
   if ($self->{type} =~ /_delivery_order$/) {
   IC->retrieve_accounts(\%::myconfig, $self, map { $_ => $self->{"id_$_"} } 1 .. $self->{rowcount});
 
   if ($self->{type} =~ /_delivery_order$/) {
-    DO->order_details();
+    DO->order_details(\%::myconfig, $self);
   } elsif ($self->{type} =~ /sales_order|sales_quotation|request_quotation|purchase_order/) {
     OE->order_details(\%::myconfig, $self);
   } else {
   } elsif ($self->{type} =~ /sales_order|sales_quotation|request_quotation|purchase_order/) {
     OE->order_details(\%::myconfig, $self);
   } else {
@@ -3587,8 +3446,8 @@ sub prepare_for_printing {
     $extension            = 'xls';
   }
 
     $extension            = 'xls';
   }
 
-  my $printer_code    = '_' . $self->{printer_code} if $self->{printer_code};
-  my $email_extension = '_email' if -f "$self->{templates}/$self->{formname}_email${language}${printer_code}.${extension}";
+  my $printer_code    = $self->{printer_code} ? '_' . $self->{printer_code} : '';
+  my $email_extension = $self->{media} eq 'email' && -f ($defaults->templates . "/$self->{formname}_email${language}.${extension}") ? '_email' : '';
   $self->{IN}         = "$self->{formname}${email_extension}${language}${printer_code}.${extension}";
 
   # Format dates.
   $self->{IN}         = "$self->{formname}${email_extension}${language}${printer_code}.${extension}";
 
   # Format dates.
@@ -3690,6 +3549,29 @@ sub reformat_numbers {
   $::myconfig{numberformat} = $saved_numberformat;
 }
 
   $::myconfig{numberformat} = $saved_numberformat;
 }
 
+sub layout {
+  my ($self) = @_;
+  $::lxdebug->enter_sub;
+
+  my %style_to_script_map = (
+    v3  => 'v3',
+    neu => 'new',
+  );
+
+  my $menu_script = $style_to_script_map{$::myconfig{menustyle}} || '';
+
+  package main;
+  require "bin/mozilla/menu$menu_script.pl";
+  package Form;
+  require SL::Controller::FrameHeader;
+
+
+  my $layout = SL::Controller::FrameHeader->new->action_header . ::render();
+
+  $::lxdebug->leave_sub;
+  return $layout;
+}
+
 1;
 
 __END__
 1;
 
 __END__
@@ -3700,7 +3582,7 @@ SL::Form.pm - main data object.
 
 =head1 SYNOPSIS
 
 
 =head1 SYNOPSIS
 
-This is the main data object of Lx-Office.
+This is the main data object of kivitendo.
 Unfortunately it also acts as a god object for certain data retrieval procedures used in the entry points.
 Points of interest for a beginner are:
 
 Unfortunately it also acts as a god object for certain data retrieval procedures used in the entry points.
 Points of interest for a beginner are:
 
@@ -3709,61 +3591,6 @@ Points of interest for a beginner are:
 
 =head1 SPECIAL FUNCTIONS
 
 
 =head1 SPECIAL FUNCTIONS
 
-=head2 C<_store_value()>
-
-parses a complex var name, and stores it in the form.
-
-syntax:
-  $form->_store_value($key, $value);
-
-keys must start with a string, and can contain various tokens.
-supported key structures are:
-
-1. simple access
-  simple key strings work as expected
-
-  id => $form->{id}
-
-2. hash access.
-  separating two keys by a dot (.) will result in a hash lookup for the inner value
-  this is similar to the behaviour of java and templating mechanisms.
-
-  filter.description => $form->{filter}->{description}
-
-3. array+hashref access
-
-  adding brackets ([]) before the dot will cause the next hash to be put into an array.
-  using [+] instead of [] will force a new array index. this is useful for recurring
-  data structures like part lists. put a [+] into the first varname, and use [] on the
-  following ones.
-
-  repeating these names in your template:
-
-    invoice.items[+].id
-    invoice.items[].parts_id
-
-  will result in:
-
-    $form->{invoice}->{items}->[
-      {
-        id       => ...
-        parts_id => ...
-      },
-      {
-        id       => ...
-        parts_id => ...
-      }
-      ...
-    ]
-
-4. arrays
-
-  using brackets at the end of a name will result in a pure array to be created.
-  note that you mustn't use [+], which is reserved for array+hash access and will
-  result in undefined behaviour in array context.
-
-  filter.status[]  => $form->{status}->[ val1, val2, ... ]
-
 =head2 C<update_business> PARAMS
 
 PARAMS (not named):
 =head2 C<update_business> PARAMS
 
 PARAMS (not named):
@@ -3780,7 +3607,7 @@ will in this case not increase the value, and return undef.
 
 Generates a HTTP redirection header for the new C<$url>. Constructs an
 absolute URL including scheme, host name and port. If C<$url> is a
 
 Generates a HTTP redirection header for the new C<$url>. Constructs an
 absolute URL including scheme, host name and port. If C<$url> is a
-relative URL then it is considered relative to Lx-Office base URL.
+relative URL then it is considered relative to kivitendo base URL.
 
 This function C<die>s if headers have already been created with
 C<$::form-E<gt>header>.
 
 This function C<die>s if headers have already been created with
 C<$::form-E<gt>header>.
@@ -3793,7 +3620,7 @@ Examples:
 =head2 C<header>
 
 Generates a general purpose http/html header and includes most of the scripts
 =head2 C<header>
 
 Generates a general purpose http/html header and includes most of the scripts
-ans stylesheets needed.
+and stylesheets needed. Stylesheets can be added with L<use_stylesheet>.
 
 Only one header will be generated. If the method was already called in this
 request it will not output anything and return undef. Also if no
 
 Only one header will be generated. If the method was already called in this
 request it will not output anything and return undef. Also if no
@@ -3813,9 +3640,8 @@ default to 3 seconds and the refering url.
 
 =item stylesheet
 
 
 =item stylesheet
 
-=item stylesheets
-
-If these are arrayrefs the contents will be inlined into the header.
+Either a scalar or an array ref. Will be inlined into the header. Add
+stylesheets with the L<use_stylesheet> function.
 
 =item landscape
 
 
 =item landscape