Kein Automatikkonto auswählen wieder möglich
[kivitendo-erp.git] / SL / Form.pm
index c2385ce..56acec2 100644 (file)
@@ -1,4 +1,4 @@
-#====================================================================
+#========= ===========================================================
 # LX-Office ERP
 # Copyright (C) 2004
 # Based on SQL-Ledger Version 2.1.9
@@ -37,6 +37,7 @@
 
 package Form;
 
+use Carp;
 use Data::Dumper;
 
 use CGI;
@@ -53,6 +54,10 @@ use SL::CVar;
 use SL::DB;
 use SL::DBConnect;
 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;
@@ -86,6 +91,17 @@ sub disconnect_standard_dbh {
   undef $standard_dbh;
 }
 
+sub read_version {
+  my ($self) = @_;
+
+  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;
+
+  return $version;
+}
+
 sub new {
   $main::lxdebug->enter_sub();
 
@@ -101,10 +117,7 @@ sub new {
 
   bless $self, $type;
 
-  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();
 
@@ -138,9 +151,15 @@ sub _flatten_variables_rec {
     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 });
       }
     }
   }
@@ -301,35 +320,13 @@ sub info {
   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 / 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 {
-
-    if ($self->{info_function}) {
-      &{ $self->{info_function} }($msg);
-    } else {
-      print "$msg\n";
-    }
+    print "$msg\n";
   }
 
   $main::lxdebug->leave_sub();
@@ -454,7 +451,6 @@ sub header {
   $::lxdebug->enter_sub;
 
   my ($self, %params) = @_;
-  my $db_charset = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
   my @header;
 
   $::lxdebug->leave_sub and return if !$ENV{HTTP_USER_AGENT} || $self->{header}++;
@@ -469,14 +465,15 @@ sub header {
   # this should gradually move to the layouts that need it
   $layout->use_stylesheet("$_.css") for qw(
     main menu list_accounts jquery.autocomplete
-    jquery.multiselect2side frame_header/header
+    jquery.multiselect2side
     ui-lightness/jquery-ui
-    jquery-ui.custom jqModal
+    jquery-ui.custom
   );
 
   $layout->use_javascript("$_.js") for (qw(
-    jquery jquery-ui jquery.cookie jqModal jquery.checkall jquery.download
-    common part_selection switchmenuframe
+    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";
@@ -506,12 +503,12 @@ sub header {
   );
 
   # output
-  print $self->create_http_response(content_type => 'text/html', charset => $db_charset);
+  print $self->create_http_response(content_type => 'text/html', charset => 'UTF-8');
   print $doctypes{$params{doctype} || 'transitional'}, $/;
   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;
@@ -550,8 +547,7 @@ sub ajax_response_header {
 
   my ($self) = @_;
 
-  my $db_charset = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
-  my $output     = $::request->{cgi}->header('-charset' => $db_charset);
+  my $output = $::request->{cgi}->header('-charset' => 'UTF-8');
 
   $main::lxdebug->leave_sub();
 
@@ -623,15 +619,7 @@ sub _prepare_html_template {
     map { $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys %::myconfig;
   }
 
-  $additional_params->{"conf_dbcharset"}              = $::lx_office_conf{system}->{dbcharset};
-  $additional_params->{"conf_webdav"}                 = $::lx_office_conf{features}->{webdav};
-  $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{features}->{vertreter};
-  $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;
+  $additional_params->{INSTANCE_CONF} = $::instance_conf;
 
   if (my $debug_options = $::lx_office_conf{debug}{options}) {
     map { $additional_params->{'DEBUG_' . uc($_)} = $debug_options->{$_} } keys %$debug_options;
@@ -985,6 +973,7 @@ sub parse_template {
 
   local (*IN, *OUT);
 
+  my $defaults  = SL::DB::Default->get;
   my $userspath = $::lx_office_conf{paths}->{userspath};
 
   $self->{"cwd"} = getcwd();
@@ -1036,11 +1025,19 @@ sub parse_template {
   $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);
 
@@ -1058,12 +1055,10 @@ sub parse_template {
   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};
-    $out_mode         = $self->{OUT_MODE} || '>';
-    $self->{OUT}      = "$self->{tmpfile}";
-    $self->{OUT_MODE} = '>';
-  }
+  $out              = $self->{OUT};
+  $out_mode         = $self->{OUT_MODE} || '>';
+  $self->{OUT}      = "$self->{tmpfile}";
+  $self->{OUT_MODE} = '>';
 
   my $result;
   my $command_formatter = sub {
@@ -1086,8 +1081,11 @@ sub parse_template {
 
   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;
+    Common::copy_file_to_webdav_folder($self)                                                                         if $copy_to_webdav;
     $self->cleanup;
     chdir("$self->{cwd}");
 
@@ -1096,93 +1094,96 @@ sub parse_template {
     return;
   }
 
-  if ($template->uses_temp_file() || $self->{media} eq 'email') {
+  Common::copy_file_to_webdav_folder($self) if $copy_to_webdav;
 
-    if ($self->{media} eq 'email') {
+  if ($self->{media} eq 'email') {
 
-      my $mail = new Mailer;
+    my $mail = new Mailer;
 
-      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} = time() . '.' . $$ . '.';
-      $myconfig->{signature} =~ s/\r//g;
+    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;
 
-      # 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>";
+    # 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>";
 
-        open(IN, "<", $self->{tmpfile})
-          or $self->error($self->cleanup . "$self->{tmpfile} : $!");
-        $mail->{message} .= $_ while <IN>;
-        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);
-
-    } else {
+      $mail->{message}  =~ s/\r//g;
+      $mail->{message} .=  "\n-- \n$myconfig->{signature}";
 
-      $self->{OUT}      = $out;
-      $self->{OUT_MODE} = $out_mode;
+    }
 
-      my $numbytes = (-s $self->{tmpfile});
-      open(IN, "<", $self->{tmpfile})
-        or $self->error($self->cleanup . "$self->{tmpfile} : $!");
-      binmode IN;
+    my $err = $mail->send();
+    $self->error($self->cleanup . "$err") if ($err);
 
-      $self->{copies} = 1 unless $self->{media} eq 'printer';
+  } else {
 
-      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});
+    $self->{OUT}      = $out;
+    $self->{OUT_MODE} = $out_mode;
 
-          open  OUT, $self->{OUT_MODE}, $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;
@@ -1361,22 +1362,13 @@ sub datetonum {
 
 # 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
-  my $dbh = SL::DBConnect->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}) {
@@ -1394,8 +1386,7 @@ sub dbconnect_noauto {
   my ($self, $myconfig) = @_;
 
   # connect to database
-  my $dbh = SL::DBConnect->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}) {
@@ -1425,28 +1416,36 @@ sub get_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) = @_;
-  my $dbh = $self->dbconnect($myconfig);
+  my $dbh = $self->get_standard_dbh;
 
   my $query = "SELECT 1 FROM defaults WHERE ? < closedto";
   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:
+  # 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.
+  #   normale Zahlungsbuchung über Rechnungsmaske i.O.
   #     SELECT 1 FROM defaults WHERE '10.05.2011' < closedto
-  # Testfälle mit definiertem closedto (30.04.2011):
+  # 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.
+  # 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!
+  #     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
 
@@ -1457,6 +1456,24 @@ sub date_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();
 
@@ -1490,7 +1507,7 @@ sub update_exchangerate {
     $main::lxdebug->leave_sub();
     return;
   }
-  $query = qq|SELECT curr FROM currencies WHERE id=(SELECT curr FROM defaults)|;
+  $query = qq|SELECT name AS curr FROM currencies WHERE id=(SELECT currency_id FROM defaults)|;
 
   my ($defaultcurrency) = selectrow_query($self, $dbh, $query);
 
@@ -1499,8 +1516,8 @@ sub update_exchangerate {
     return;
   }
 
-  $query = qq|SELECT e.curr FROM exchangerate e
-                 WHERE e.curr = (SELECT cu.id FROM currencies cu WHERE cu.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);
 
@@ -1526,12 +1543,12 @@ sub update_exchangerate {
   if ($sth->fetchrow_array) {
     $query = qq|UPDATE exchangerate
                 SET $set
-                WHERE curr = (SELECT id FROM currencies WHERE curr = ?)
+                WHERE currency_id = (SELECT id FROM currencies WHERE name = ?)
                 AND transdate = ?|;
 
   } else {
-    $query = qq|INSERT INTO exchangerate (curr, buy, sell, transdate)
-                VALUES ((SELECT id FROM currencies WHERE curr = ?), $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);
@@ -1571,7 +1588,7 @@ sub get_exchangerate {
     return 1;
   }
 
-  $query = qq|SELECT curr FROM currencies WHERE id = (SELECT curr FROM defaults)|;
+  $query = qq|SELECT name AS curr FROM currencies WHERE id = (SELECT currency_id FROM defaults)|;
 
   my ($defaultcurrency) = selectrow_query($self, $dbh, $query);
 
@@ -1581,7 +1598,7 @@ sub get_exchangerate {
   }
 
   $query = qq|SELECT e.$fld FROM exchangerate e
-                 WHERE e.curr = (SELECT id FROM currencies WHERE 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);
 
 
@@ -1614,7 +1631,7 @@ sub check_exchangerate {
 
   my $dbh   = $self->get_standard_dbh($myconfig);
   my $query = qq|SELECT e.$fld FROM exchangerate e
-                 WHERE e.curr = (SELECT id FROM currencies WHERE 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);
 
@@ -1629,16 +1646,9 @@ sub get_all_currencies {
   my $self     = shift;
   my $myconfig = shift || \%::myconfig;
   my $dbh      = $self->get_standard_dbh($myconfig);
-  my @currencies =();
-
-  my $query = qq|SELECT curr FROM currencies|;
 
-  my $sth = prepare_execute_query($self, $dbh, $query);
-
-  while (my $ref = $sth->fetchrow_hashref()) {
-    push(@currencies, $ref->{curr});
-  }
-  $sth->finish;
+  my $query = qq|SELECT name FROM currencies|;
+  my @currencies = map { $_->{name} } selectall_hashref_query($self, $dbh, $query);
 
   $main::lxdebug->leave_sub();
 
@@ -1650,7 +1660,7 @@ sub get_default_currency {
 
   my ($self, $myconfig) = @_;
   my $dbh      = $self->get_standard_dbh($myconfig);
-  my $query = qq|SELECT curr FROM currencies WHERE id = (SELECT curr FROM defaults)|;
+  my $query = qq|SELECT name AS curr FROM currencies WHERE id = (SELECT currency_id FROM defaults)|;
 
   my ($defaultcurrency) = selectrow_query($self, $dbh, $query);
 
@@ -1903,6 +1913,7 @@ sub get_employee_data {
 
   my $self     = shift;
   my %params   = @_;
+  my $defaults = SL::DB::Default->get;
 
   Common::check_params(\%params, qw(prefix));
   Common::check_params_x(\%params, qw(id));
@@ -1919,7 +1930,8 @@ sub get_employee_data {
 
   if ($login) {
     my $user = User->new(login => $login);
-    map { $self->{$params{prefix} . "_${_}"} = $user->{$_}; } qw(address businessnumber co_ustid company duns email fax name signature taxnumber tel);
+    $self->{$params{prefix} . "_${_}"}    = $user->{$_}   for qw(email fax name signature tel);
+    $self->{$params{prefix} . "_${_}"}    = $defaults->$_ for qw(address businessnumber co_ustid company duns taxnumber);
 
     $self->{$params{prefix} . '_login'}   = $login;
     $self->{$params{prefix} . '_name'}  ||= $login;
@@ -1933,22 +1945,12 @@ sub get_duedate {
 
   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 ($payment_id, $duedate);
-
-  if($self->{payment_id}) {
-    $payment_id = $self->{payment_id};
-  } elsif($self->{vendor_id}) {
-    my $query = 'SELECT payment_id FROM vendor WHERE id = ?';
-    ($payment_id) = selectrow_query($self, $dbh, $query, $self->{vendor_id});
-  }
-
-  if ($payment_id) {
-    my $query  = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
-    ($duedate) = selectrow_query($self, $dbh, $query, $payment_id);
-  }
+  my $duedate = $terms ? $terms->calc_date(reference_date => $reference_date)->to_kivitendo : undef;
 
   $main::lxdebug->leave_sub();
 
@@ -2497,14 +2499,14 @@ sub all_vc {
   $table = $table eq "customer" ? "customer" : "vendor";
 
   # build selection list
-  # Hotfix für Bug 1837 - Besser wäre es alte Buchungsbelege
+  # 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, ...)
+  # 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);
 
-  if ($count < $myconfig->{vclimit}) {
+  if ($count <= $myconfig->{vclimit}) {
     $query = qq|SELECT id, name, salesman_id
                 FROM $table $obsolete
                 ORDER BY name|;
@@ -2707,7 +2709,7 @@ sub create_links {
     $query =
       qq|SELECT
            a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid,
-           a.duedate, a.ordnumber, a.taxincluded, (SELECT cu.curr FROM currencies cu WHERE cu.id=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.globalproject_id, ${extra_columns}
@@ -2773,14 +2775,7 @@ sub create_links {
          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|;
@@ -2812,7 +2807,7 @@ sub create_links {
     $query =
       qq|SELECT
            d.closedto, d.revtrans,
-           (SELECT cu.curr FROM currencies cu WHERE cu.id=d.curr) AS defaultcurrency,
+           (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|;
@@ -2825,7 +2820,7 @@ sub create_links {
     $query =
        qq|SELECT
             current_date AS transdate, d.closedto, d.revtrans,
-            (SELECT cu.curr FROM currencies cu WHERE cu.id=d.curr) AS defaultcurrency,
+            (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|;
@@ -2864,7 +2859,7 @@ sub lastname_used {
                     "a.department_id"         => "department_id",
                     "d.description"           => "department",
                     "ct.name"                 => $table,
-                    "cu.curr"                 => "currency",
+                    "cu.name"                 => "currency",
                     "current_date + ct.terms" => "duedate",
     );
 
@@ -2899,7 +2894,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)
-                        LEFT JOIN currencies cu ON (cu.id=ct.curr)
+                        LEFT JOIN currencies cu ON (cu.id=ct.currency_id)
                         WHERE a.id = ?|;
   my $ref          = selectfirst_hashref_query($self, $dbh, $query, $trans_id);
 
@@ -3374,17 +3369,24 @@ sub restore_vars {
 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)$/;
 
+  # 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)$/)) {
-    $self->{shiptoname}   = $::myconfig{company};
-    $self->{shiptostreet} = $::myconfig{address};
+    $self->{shiptoname}   = $defaults->company;
+    $self->{shiptostreet} = $defaults->address;
   }
 
   my $language = $self->{language} ? '_' . $self->{language} : '';
@@ -3426,7 +3428,7 @@ sub prepare_for_printing {
   }
 
   my $printer_code    = $self->{printer_code} ? '_' . $self->{printer_code} : '';
-  my $email_extension = -f "$::myconfig{templates}/$self->{formname}_email${language}.${extension}" ? '_email' : '';
+  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.