1 #====================================================================
 
   4 # Based on SQL-Ledger Version 2.1.9
 
   5 # Web http://www.lx-office.org
 
   7 #=====================================================================
 
   8 # SQL-Ledger Accounting
 
   9 # Copyright (C) 1998-2002
 
  11 #  Author: Dieter Simader
 
  12 #   Email: dsimader@sql-ledger.org
 
  13 #     Web: http://www.sql-ledger.org
 
  15 # Contributors: Thomas Bayen <bayen@gmx.de>
 
  16 #               Antti Kaihola <akaihola@siba.fi>
 
  17 #               Moritz Bunkus (tex code)
 
  19 # This program is free software; you can redistribute it and/or modify
 
  20 # it under the terms of the GNU General Public License as published by
 
  21 # the Free Software Foundation; either version 2 of the License, or
 
  22 # (at your option) any later version.
 
  24 # This program is distributed in the hope that it will be useful,
 
  25 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  26 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  27 # GNU General Public License for more details.
 
  28 # You should have received a copy of the GNU General Public License
 
  29 # along with this program; if not, write to the Free Software
 
  30 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
  31 #======================================================================
 
  32 # Utilities for parsing forms
 
  33 # and supporting routines for linking account numbers
 
  34 # used in AR, AP and IS, IR modules
 
  36 #======================================================================
 
  59 use List::Util qw(first max min sum);
 
  60 use List::MoreUtils qw(any apply);
 
  67   disconnect_standard_dbh();
 
  70 sub disconnect_standard_dbh {
 
  71   return unless $standard_dbh;
 
  72   $standard_dbh->disconnect();
 
  77   $main::lxdebug->enter_sub(2);
 
  83   my @tokens = split /((?:\[\+?\])?(?:\.|$))/, $key;
 
  88      $curr = \ $self->{ shift @tokens };
 
  92     my $sep = shift @tokens;
 
  93     my $key = shift @tokens;
 
  95     $curr = \ $$curr->[++$#$$curr], next if $sep eq '[]';
 
  96     $curr = \ $$curr->[max 0, $#$$curr]  if $sep eq '[].';
 
  97     $curr = \ $$curr->[++$#$$curr]       if $sep eq '[+].';
 
  98     $curr = \ $$curr->{$key}
 
 103   $main::lxdebug->leave_sub(2);
 
 109   $main::lxdebug->enter_sub(2);
 
 114   my @pairs = split(/&/, $input);
 
 117     my ($key, $value) = split(/=/, $_, 2);
 
 118     $self->_store_value($self->unescape($key), $self->unescape($value)) if ($key);
 
 121   $main::lxdebug->leave_sub(2);
 
 124 sub _request_to_hash {
 
 125   $main::lxdebug->enter_sub(2);
 
 130   if (!$ENV{'CONTENT_TYPE'}
 
 131       || ($ENV{'CONTENT_TYPE'} !~ /multipart\/form-data\s*;\s*boundary\s*=\s*(.+)$/)) {
 
 133     $self->_input_to_hash($input);
 
 135     $main::lxdebug->leave_sub(2);
 
 139   my ($name, $filename, $headers_done, $content_type, $boundary_found, $need_cr, $previous);
 
 141   my $boundary = '--' . $1;
 
 143   foreach my $line (split m/\n/, $input) {
 
 144     last if (($line eq "${boundary}--") || ($line eq "${boundary}--\r"));
 
 146     if (($line eq $boundary) || ($line eq "$boundary\r")) {
 
 147       ${ $previous } =~ s|\r?\n$|| if $previous;
 
 153       $content_type   = "text/plain";
 
 160     next unless $boundary_found;
 
 162     if (!$headers_done) {
 
 163       $line =~ s/[\r\n]*$//;
 
 170       if ($line =~ m|^content-disposition\s*:.*?form-data\s*;|i) {
 
 171         if ($line =~ m|filename\s*=\s*"(.*?)"|i) {
 
 173           substr $line, $-[0], $+[0] - $-[0], "";
 
 176         if ($line =~ m|name\s*=\s*"(.*?)"|i) {
 
 178           substr $line, $-[0], $+[0] - $-[0], "";
 
 181         $previous         = $self->_store_value($name, '') if ($name);
 
 182         $self->{FILENAME} = $filename if ($filename);
 
 187       if ($line =~ m|^content-type\s*:\s*(.*?)$|i) {
 
 194     next unless $previous;
 
 196     ${ $previous } .= "${line}\n";
 
 199   ${ $previous } =~ s|\r?\n$|| if $previous;
 
 201   $main::lxdebug->leave_sub(2);
 
 204 sub _recode_recursively {
 
 205   $main::lxdebug->enter_sub();
 
 206   my ($iconv, $param) = @_;
 
 208   if (any { ref $param eq $_ } qw(Form HASH)) {
 
 209     foreach my $key (keys %{ $param }) {
 
 210       if (!ref $param->{$key}) {
 
 211         # Workaround for a bug: converting $param->{$key} directly
 
 212         # leads to 'undef'. I don't know why. Converting a copy works,
 
 214         $param->{$key} = $iconv->convert("" . $param->{$key});
 
 216         _recode_recursively($iconv, $param->{$key});
 
 220   } elsif (ref $param eq 'ARRAY') {
 
 221     foreach my $idx (0 .. scalar(@{ $param }) - 1) {
 
 222       if (!ref $param->[$idx]) {
 
 223         # Workaround for a bug: converting $param->[$idx] directly
 
 224         # leads to 'undef'. I don't know why. Converting a copy works,
 
 226         $param->[$idx] = $iconv->convert("" . $param->[$idx]);
 
 228         _recode_recursively($iconv, $param->[$idx]);
 
 232   $main::lxdebug->leave_sub();
 
 236   $main::lxdebug->enter_sub();
 
 242   if ($LXDebug::watch_form) {
 
 243     require SL::Watchdog;
 
 244     tie %{ $self }, 'SL::Watchdog';
 
 249   $self->_input_to_hash($ENV{QUERY_STRING}) if $ENV{QUERY_STRING};
 
 250   $self->_input_to_hash($ARGV[0])           if @ARGV && $ARGV[0];
 
 252   if ($ENV{CONTENT_LENGTH}) {
 
 254     read STDIN, $content, $ENV{CONTENT_LENGTH};
 
 255     $self->_request_to_hash($content);
 
 258   my $db_charset   = $main::dbcharset;
 
 259   $db_charset    ||= Common::DEFAULT_CHARSET;
 
 261   my $encoding     = $self->{INPUT_ENCODING} || $db_charset;
 
 262   delete $self->{INPUT_ENCODING};
 
 264   _recode_recursively(SL::Iconv->new($encoding, $db_charset), $self);
 
 266   $self->{action}  =  lc $self->{action};
 
 267   $self->{action}  =~ s/( |-|,|\#)/_/g;
 
 269   #$self->{version} =  "2.6.1";                 # Old hardcoded but secure style
 
 270   open VERSION_FILE, "VERSION";                 # New but flexible code reads version from VERSION-file
 
 271   $self->{version} =  <VERSION_FILE>;
 
 273   $self->{version}  =~ s/[^0-9A-Za-z\.\_\-]//g; # only allow numbers, letters, points, underscores and dashes. Prevents injecting of malicious code.
 
 275   $main::lxdebug->leave_sub();
 
 280 sub _flatten_variables_rec {
 
 281   $main::lxdebug->enter_sub(2);
 
 290   if ('' eq ref $curr->{$key}) {
 
 291     @result = ({ 'key' => $prefix . $key, 'value' => $curr->{$key} });
 
 293   } elsif ('HASH' eq ref $curr->{$key}) {
 
 294     foreach my $hash_key (sort keys %{ $curr->{$key} }) {
 
 295       push @result, $self->_flatten_variables_rec($curr->{$key}, $prefix . $key . '.', $hash_key);
 
 299     foreach my $idx (0 .. scalar @{ $curr->{$key} } - 1) {
 
 300       my $first_array_entry = 1;
 
 302       foreach my $hash_key (sort keys %{ $curr->{$key}->[$idx] }) {
 
 303         push @result, $self->_flatten_variables_rec($curr->{$key}->[$idx], $prefix . $key . ($first_array_entry ? '[+].' : '[].'), $hash_key);
 
 304         $first_array_entry = 0;
 
 309   $main::lxdebug->leave_sub(2);
 
 314 sub flatten_variables {
 
 315   $main::lxdebug->enter_sub(2);
 
 323     push @variables, $self->_flatten_variables_rec($self, '', $_);
 
 326   $main::lxdebug->leave_sub(2);
 
 331 sub flatten_standard_variables {
 
 332   $main::lxdebug->enter_sub(2);
 
 335   my %skip_keys = map { $_ => 1 } (qw(login password header stylesheet titlebar version), @_);
 
 339   foreach (grep { ! $skip_keys{$_} } keys %{ $self }) {
 
 340     push @variables, $self->_flatten_variables_rec($self, '', $_);
 
 343   $main::lxdebug->leave_sub(2);
 
 349   $main::lxdebug->enter_sub();
 
 355   map { print "$_ = $self->{$_}\n" } (sort keys %{$self});
 
 357   $main::lxdebug->leave_sub();
 
 361   $main::lxdebug->enter_sub(2);
 
 364   my $password      = $self->{password};
 
 366   $self->{password} = 'X' x 8;
 
 368   local $Data::Dumper::Sortkeys = 1;
 
 369   my $output                    = Dumper($self);
 
 371   $self->{password} = $password;
 
 373   $main::lxdebug->leave_sub(2);
 
 379   $main::lxdebug->enter_sub(2);
 
 381   my ($self, $str) = @_;
 
 383   $str =  Encode::encode('utf-8-strict', $str) if $::locale->is_utf8;
 
 384   $str =~ s/([^a-zA-Z0-9_.-])/sprintf("%%%02x", ord($1))/ge;
 
 386   $main::lxdebug->leave_sub(2);
 
 392   $main::lxdebug->enter_sub(2);
 
 394   my ($self, $str) = @_;
 
 399   $str =~ s/%([0-9a-fA-Z]{2})/pack("c",hex($1))/eg;
 
 401   $main::lxdebug->leave_sub(2);
 
 407   $main::lxdebug->enter_sub();
 
 408   my ($self, $str) = @_;
 
 410   if ($str && !ref($str)) {
 
 411     $str =~ s/\"/"/g;
 
 414   $main::lxdebug->leave_sub();
 
 420   $main::lxdebug->enter_sub();
 
 421   my ($self, $str) = @_;
 
 423   if ($str && !ref($str)) {
 
 424     $str =~ s/"/\"/g;
 
 427   $main::lxdebug->leave_sub();
 
 433   $main::lxdebug->enter_sub();
 
 437     map({ print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n"); } @_);
 
 439     for (sort keys %$self) {
 
 440       next if (($_ eq "header") || (ref($self->{$_}) ne ""));
 
 441       print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n");
 
 444   $main::lxdebug->leave_sub();
 
 448   $main::lxdebug->enter_sub();
 
 450   $main::lxdebug->show_backtrace();
 
 452   my ($self, $msg) = @_;
 
 453   if ($ENV{HTTP_USER_AGENT}) {
 
 455     $self->show_generic_error($msg);
 
 458     print STDERR "Error: $msg\n";
 
 462   $main::lxdebug->leave_sub();
 
 466   $main::lxdebug->enter_sub();
 
 468   my ($self, $msg) = @_;
 
 470   if ($ENV{HTTP_USER_AGENT}) {
 
 473     if (!$self->{header}) {
 
 479     <p class="message_ok"><b>$msg</b></p>
 
 481     <script type="text/javascript">
 
 483     // If JavaScript is enabled, the whole thing will be reloaded.
 
 484     // The reason is: When one changes his menu setup (HTML / XUL / CSS ...)
 
 485     // it now loads the correct code into the browser instead of do nothing.
 
 486     setTimeout("top.frames.location.href='login.pl'",500);
 
 495     if ($self->{info_function}) {
 
 496       &{ $self->{info_function} }($msg);
 
 502   $main::lxdebug->leave_sub();
 
 505 # calculates the number of rows in a textarea based on the content and column number
 
 506 # can be capped with maxrows
 
 508   $main::lxdebug->enter_sub();
 
 509   my ($self, $str, $cols, $maxrows, $minrows) = @_;
 
 513   my $rows   = sum map { int((length() - 2) / $cols) + 1 } split /\r/, $str;
 
 516   $main::lxdebug->leave_sub();
 
 518   return max(min($rows, $maxrows), $minrows);
 
 522   $main::lxdebug->enter_sub();
 
 524   my ($self, $msg) = @_;
 
 526   $self->error("$msg\n" . $DBI::errstr);
 
 528   $main::lxdebug->leave_sub();
 
 532   $main::lxdebug->enter_sub();
 
 534   my ($self, $name, $msg) = @_;
 
 537   foreach my $part (split m/\./, $name) {
 
 538     if (!$curr->{$part} || ($curr->{$part} =~ /^\s*$/)) {
 
 541     $curr = $curr->{$part};
 
 544   $main::lxdebug->leave_sub();
 
 547 sub _get_request_uri {
 
 550   return URI->new($ENV{HTTP_REFERER})->canonical() if $ENV{HTTP_X_FORWARDED_FOR};
 
 552   my $scheme =  $ENV{HTTPS} && (lc $ENV{HTTPS} eq 'on') ? 'https' : 'http';
 
 553   my $port   =  $ENV{SERVER_PORT} || '';
 
 554   $port      =  undef if (($scheme eq 'http' ) && ($port == 80))
 
 555                       || (($scheme eq 'https') && ($port == 443));
 
 557   my $uri    =  URI->new("${scheme}://");
 
 558   $uri->scheme($scheme);
 
 560   $uri->host($ENV{HTTP_HOST} || $ENV{SERVER_ADDR});
 
 561   $uri->path_query($ENV{REQUEST_URI});
 
 567 sub _add_to_request_uri {
 
 570   my $relative_new_path = shift;
 
 571   my $request_uri       = shift || $self->_get_request_uri;
 
 572   my $relative_new_uri  = URI->new($relative_new_path);
 
 573   my @request_segments  = $request_uri->path_segments;
 
 575   my $new_uri           = $request_uri->clone;
 
 576   $new_uri->path_segments(@request_segments[0..scalar(@request_segments) - 2], $relative_new_uri->path_segments);
 
 581 sub create_http_response {
 
 582   $main::lxdebug->enter_sub();
 
 587   my $cgi      = $main::cgi;
 
 588   $cgi       ||= CGI->new('');
 
 591   if (defined $main::auth) {
 
 592     my $uri      = $self->_get_request_uri;
 
 593     my @segments = $uri->path_segments;
 
 595     $uri->path_segments(@segments);
 
 597     my $session_cookie_value = $main::auth->get_session_id();
 
 599     if ($session_cookie_value) {
 
 600       $session_cookie = $cgi->cookie('-name'   => $main::auth->get_session_cookie_name(),
 
 601                                      '-value'  => $session_cookie_value,
 
 602                                      '-path'   => $uri->path,
 
 603                                      '-secure' => $ENV{HTTPS});
 
 607   my %cgi_params = ('-type' => $params{content_type});
 
 608   $cgi_params{'-charset'} = $params{charset} if ($params{charset});
 
 609   $cgi_params{'-cookie'}  = $session_cookie  if ($session_cookie);
 
 611   my $output = $cgi->header(%cgi_params);
 
 613   $main::lxdebug->leave_sub();
 
 620   $::lxdebug->enter_sub;
 
 622   # extra code is currently only used by menuv3 and menuv4 to set their css.
 
 623   # it is strongly deprecated, and will be changed in a future version.
 
 624   my ($self, $extra_code) = @_;
 
 625   my $db_charset = $::dbcharset || Common::DEFAULT_CHARSET;
 
 628   $::lxdebug->leave_sub and return if !$ENV{HTTP_USER_AGENT} || $self->{header}++;
 
 630   $self->{favicon} ||= "favicon.ico";
 
 631   $self->{titlebar}  = "$self->{title} - $self->{titlebar}" if $self->{title};
 
 634   if ($self->{refresh_url} || $self->{refresh_time}) {
 
 635     my $refresh_time = $self->{refresh_time} || 3;
 
 636     my $refresh_url  = $self->{refresh_url}  || $ENV{REFERER};
 
 637     push @header, "<meta http-equiv='refresh' content='$refresh_time;$refresh_url'>";
 
 640   push @header, "<link rel='stylesheet' href='css/$_' type='text/css' title='Lx-Office stylesheet'>"
 
 641     for grep { -f "css/$_" } apply { s|.*/|| } $self->{stylesheet}, $self->{stylesheets};
 
 643   push @header, "<style type='text/css'>\@page { size:landscape; }</style>" if $self->{landscape};
 
 644   push @header, "<link rel='shortcut icon' href='$self->{favicon}' type='image/x-icon'>" if -f $self->{favicon};
 
 645   push @header, '<script type="text/javascript" src="js/jquery.js"></script>',
 
 646                 '<script type="text/javascript" src="js/common.js"></script>',
 
 647                 '<style type="text/css">@import url(js/jscalendar/calendar-win2k-1.css);</style>',
 
 648                 '<script type="text/javascript" src="js/jscalendar/calendar.js"></script>',
 
 649                 '<script type="text/javascript" src="js/jscalendar/lang/calendar-de.js"></script>',
 
 650                 '<script type="text/javascript" src="js/jscalendar/calendar-setup.js"></script>',
 
 651                 '<script type="text/javascript" src="js/part_selection.js"></script>';
 
 652   push @header, $self->{javascript} if $self->{javascript};
 
 653   push @header, map { $_->show_javascript } @{ $self->{AJAX} || [] };
 
 654   push @header, "<script type='text/javascript'>function fokus(){ document.$self->{fokus}.focus(); }</script>" if $self->{fokus};
 
 655   push @header, sprintf "<script type='text/javascript'>top.document.title='%s';</script>",
 
 656     join ' - ', grep $_, $self->{title}, $self->{login}, $::myconfig{dbname}, $self->{version} if $self->{title};
 
 658   # if there is a title, we put some JavaScript in to the page, wich writes a
 
 659   # meaningful title-tag for our frameset.
 
 661   if ($self->{title}) {
 
 663     <script type="text/javascript">
 
 665       // Write a meaningful title-tag for our frameset.
 
 666       top.document.title="| . $self->{"title"} . qq| - | . $self->{"login"} . qq| - | . $::myconfig{dbname} . qq| - V| . $self->{"version"} . qq|";
 
 672   print $self->create_http_response(content_type => 'text/html', charset => $db_charset);
 
 673   print "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>\n"
 
 674     if  $ENV{'HTTP_USER_AGENT'} =~ m/MSIE\s+\d/; # Other browsers may choke on menu scripts with DOCTYPE.
 
 678   <meta http-equiv="Content-Type" content="text/html; charset=$db_charset">
 
 679   <title>$self->{titlebar}</title>
 
 681   print "  $_\n" for @header;
 
 683   <link rel="stylesheet" href="css/jquery.autocomplete.css" type="text/css" />
 
 684   <meta name="robots" content="noindex,nofollow" />
 
 685   <script type="text/javascript" src="js/highlight_input.js"></script>
 
 686   <link rel="stylesheet" type="text/css" href="css/tabcontent.css" />
 
 687   <script type="text/javascript" src="js/tabcontent.js">
 
 689   /***********************************************
 
 690    * Tab Content script v2.2- Â© Dynamic Drive DHTML code library (www.dynamicdrive.com)
 
 691    * This notice MUST stay intact for legal use
 
 692    * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
 
 693    ***********************************************/
 
 702   $::lxdebug->leave_sub;
 
 705 sub ajax_response_header {
 
 706   $main::lxdebug->enter_sub();
 
 710   my $db_charset = $main::dbcharset ? $main::dbcharset : Common::DEFAULT_CHARSET;
 
 711   my $cgi        = $main::cgi || CGI->new('');
 
 712   my $output     = $cgi->header('-charset' => $db_charset);
 
 714   $main::lxdebug->leave_sub();
 
 719 sub redirect_header {
 
 723   my $base_uri = $self->_get_request_uri;
 
 724   my $new_uri  = URI->new_abs($new_url, $base_uri);
 
 726   die "Headers already sent" if $::self->{header};
 
 729   my $cgi = $main::cgi || CGI->new('');
 
 730   return $cgi->redirect($new_uri);
 
 733 sub set_standard_title {
 
 734   $::lxdebug->enter_sub;
 
 737   $self->{titlebar}  = "Lx-Office " . $::locale->text('Version') . " $self->{version}";
 
 738   $self->{titlebar} .= "- $::myconfig{name}"   if $::myconfig{name};
 
 739   $self->{titlebar} .= "- $::myconfig{dbname}" if $::myconfig{name};
 
 741   $::lxdebug->leave_sub;
 
 744 sub _prepare_html_template {
 
 745   $main::lxdebug->enter_sub();
 
 747   my ($self, $file, $additional_params) = @_;
 
 750   if (!%::myconfig || !$::myconfig{"countrycode"}) {
 
 751     $language = $main::language;
 
 753     $language = $main::myconfig{"countrycode"};
 
 755   $language = "de" unless ($language);
 
 757   if (-f "templates/webpages/${file}.html") {
 
 758     if ((-f ".developer") && ((stat("templates/webpages/${file}.html"))[9] > (stat("locale/${language}/all"))[9])) {
 
 759       my $info = "Developer information: templates/webpages/${file}.html is newer than the translation file locale/${language}/all.\n" .
 
 760         "Please re-run 'locales.pl' in 'locale/${language}'.";
 
 761       print(qq|<pre>$info</pre>|);
 
 765     $file = "templates/webpages/${file}.html";
 
 768     my $info = "Web page template '${file}' not found.\n" .
 
 769       "Please re-run 'locales.pl' in 'locale/${language}'.";
 
 770     print(qq|<pre>$info</pre>|);
 
 774   if ($self->{"DEBUG"}) {
 
 775     $additional_params->{"DEBUG"} = $self->{"DEBUG"};
 
 778   if ($additional_params->{"DEBUG"}) {
 
 779     $additional_params->{"DEBUG"} =
 
 780       "<br><em>DEBUG INFORMATION:</em><pre>" . $additional_params->{"DEBUG"} . "</pre>";
 
 783   if (%main::myconfig) {
 
 784     $::myconfig{jsc_dateformat} = apply {
 
 788     } $::myconfig{"dateformat"};
 
 789     $additional_params->{"myconfig"} ||= \%::myconfig;
 
 790     map { $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys %::myconfig;
 
 793   $additional_params->{"conf_dbcharset"}              = $::dbcharset;
 
 794   $additional_params->{"conf_webdav"}                 = $::webdav;
 
 795   $additional_params->{"conf_lizenzen"}               = $::lizenzen;
 
 796   $additional_params->{"conf_latex_templates"}        = $::latex;
 
 797   $additional_params->{"conf_opendocument_templates"} = $::opendocument_templates;
 
 798   $additional_params->{"conf_vertreter"}              = $::vertreter;
 
 799   $additional_params->{"conf_show_best_before"}       = $::show_best_before;
 
 800   $additional_params->{"conf_parts_image_css"}        = $::parts_image_css;
 
 801   $additional_params->{"conf_parts_listing_images"}   = $::parts_listing_images;
 
 802   $additional_params->{"conf_parts_show_image"}       = $::parts_show_image;
 
 804   if (%main::debug_options) {
 
 805     map { $additional_params->{'DEBUG_' . uc($_)} = $main::debug_options{$_} } keys %main::debug_options;
 
 808   if ($main::auth && $main::auth->{RIGHTS} && $main::auth->{RIGHTS}->{$self->{login}}) {
 
 809     while (my ($key, $value) = each %{ $main::auth->{RIGHTS}->{$self->{login}} }) {
 
 810       $additional_params->{"AUTH_RIGHTS_" . uc($key)} = $value;
 
 814   $main::lxdebug->leave_sub();
 
 819 sub parse_html_template {
 
 820   $main::lxdebug->enter_sub();
 
 822   my ($self, $file, $additional_params) = @_;
 
 824   $additional_params ||= { };
 
 826   my $real_file = $self->_prepare_html_template($file, $additional_params);
 
 827   my $template  = $self->template || $self->init_template;
 
 829   map { $additional_params->{$_} ||= $self->{$_} } keys %{ $self };
 
 832   $template->process($real_file, $additional_params, \$output) || die $template->error;
 
 834   $main::lxdebug->leave_sub();
 
 842   return if $self->template;
 
 844   return $self->template(Template->new({
 
 849      'PLUGIN_BASE'  => 'SL::Template::Plugin',
 
 850      'INCLUDE_PATH' => '.:templates/webpages',
 
 851      'COMPILE_EXT'  => '.tcc',
 
 852      'COMPILE_DIR'  => $::userspath . '/templates-cache',
 
 858   $self->{template_object} = shift if @_;
 
 859   return $self->{template_object};
 
 862 sub show_generic_error {
 
 863   $main::lxdebug->enter_sub();
 
 865   my ($self, $error, %params) = @_;
 
 868     'title_error' => $params{title},
 
 869     'label_error' => $error,
 
 872   if ($params{action}) {
 
 875     map { delete($self->{$_}); } qw(action);
 
 876     map { push @vars, { "name" => $_, "value" => $self->{$_} } if (!ref($self->{$_})); } keys %{ $self };
 
 878     $add_params->{SHOW_BUTTON}  = 1;
 
 879     $add_params->{BUTTON_LABEL} = $params{label} || $params{action};
 
 880     $add_params->{VARIABLES}    = \@vars;
 
 882   } elsif ($params{back_button}) {
 
 883     $add_params->{SHOW_BACK_BUTTON} = 1;
 
 886   $self->{title} = $params{title} if $params{title};
 
 889   print $self->parse_html_template("generic/error", $add_params);
 
 891   print STDERR "Error: $error\n";
 
 893   $main::lxdebug->leave_sub();
 
 898 sub show_generic_information {
 
 899   $main::lxdebug->enter_sub();
 
 901   my ($self, $text, $title) = @_;
 
 904     'title_information' => $title,
 
 905     'label_information' => $text,
 
 908   $self->{title} = $title if ($title);
 
 911   print $self->parse_html_template("generic/information", $add_params);
 
 913   $main::lxdebug->leave_sub();
 
 918 # write Trigger JavaScript-Code ($qty = quantity of Triggers)
 
 919 # changed it to accept an arbitrary number of triggers - sschoeling
 
 921   $main::lxdebug->enter_sub();
 
 924   my $myconfig = shift;
 
 927   # set dateform for jsscript
 
 930     "dd.mm.yy" => "%d.%m.%Y",
 
 931     "dd-mm-yy" => "%d-%m-%Y",
 
 932     "dd/mm/yy" => "%d/%m/%Y",
 
 933     "mm/dd/yy" => "%m/%d/%Y",
 
 934     "mm-dd-yy" => "%m-%d-%Y",
 
 935     "yyyy-mm-dd" => "%Y-%m-%d",
 
 938   my $ifFormat = defined($dateformats{$myconfig->{"dateformat"}}) ?
 
 939     $dateformats{$myconfig->{"dateformat"}} : "%d.%m.%Y";
 
 946       inputField : "| . (shift) . qq|",
 
 947       ifFormat :"$ifFormat",
 
 948       align : "| .  (shift) . qq|",
 
 949       button : "| . (shift) . qq|"
 
 955        <script type="text/javascript">
 
 956        <!--| . join("", @triggers) . qq|//-->
 
 960   $main::lxdebug->leave_sub();
 
 963 }    #end sub write_trigger
 
 966   $main::lxdebug->enter_sub();
 
 968   my ($self, $msg) = @_;
 
 970   if (!$self->{callback}) {
 
 976 #  my ($script, $argv) = split(/\?/, $self->{callback}, 2);
 
 977 #  $script =~ s|.*/||;
 
 978 #  $script =~ s|[^a-zA-Z0-9_\.]||g;
 
 979 #  exec("perl", "$script", $argv);
 
 981   print $::form->redirect_header($self->{callback});
 
 983   $main::lxdebug->leave_sub();
 
 986 # sort of columns removed - empty sub
 
 988   $main::lxdebug->enter_sub();
 
 990   my ($self, @columns) = @_;
 
 992   $main::lxdebug->leave_sub();
 
 998   $main::lxdebug->enter_sub(2);
 
1000   my ($self, $myconfig, $amount, $places, $dash) = @_;
 
1002   if ($amount eq "") {
 
1006   # Hey watch out! The amount can be an exponential term like 1.13686837721616e-13
 
1008   my $neg = ($amount =~ s/^-//);
 
1009   my $exp = ($amount =~ m/[e]/) ? 1 : 0;
 
1011   if (defined($places) && ($places ne '')) {
 
1017         my ($actual_places) = ($amount =~ /\.(\d+)/);
 
1018         $actual_places = length($actual_places);
 
1019         $places = $actual_places > $places ? $actual_places : $places;
 
1022     $amount = $self->round_amount($amount, $places);
 
1025   my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars
 
1026   my @p = split(/\./, $amount); # split amount at decimal point
 
1028   $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters
 
1031   $amount .= $d[0].$p[1].(0 x ($places - length $p[1])) if ($places || $p[1] ne '');
 
1034     ($dash =~ /-/)    ? ($neg ? "($amount)"                            : "$amount" )                              :
 
1035     ($dash =~ /DRCR/) ? ($neg ? "$amount " . $main::locale->text('DR') : "$amount " . $main::locale->text('CR') ) :
 
1036                         ($neg ? "-$amount"                             : "$amount" )                              ;
 
1040   $main::lxdebug->leave_sub(2);
 
1044 sub format_amount_units {
 
1045   $main::lxdebug->enter_sub();
 
1050   my $myconfig         = \%main::myconfig;
 
1051   my $amount           = $params{amount} * 1;
 
1052   my $places           = $params{places};
 
1053   my $part_unit_name   = $params{part_unit};
 
1054   my $amount_unit_name = $params{amount_unit};
 
1055   my $conv_units       = $params{conv_units};
 
1056   my $max_places       = $params{max_places};
 
1058   if (!$part_unit_name) {
 
1059     $main::lxdebug->leave_sub();
 
1063   AM->retrieve_all_units();
 
1064   my $all_units        = $main::all_units;
 
1066   if (('' eq ref $conv_units) && ($conv_units =~ /convertible/)) {
 
1067     $conv_units = AM->convertible_units($all_units, $part_unit_name, $conv_units eq 'convertible_not_smaller');
 
1070   if (!scalar @{ $conv_units }) {
 
1071     my $result = $self->format_amount($myconfig, $amount, $places, undef, $max_places) . " " . $part_unit_name;
 
1072     $main::lxdebug->leave_sub();
 
1076   my $part_unit  = $all_units->{$part_unit_name};
 
1077   my $conv_unit  = ($amount_unit_name && ($amount_unit_name ne $part_unit_name)) ? $all_units->{$amount_unit_name} : $part_unit;
 
1079   $amount       *= $conv_unit->{factor};
 
1084   foreach my $unit (@$conv_units) {
 
1085     my $last = $unit->{name} eq $part_unit->{name};
 
1087       $num     = int($amount / $unit->{factor});
 
1088       $amount -= $num * $unit->{factor};
 
1091     if ($last ? $amount : $num) {
 
1092       push @values, { "unit"   => $unit->{name},
 
1093                       "amount" => $last ? $amount / $unit->{factor} : $num,
 
1094                       "places" => $last ? $places : 0 };
 
1101     push @values, { "unit"   => $part_unit_name,
 
1106   my $result = join " ", map { $self->format_amount($myconfig, $_->{amount}, $_->{places}, undef, $max_places), $_->{unit} } @values;
 
1108   $main::lxdebug->leave_sub();
 
1114   $main::lxdebug->enter_sub(2);
 
1119   $input =~ s/(^|[^\#]) \#  (\d+)  /$1$_[$2 - 1]/gx;
 
1120   $input =~ s/(^|[^\#]) \#\{(\d+)\}/$1$_[$2 - 1]/gx;
 
1121   $input =~ s/\#\#/\#/g;
 
1123   $main::lxdebug->leave_sub(2);
 
1131   $main::lxdebug->enter_sub(2);
 
1133   my ($self, $myconfig, $amount) = @_;
 
1135   if (   ($myconfig->{numberformat} eq '1.000,00')
 
1136       || ($myconfig->{numberformat} eq '1000,00')) {
 
1141   if ($myconfig->{numberformat} eq "1'000.00") {
 
1147   $main::lxdebug->leave_sub(2);
 
1149   return ($amount * 1);
 
1153   $main::lxdebug->enter_sub(2);
 
1155   my ($self, $amount, $places) = @_;
 
1158   # Rounding like "Kaufmannsrunden" (see http://de.wikipedia.org/wiki/Rundung )
 
1160   # Round amounts to eight places before rounding to the requested
 
1161   # number of places. This gets rid of errors due to internal floating
 
1162   # point representation.
 
1163   $amount       = $self->round_amount($amount, 8) if $places < 8;
 
1164   $amount       = $amount * (10**($places));
 
1165   $round_amount = int($amount + .5 * ($amount <=> 0)) / (10**($places));
 
1167   $main::lxdebug->leave_sub(2);
 
1169   return $round_amount;
 
1173 sub parse_template {
 
1174   $main::lxdebug->enter_sub();
 
1176   my ($self, $myconfig, $userspath) = @_;
 
1181   $self->{"cwd"} = getcwd();
 
1182   $self->{"tmpdir"} = $self->{cwd} . "/${userspath}";
 
1187   if ($self->{"format"} =~ /(opendocument|oasis)/i) {
 
1188     $template_type  = 'OpenDocument';
 
1189     $ext_for_format = $self->{"format"} =~ m/pdf/ ? 'pdf' : 'odt';
 
1191   } elsif ($self->{"format"} =~ /(postscript|pdf)/i) {
 
1192     $ENV{"TEXINPUTS"} = ".:" . getcwd() . "/" . $myconfig->{"templates"} . ":" . $ENV{"TEXINPUTS"};
 
1193     $template_type    = 'LaTeX';
 
1194     $ext_for_format   = 'pdf';
 
1196   } elsif (($self->{"format"} =~ /html/i) || (!$self->{"format"} && ($self->{"IN"} =~ /html$/i))) {
 
1197     $template_type  = 'HTML';
 
1198     $ext_for_format = 'html';
 
1200   } elsif (($self->{"format"} =~ /xml/i) || (!$self->{"format"} && ($self->{"IN"} =~ /xml$/i))) {
 
1201     $template_type  = 'XML';
 
1202     $ext_for_format = 'xml';
 
1204   } elsif ( $self->{"format"} =~ /elster(?:winston|taxbird)/i ) {
 
1205     $template_type = 'XML';
 
1207   } elsif ( $self->{"format"} =~ /excel/i ) {
 
1208     $template_type  = 'Excel';
 
1209     $ext_for_format = 'xls';
 
1211   } elsif ( defined $self->{'format'}) {
 
1212     $self->error("Outputformat not defined. This may be a future feature: $self->{'format'}");
 
1214   } elsif ( $self->{'format'} eq '' ) {
 
1215     $self->error("No Outputformat given: $self->{'format'}");
 
1217   } else { #Catch the rest
 
1218     $self->error("Outputformat not defined: $self->{'format'}");
 
1221   my $template = SL::Template::create(type      => $template_type,
 
1222                                       file_name => $self->{IN},
 
1224                                       myconfig  => $myconfig,
 
1225                                       userspath => $userspath);
 
1227   # Copy the notes from the invoice/sales order etc. back to the variable "notes" because that is where most templates expect it to be.
 
1228   $self->{"notes"} = $self->{ $self->{"formname"} . "notes" };
 
1230   if (!$self->{employee_id}) {
 
1231     map { $self->{"employee_${_}"} = $myconfig->{$_}; } qw(email tel fax name signature company address businessnumber co_ustid taxnumber duns);
 
1234   map { $self->{"${_}"} = $myconfig->{$_}; } qw(co_ustid);
 
1236   $self->{copies} = 1 if (($self->{copies} *= 1) <= 0);
 
1238   # OUT is used for the media, screen, printer, email
 
1239   # for postscript we store a copy in a temporary file
 
1241   my $prepend_userspath;
 
1243   if (!$self->{tmpfile}) {
 
1244     $self->{tmpfile}   = "${fileid}.$self->{IN}";
 
1245     $prepend_userspath = 1;
 
1248   $prepend_userspath = 1 if substr($self->{tmpfile}, 0, length $userspath) eq $userspath;
 
1250   $self->{tmpfile} =~ s|.*/||;
 
1251   $self->{tmpfile} =~ s/[^a-zA-Z0-9\._\ \-]//g;
 
1252   $self->{tmpfile} = "$userspath/$self->{tmpfile}" if $prepend_userspath;
 
1254   if ($template->uses_temp_file() || $self->{media} eq 'email') {
 
1255     $out = $self->{OUT};
 
1256     $self->{OUT} = ">$self->{tmpfile}";
 
1262     open OUT, "$self->{OUT}" or $self->error("$self->{OUT} : $!");
 
1263     $result = $template->parse(*OUT);
 
1268     $result = $template->parse(*STDOUT);
 
1273     $self->error("$self->{IN} : " . $template->get_error());
 
1276   if ($template->uses_temp_file() || $self->{media} eq 'email') {
 
1278     if ($self->{media} eq 'email') {
 
1280       my $mail = new Mailer;
 
1282       map { $mail->{$_} = $self->{$_} }
 
1283         qw(cc bcc subject message version format);
 
1284       $mail->{charset} = $main::dbcharset ? $main::dbcharset : Common::DEFAULT_CHARSET;
 
1285       $mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
 
1286       $mail->{from}   = qq|"$myconfig->{name}" <$myconfig->{email}>|;
 
1287       $mail->{fileid} = "$fileid.";
 
1288       $myconfig->{signature} =~ s/\r//g;
 
1290       # if we send html or plain text inline
 
1291       if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
 
1292         $mail->{contenttype} = "text/html";
 
1294         $mail->{message}       =~ s/\r//g;
 
1295         $mail->{message}       =~ s/\n/<br>\n/g;
 
1296         $myconfig->{signature} =~ s/\n/<br>\n/g;
 
1297         $mail->{message} .= "<br>\n-- <br>\n$myconfig->{signature}\n<br>";
 
1299         open(IN, $self->{tmpfile})
 
1300           or $self->error($self->cleanup . "$self->{tmpfile} : $!");
 
1302           $mail->{message} .= $_;
 
1309         if (!$self->{"do_not_attach"}) {
 
1310           my $attachment_name  =  $self->{attachment_filename} || $self->{tmpfile};
 
1311           $attachment_name     =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
 
1312           $mail->{attachments} =  [{ "filename" => $self->{tmpfile},
 
1313                                      "name"     => $attachment_name }];
 
1316         $mail->{message}  =~ s/\r//g;
 
1317         $mail->{message} .=  "\n-- \n$myconfig->{signature}";
 
1321       my $err = $mail->send();
 
1322       $self->error($self->cleanup . "$err") if ($err);
 
1326       $self->{OUT} = $out;
 
1328       my $numbytes = (-s $self->{tmpfile});
 
1329       open(IN, $self->{tmpfile})
 
1330         or $self->error($self->cleanup . "$self->{tmpfile} : $!");
 
1332       $self->{copies} = 1 unless $self->{media} eq 'printer';
 
1334       chdir("$self->{cwd}");
 
1335       #print(STDERR "Kopien $self->{copies}\n");
 
1336       #print(STDERR "OUT $self->{OUT}\n");
 
1337       for my $i (1 .. $self->{copies}) {
 
1339           open OUT, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
 
1340           print OUT while <IN>;
 
1345           $self->{attachment_filename} = ($self->{attachment_filename})
 
1346                                        ? $self->{attachment_filename}
 
1347                                        : $self->generate_attachment_filename();
 
1349           # launch application
 
1350           print qq|Content-Type: | . $template->get_mime_type() . qq|
 
1351 Content-Disposition: attachment; filename="$self->{attachment_filename}"
 
1352 Content-Length: $numbytes
 
1356           $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
 
1367   chdir("$self->{cwd}");
 
1368   $main::lxdebug->leave_sub();
 
1371 sub get_formname_translation {
 
1372   $main::lxdebug->enter_sub();
 
1373   my ($self, $formname) = @_;
 
1375   $formname ||= $self->{formname};
 
1377   my %formname_translations = (
 
1378     bin_list                => $main::locale->text('Bin List'),
 
1379     credit_note             => $main::locale->text('Credit Note'),
 
1380     invoice                 => $main::locale->text('Invoice'),
 
1381     pick_list               => $main::locale->text('Pick List'),
 
1382     proforma                => $main::locale->text('Proforma Invoice'),
 
1383     purchase_order          => $main::locale->text('Purchase Order'),
 
1384     request_quotation       => $main::locale->text('RFQ'),
 
1385     sales_order             => $main::locale->text('Confirmation'),
 
1386     sales_quotation         => $main::locale->text('Quotation'),
 
1387     storno_invoice          => $main::locale->text('Storno Invoice'),
 
1388     sales_delivery_order    => $main::locale->text('Delivery Order'),
 
1389     purchase_delivery_order => $main::locale->text('Delivery Order'),
 
1390     dunning                 => $main::locale->text('Dunning'),
 
1393   $main::lxdebug->leave_sub();
 
1394   return $formname_translations{$formname}
 
1397 sub get_number_prefix_for_type {
 
1398   $main::lxdebug->enter_sub();
 
1402       (first { $self->{type} eq $_ } qw(invoice credit_note)) ? 'inv'
 
1403     : ($self->{type} =~ /_quotation$/)                        ? 'quo'
 
1404     : ($self->{type} =~ /_delivery_order$/)                   ? 'do'
 
1407   $main::lxdebug->leave_sub();
 
1411 sub get_extension_for_format {
 
1412   $main::lxdebug->enter_sub();
 
1415   my $extension = $self->{format} =~ /pdf/i          ? ".pdf"
 
1416                 : $self->{format} =~ /postscript/i   ? ".ps"
 
1417                 : $self->{format} =~ /opendocument/i ? ".odt"
 
1418                 : $self->{format} =~ /excel/i        ? ".xls"
 
1419                 : $self->{format} =~ /html/i         ? ".html"
 
1422   $main::lxdebug->leave_sub();
 
1426 sub generate_attachment_filename {
 
1427   $main::lxdebug->enter_sub();
 
1430   my $attachment_filename = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
 
1431   my $prefix              = $self->get_number_prefix_for_type();
 
1433   if ($self->{preview} && (first { $self->{type} eq $_ } qw(invoice credit_note))) {
 
1434     $attachment_filename .= ' (' . $main::locale->text('Preview') . ')' . $self->get_extension_for_format();
 
1436   } elsif ($attachment_filename && $self->{"${prefix}number"}) {
 
1437     $attachment_filename .=  "_" . $self->{"${prefix}number"} . $self->get_extension_for_format();
 
1440     $attachment_filename = "";
 
1443   $attachment_filename =  $main::locale->quote_special_chars('filenames', $attachment_filename);
 
1444   $attachment_filename =~ s|[\s/\\]+|_|g;
 
1446   $main::lxdebug->leave_sub();
 
1447   return $attachment_filename;
 
1450 sub generate_email_subject {
 
1451   $main::lxdebug->enter_sub();
 
1454   my $subject = $main::locale->unquote_special_chars('HTML', $self->get_formname_translation());
 
1455   my $prefix  = $self->get_number_prefix_for_type();
 
1457   if ($subject && $self->{"${prefix}number"}) {
 
1458     $subject .= " " . $self->{"${prefix}number"}
 
1461   $main::lxdebug->leave_sub();
 
1466   $main::lxdebug->enter_sub();
 
1470   chdir("$self->{tmpdir}");
 
1473   if (-f "$self->{tmpfile}.err") {
 
1474     open(FH, "$self->{tmpfile}.err");
 
1479   if ($self->{tmpfile} && ! $::keep_temp_files) {
 
1480     $self->{tmpfile} =~ s|.*/||g;
 
1482     $self->{tmpfile} =~ s/\.\w+$//g;
 
1483     my $tmpfile = $self->{tmpfile};
 
1484     unlink(<$tmpfile.*>);
 
1487   chdir("$self->{cwd}");
 
1489   $main::lxdebug->leave_sub();
 
1495   $main::lxdebug->enter_sub();
 
1497   my ($self, $date, $myconfig) = @_;
 
1500   if ($date && $date =~ /\D/) {
 
1502     if ($myconfig->{dateformat} =~ /^yy/) {
 
1503       ($yy, $mm, $dd) = split /\D/, $date;
 
1505     if ($myconfig->{dateformat} =~ /^mm/) {
 
1506       ($mm, $dd, $yy) = split /\D/, $date;
 
1508     if ($myconfig->{dateformat} =~ /^dd/) {
 
1509       ($dd, $mm, $yy) = split /\D/, $date;
 
1514     $yy = ($yy < 70) ? $yy + 2000 : $yy;
 
1515     $yy = ($yy >= 70 && $yy <= 99) ? $yy + 1900 : $yy;
 
1517     $dd = "0$dd" if ($dd < 10);
 
1518     $mm = "0$mm" if ($mm < 10);
 
1520     $date = "$yy$mm$dd";
 
1523   $main::lxdebug->leave_sub();
 
1528 # Database routines used throughout
 
1530 sub _dbconnect_options {
 
1532   my $options = { pg_enable_utf8 => $::locale->is_utf8,
 
1539   $main::lxdebug->enter_sub(2);
 
1541   my ($self, $myconfig) = @_;
 
1543   # connect to database
 
1544   my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options)
 
1548   if ($myconfig->{dboptions}) {
 
1549     $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
 
1552   $main::lxdebug->leave_sub(2);
 
1557 sub dbconnect_noauto {
 
1558   $main::lxdebug->enter_sub();
 
1560   my ($self, $myconfig) = @_;
 
1562   # connect to database
 
1563   my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options(AutoCommit => 0))
 
1567   if ($myconfig->{dboptions}) {
 
1568     $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
 
1571   $main::lxdebug->leave_sub();
 
1576 sub get_standard_dbh {
 
1577   $main::lxdebug->enter_sub(2);
 
1580   my $myconfig = shift || \%::myconfig;
 
1582   if ($standard_dbh && !$standard_dbh->{Active}) {
 
1583     $main::lxdebug->message(LXDebug->INFO(), "get_standard_dbh: \$standard_dbh is defined but not Active anymore");
 
1584     undef $standard_dbh;
 
1587   $standard_dbh ||= $self->dbconnect_noauto($myconfig);
 
1589   $main::lxdebug->leave_sub(2);
 
1591   return $standard_dbh;
 
1595   $main::lxdebug->enter_sub();
 
1597   my ($self, $date, $myconfig) = @_;
 
1598   my $dbh = $self->dbconnect($myconfig);
 
1600   my $query = "SELECT 1 FROM defaults WHERE ? < closedto";
 
1601   my $sth = prepare_execute_query($self, $dbh, $query, $date);
 
1602   my ($closed) = $sth->fetchrow_array;
 
1604   $main::lxdebug->leave_sub();
 
1609 sub update_balance {
 
1610   $main::lxdebug->enter_sub();
 
1612   my ($self, $dbh, $table, $field, $where, $value, @values) = @_;
 
1614   # if we have a value, go do it
 
1617     # retrieve balance from table
 
1618     my $query = "SELECT $field FROM $table WHERE $where FOR UPDATE";
 
1619     my $sth = prepare_execute_query($self, $dbh, $query, @values);
 
1620     my ($balance) = $sth->fetchrow_array;
 
1626     $query = "UPDATE $table SET $field = $balance WHERE $where";
 
1627     do_query($self, $dbh, $query, @values);
 
1629   $main::lxdebug->leave_sub();
 
1632 sub update_exchangerate {
 
1633   $main::lxdebug->enter_sub();
 
1635   my ($self, $dbh, $curr, $transdate, $buy, $sell) = @_;
 
1637   # some sanity check for currency
 
1639     $main::lxdebug->leave_sub();
 
1642   $query = qq|SELECT curr FROM defaults|;
 
1644   my ($currency) = selectrow_query($self, $dbh, $query);
 
1645   my ($defaultcurrency) = split m/:/, $currency;
 
1648   if ($curr eq $defaultcurrency) {
 
1649     $main::lxdebug->leave_sub();
 
1653   $query = qq|SELECT e.curr FROM exchangerate e
 
1654                  WHERE e.curr = ? AND e.transdate = ?
 
1656   my $sth = prepare_execute_query($self, $dbh, $query, $curr, $transdate);
 
1665   $buy = conv_i($buy, "NULL");
 
1666   $sell = conv_i($sell, "NULL");
 
1669   if ($buy != 0 && $sell != 0) {
 
1670     $set = "buy = $buy, sell = $sell";
 
1671   } elsif ($buy != 0) {
 
1672     $set = "buy = $buy";
 
1673   } elsif ($sell != 0) {
 
1674     $set = "sell = $sell";
 
1677   if ($sth->fetchrow_array) {
 
1678     $query = qq|UPDATE exchangerate
 
1684     $query = qq|INSERT INTO exchangerate (curr, buy, sell, transdate)
 
1685                 VALUES (?, $buy, $sell, ?)|;
 
1688   do_query($self, $dbh, $query, $curr, $transdate);
 
1690   $main::lxdebug->leave_sub();
 
1693 sub save_exchangerate {
 
1694   $main::lxdebug->enter_sub();
 
1696   my ($self, $myconfig, $currency, $transdate, $rate, $fld) = @_;
 
1698   my $dbh = $self->dbconnect($myconfig);
 
1702   $buy  = $rate if $fld eq 'buy';
 
1703   $sell = $rate if $fld eq 'sell';
 
1706   $self->update_exchangerate($dbh, $currency, $transdate, $buy, $sell);
 
1711   $main::lxdebug->leave_sub();
 
1714 sub get_exchangerate {
 
1715   $main::lxdebug->enter_sub();
 
1717   my ($self, $dbh, $curr, $transdate, $fld) = @_;
 
1720   unless ($transdate) {
 
1721     $main::lxdebug->leave_sub();
 
1725   $query = qq|SELECT curr FROM defaults|;
 
1727   my ($currency) = selectrow_query($self, $dbh, $query);
 
1728   my ($defaultcurrency) = split m/:/, $currency;
 
1730   if ($currency eq $defaultcurrency) {
 
1731     $main::lxdebug->leave_sub();
 
1735   $query = qq|SELECT e.$fld FROM exchangerate e
 
1736                  WHERE e.curr = ? AND e.transdate = ?|;
 
1737   my ($exchangerate) = selectrow_query($self, $dbh, $query, $curr, $transdate);
 
1741   $main::lxdebug->leave_sub();
 
1743   return $exchangerate;
 
1746 sub check_exchangerate {
 
1747   $main::lxdebug->enter_sub();
 
1749   my ($self, $myconfig, $currency, $transdate, $fld) = @_;
 
1751   if ($fld !~/^buy|sell$/) {
 
1752     $self->error('Fatal: check_exchangerate called with invalid buy/sell argument');
 
1755   unless ($transdate) {
 
1756     $main::lxdebug->leave_sub();
 
1760   my ($defaultcurrency) = $self->get_default_currency($myconfig);
 
1762   if ($currency eq $defaultcurrency) {
 
1763     $main::lxdebug->leave_sub();
 
1767   my $dbh   = $self->get_standard_dbh($myconfig);
 
1768   my $query = qq|SELECT e.$fld FROM exchangerate e
 
1769                  WHERE e.curr = ? AND e.transdate = ?|;
 
1771   my ($exchangerate) = selectrow_query($self, $dbh, $query, $currency, $transdate);
 
1773   $main::lxdebug->leave_sub();
 
1775   return $exchangerate;
 
1778 sub get_all_currencies {
 
1779   $main::lxdebug->enter_sub();
 
1782   my $myconfig = shift || \%::myconfig;
 
1783   my $dbh      = $self->get_standard_dbh($myconfig);
 
1785   my $query = qq|SELECT curr FROM defaults|;
 
1787   my ($curr)     = selectrow_query($self, $dbh, $query);
 
1788   my @currencies = grep { $_ } map { s/\s//g; $_ } split m/:/, $curr;
 
1790   $main::lxdebug->leave_sub();
 
1795 sub get_default_currency {
 
1796   $main::lxdebug->enter_sub();
 
1798   my ($self, $myconfig) = @_;
 
1799   my @currencies        = $self->get_all_currencies($myconfig);
 
1801   $main::lxdebug->leave_sub();
 
1803   return $currencies[0];
 
1806 sub set_payment_options {
 
1807   $main::lxdebug->enter_sub();
 
1809   my ($self, $myconfig, $transdate) = @_;
 
1811   return $main::lxdebug->leave_sub() unless ($self->{payment_id});
 
1813   my $dbh = $self->get_standard_dbh($myconfig);
 
1816     qq|SELECT p.terms_netto, p.terms_skonto, p.percent_skonto, p.description_long | .
 
1817     qq|FROM payment_terms p | .
 
1820   ($self->{terms_netto}, $self->{terms_skonto}, $self->{percent_skonto},
 
1821    $self->{payment_terms}) =
 
1822      selectrow_query($self, $dbh, $query, $self->{payment_id});
 
1824   if ($transdate eq "") {
 
1825     if ($self->{invdate}) {
 
1826       $transdate = $self->{invdate};
 
1828       $transdate = $self->{transdate};
 
1833     qq|SELECT ?::date + ?::integer AS netto_date, ?::date + ?::integer AS skonto_date | .
 
1834     qq|FROM payment_terms|;
 
1835   ($self->{netto_date}, $self->{skonto_date}) =
 
1836     selectrow_query($self, $dbh, $query, $transdate, $self->{terms_netto}, $transdate, $self->{terms_skonto});
 
1838   my ($invtotal, $total);
 
1839   my (%amounts, %formatted_amounts);
 
1841   if ($self->{type} =~ /_order$/) {
 
1842     $amounts{invtotal} = $self->{ordtotal};
 
1843     $amounts{total}    = $self->{ordtotal};
 
1845   } elsif ($self->{type} =~ /_quotation$/) {
 
1846     $amounts{invtotal} = $self->{quototal};
 
1847     $amounts{total}    = $self->{quototal};
 
1850     $amounts{invtotal} = $self->{invtotal};
 
1851     $amounts{total}    = $self->{total};
 
1853   $amounts{skonto_in_percent} = 100.0 * $self->{percent_skonto};
 
1855   map { $amounts{$_} = $self->parse_amount($myconfig, $amounts{$_}) } keys %amounts;
 
1857   $amounts{skonto_amount}      = $amounts{invtotal} * $self->{percent_skonto};
 
1858   $amounts{invtotal_wo_skonto} = $amounts{invtotal} * (1 - $self->{percent_skonto});
 
1859   $amounts{total_wo_skonto}    = $amounts{total}    * (1 - $self->{percent_skonto});
 
1861   foreach (keys %amounts) {
 
1862     $amounts{$_}           = $self->round_amount($amounts{$_}, 2);
 
1863     $formatted_amounts{$_} = $self->format_amount($myconfig, $amounts{$_}, 2);
 
1866   if ($self->{"language_id"}) {
 
1868       qq|SELECT t.description_long, l.output_numberformat, l.output_dateformat, l.output_longdates | .
 
1869       qq|FROM translation_payment_terms t | .
 
1870       qq|LEFT JOIN language l ON t.language_id = l.id | .
 
1871       qq|WHERE (t.language_id = ?) AND (t.payment_terms_id = ?)|;
 
1872     my ($description_long, $output_numberformat, $output_dateformat,
 
1873       $output_longdates) =
 
1874       selectrow_query($self, $dbh, $query,
 
1875                       $self->{"language_id"}, $self->{"payment_id"});
 
1877     $self->{payment_terms} = $description_long if ($description_long);
 
1879     if ($output_dateformat) {
 
1880       foreach my $key (qw(netto_date skonto_date)) {
 
1882           $main::locale->reformat_date($myconfig, $self->{$key},
 
1888     if ($output_numberformat &&
 
1889         ($output_numberformat ne $myconfig->{"numberformat"})) {
 
1890       my $saved_numberformat = $myconfig->{"numberformat"};
 
1891       $myconfig->{"numberformat"} = $output_numberformat;
 
1892       map { $formatted_amounts{$_} = $self->format_amount($myconfig, $amounts{$_}) } keys %amounts;
 
1893       $myconfig->{"numberformat"} = $saved_numberformat;
 
1897   $self->{payment_terms} =~ s/<%netto_date%>/$self->{netto_date}/g;
 
1898   $self->{payment_terms} =~ s/<%skonto_date%>/$self->{skonto_date}/g;
 
1899   $self->{payment_terms} =~ s/<%currency%>/$self->{currency}/g;
 
1900   $self->{payment_terms} =~ s/<%terms_netto%>/$self->{terms_netto}/g;
 
1901   $self->{payment_terms} =~ s/<%account_number%>/$self->{account_number}/g;
 
1902   $self->{payment_terms} =~ s/<%bank%>/$self->{bank}/g;
 
1903   $self->{payment_terms} =~ s/<%bank_code%>/$self->{bank_code}/g;
 
1905   map { $self->{payment_terms} =~ s/<%${_}%>/$formatted_amounts{$_}/g; } keys %formatted_amounts;
 
1907   $self->{skonto_in_percent} = $formatted_amounts{skonto_in_percent};
 
1909   $main::lxdebug->leave_sub();
 
1913 sub get_template_language {
 
1914   $main::lxdebug->enter_sub();
 
1916   my ($self, $myconfig) = @_;
 
1918   my $template_code = "";
 
1920   if ($self->{language_id}) {
 
1921     my $dbh = $self->get_standard_dbh($myconfig);
 
1922     my $query = qq|SELECT template_code FROM language WHERE id = ?|;
 
1923     ($template_code) = selectrow_query($self, $dbh, $query, $self->{language_id});
 
1926   $main::lxdebug->leave_sub();
 
1928   return $template_code;
 
1931 sub get_printer_code {
 
1932   $main::lxdebug->enter_sub();
 
1934   my ($self, $myconfig) = @_;
 
1936   my $template_code = "";
 
1938   if ($self->{printer_id}) {
 
1939     my $dbh = $self->get_standard_dbh($myconfig);
 
1940     my $query = qq|SELECT template_code, printer_command FROM printers WHERE id = ?|;
 
1941     ($template_code, $self->{printer_command}) = selectrow_query($self, $dbh, $query, $self->{printer_id});
 
1944   $main::lxdebug->leave_sub();
 
1946   return $template_code;
 
1950   $main::lxdebug->enter_sub();
 
1952   my ($self, $myconfig) = @_;
 
1954   my $template_code = "";
 
1956   if ($self->{shipto_id}) {
 
1957     my $dbh = $self->get_standard_dbh($myconfig);
 
1958     my $query = qq|SELECT * FROM shipto WHERE shipto_id = ?|;
 
1959     my $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{shipto_id});
 
1960     map({ $self->{$_} = $ref->{$_} } keys(%$ref));
 
1963   $main::lxdebug->leave_sub();
 
1967   $main::lxdebug->enter_sub();
 
1969   my ($self, $dbh, $id, $module) = @_;
 
1974   foreach my $item (qw(name department_1 department_2 street zipcode city country
 
1975                        contact cp_gender phone fax email)) {
 
1976     if ($self->{"shipto$item"}) {
 
1977       $shipto = 1 if ($self->{$item} ne $self->{"shipto$item"});
 
1979     push(@values, $self->{"shipto${item}"});
 
1983     if ($self->{shipto_id}) {
 
1984       my $query = qq|UPDATE shipto set
 
1986                        shiptodepartment_1 = ?,
 
1987                        shiptodepartment_2 = ?,
 
1993                        shiptocp_gender = ?,
 
1997                      WHERE shipto_id = ?|;
 
1998       do_query($self, $dbh, $query, @values, $self->{shipto_id});
 
2000       my $query = qq|SELECT * FROM shipto
 
2001                      WHERE shiptoname = ? AND
 
2002                        shiptodepartment_1 = ? AND
 
2003                        shiptodepartment_2 = ? AND
 
2004                        shiptostreet = ? AND
 
2005                        shiptozipcode = ? AND
 
2007                        shiptocountry = ? AND
 
2008                        shiptocontact = ? AND
 
2009                        shiptocp_gender = ? AND
 
2015       my $insert_check = selectfirst_hashref_query($self, $dbh, $query, @values, $module, $id);
 
2018           qq|INSERT INTO shipto (trans_id, shiptoname, shiptodepartment_1, shiptodepartment_2,
 
2019                                  shiptostreet, shiptozipcode, shiptocity, shiptocountry,
 
2020                                  shiptocontact, shiptocp_gender, shiptophone, shiptofax, shiptoemail, module)
 
2021              VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|;
 
2022         do_query($self, $dbh, $query, $id, @values, $module);
 
2027   $main::lxdebug->leave_sub();
 
2031   $main::lxdebug->enter_sub();
 
2033   my ($self, $dbh) = @_;
 
2035   $dbh ||= $self->get_standard_dbh(\%main::myconfig);
 
2037   my $query = qq|SELECT id, name FROM employee WHERE login = ?|;
 
2038   ($self->{"employee_id"}, $self->{"employee"}) = selectrow_query($self, $dbh, $query, $self->{login});
 
2039   $self->{"employee_id"} *= 1;
 
2041   $main::lxdebug->leave_sub();
 
2044 sub get_employee_data {
 
2045   $main::lxdebug->enter_sub();
 
2050   Common::check_params(\%params, qw(prefix));
 
2051   Common::check_params_x(\%params, qw(id));
 
2054     $main::lxdebug->leave_sub();
 
2058   my $myconfig = \%main::myconfig;
 
2059   my $dbh      = $params{dbh} || $self->get_standard_dbh($myconfig);
 
2061   my ($login)  = selectrow_query($self, $dbh, qq|SELECT login FROM employee WHERE id = ?|, conv_i($params{id}));
 
2064     my $user = User->new($login);
 
2065     map { $self->{$params{prefix} . "_${_}"} = $user->{$_}; } qw(address businessnumber co_ustid company duns email fax name signature taxnumber tel);
 
2067     $self->{$params{prefix} . '_login'}   = $login;
 
2068     $self->{$params{prefix} . '_name'}  ||= $login;
 
2071   $main::lxdebug->leave_sub();
 
2075   $main::lxdebug->enter_sub();
 
2077   my ($self, $myconfig, $reference_date) = @_;
 
2079   $reference_date = $reference_date ? conv_dateq($reference_date) . '::DATE' : 'current_date';
 
2081   my $dbh         = $self->get_standard_dbh($myconfig);
 
2082   my $query       = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
 
2083   my ($duedate)   = selectrow_query($self, $dbh, $query, $self->{payment_id});
 
2085   $main::lxdebug->leave_sub();
 
2091   $main::lxdebug->enter_sub();
 
2093   my ($self, $dbh, $id, $key) = @_;
 
2095   $key = "all_contacts" unless ($key);
 
2099     $main::lxdebug->leave_sub();
 
2104     qq|SELECT cp_id, cp_cv_id, cp_name, cp_givenname, cp_abteilung | .
 
2105     qq|FROM contacts | .
 
2106     qq|WHERE cp_cv_id = ? | .
 
2107     qq|ORDER BY lower(cp_name)|;
 
2109   $self->{$key} = selectall_hashref_query($self, $dbh, $query, $id);
 
2111   $main::lxdebug->leave_sub();
 
2115   $main::lxdebug->enter_sub();
 
2117   my ($self, $dbh, $key) = @_;
 
2119   my ($all, $old_id, $where, @values);
 
2121   if (ref($key) eq "HASH") {
 
2124     $key = "ALL_PROJECTS";
 
2126     foreach my $p (keys(%{$params})) {
 
2128         $all = $params->{$p};
 
2129       } elsif ($p eq "old_id") {
 
2130         $old_id = $params->{$p};
 
2131       } elsif ($p eq "key") {
 
2132         $key = $params->{$p};
 
2138     $where = "WHERE active ";
 
2140       if (ref($old_id) eq "ARRAY") {
 
2141         my @ids = grep({ $_ } @{$old_id});
 
2143           $where .= " OR id IN (" . join(",", map({ "?" } @ids)) . ") ";
 
2144           push(@values, @ids);
 
2147         $where .= " OR (id = ?) ";
 
2148         push(@values, $old_id);
 
2154     qq|SELECT id, projectnumber, description, active | .
 
2157     qq|ORDER BY lower(projectnumber)|;
 
2159   $self->{$key} = selectall_hashref_query($self, $dbh, $query, @values);
 
2161   $main::lxdebug->leave_sub();
 
2165   $main::lxdebug->enter_sub();
 
2167   my ($self, $dbh, $vc_id, $key) = @_;
 
2169   $key = "all_shipto" unless ($key);
 
2172     # get shipping addresses
 
2173     my $query = qq|SELECT * FROM shipto WHERE trans_id = ?|;
 
2175     $self->{$key} = selectall_hashref_query($self, $dbh, $query, $vc_id);
 
2181   $main::lxdebug->leave_sub();
 
2185   $main::lxdebug->enter_sub();
 
2187   my ($self, $dbh, $key) = @_;
 
2189   $key = "all_printers" unless ($key);
 
2191   my $query = qq|SELECT id, printer_description, printer_command, template_code FROM printers|;
 
2193   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2195   $main::lxdebug->leave_sub();
 
2199   $main::lxdebug->enter_sub();
 
2201   my ($self, $dbh, $params) = @_;
 
2204   $key = $params->{key};
 
2205   $key = "all_charts" unless ($key);
 
2207   my $transdate = quote_db_date($params->{transdate});
 
2210     qq|SELECT c.id, c.accno, c.description, c.link, c.charttype, tk.taxkey_id, tk.tax_id | .
 
2212     qq|LEFT JOIN taxkeys tk ON | .
 
2213     qq|(tk.id = (SELECT id FROM taxkeys | .
 
2214     qq|          WHERE taxkeys.chart_id = c.id AND startdate <= $transdate | .
 
2215     qq|          ORDER BY startdate DESC LIMIT 1)) | .
 
2216     qq|ORDER BY c.accno|;
 
2218   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2220   $main::lxdebug->leave_sub();
 
2223 sub _get_taxcharts {
 
2224   $main::lxdebug->enter_sub();
 
2226   my ($self, $dbh, $params) = @_;
 
2228   my $key = "all_taxcharts";
 
2231   if (ref $params eq 'HASH') {
 
2232     $key = $params->{key} if ($params->{key});
 
2233     if ($params->{module} eq 'AR') {
 
2234       push @where, 'taxkey NOT IN (8, 9, 18, 19)';
 
2236     } elsif ($params->{module} eq 'AP') {
 
2237       push @where, 'taxkey NOT IN (1, 2, 3, 12, 13)';
 
2244   my $where = ' WHERE ' . join(' AND ', map { "($_)" } @where) if (@where);
 
2246   my $query = qq|SELECT * FROM tax $where ORDER BY taxkey|;
 
2248   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2250   $main::lxdebug->leave_sub();
 
2254   $main::lxdebug->enter_sub();
 
2256   my ($self, $dbh, $key) = @_;
 
2258   $key = "all_taxzones" unless ($key);
 
2260   my $query = qq|SELECT * FROM tax_zones ORDER BY id|;
 
2262   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2264   $main::lxdebug->leave_sub();
 
2267 sub _get_employees {
 
2268   $main::lxdebug->enter_sub();
 
2270   my ($self, $dbh, $default_key, $key) = @_;
 
2272   $key = $default_key unless ($key);
 
2273   $self->{$key} = selectall_hashref_query($self, $dbh, qq|SELECT * FROM employee ORDER BY lower(name)|);
 
2275   $main::lxdebug->leave_sub();
 
2278 sub _get_business_types {
 
2279   $main::lxdebug->enter_sub();
 
2281   my ($self, $dbh, $key) = @_;
 
2283   my $options       = ref $key eq 'HASH' ? $key : { key => $key };
 
2284   $options->{key} ||= "all_business_types";
 
2287   if (exists $options->{salesman}) {
 
2288     $where = 'WHERE ' . ($options->{salesman} ? '' : 'NOT ') . 'COALESCE(salesman)';
 
2291   $self->{ $options->{key} } = selectall_hashref_query($self, $dbh, qq|SELECT * FROM business $where ORDER BY lower(description)|);
 
2293   $main::lxdebug->leave_sub();
 
2296 sub _get_languages {
 
2297   $main::lxdebug->enter_sub();
 
2299   my ($self, $dbh, $key) = @_;
 
2301   $key = "all_languages" unless ($key);
 
2303   my $query = qq|SELECT * FROM language ORDER BY id|;
 
2305   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2307   $main::lxdebug->leave_sub();
 
2310 sub _get_dunning_configs {
 
2311   $main::lxdebug->enter_sub();
 
2313   my ($self, $dbh, $key) = @_;
 
2315   $key = "all_dunning_configs" unless ($key);
 
2317   my $query = qq|SELECT * FROM dunning_config ORDER BY dunning_level|;
 
2319   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2321   $main::lxdebug->leave_sub();
 
2324 sub _get_currencies {
 
2325 $main::lxdebug->enter_sub();
 
2327   my ($self, $dbh, $key) = @_;
 
2329   $key = "all_currencies" unless ($key);
 
2331   my $query = qq|SELECT curr AS currency FROM defaults|;
 
2333   $self->{$key} = [split(/\:/ , selectfirst_hashref_query($self, $dbh, $query)->{currency})];
 
2335   $main::lxdebug->leave_sub();
 
2339 $main::lxdebug->enter_sub();
 
2341   my ($self, $dbh, $key) = @_;
 
2343   $key = "all_payments" unless ($key);
 
2345   my $query = qq|SELECT * FROM payment_terms ORDER BY id|;
 
2347   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2349   $main::lxdebug->leave_sub();
 
2352 sub _get_customers {
 
2353   $main::lxdebug->enter_sub();
 
2355   my ($self, $dbh, $key) = @_;
 
2357   my $options        = ref $key eq 'HASH' ? $key : { key => $key };
 
2358   $options->{key}  ||= "all_customers";
 
2359   my $limit_clause   = "LIMIT $options->{limit}" if $options->{limit};
 
2362   push @where, qq|business_id IN (SELECT id FROM business WHERE salesman)| if  $options->{business_is_salesman};
 
2363   push @where, qq|NOT obsolete|                                            if !$options->{with_obsolete};
 
2364   my $where_str = @where ? "WHERE " . join(" AND ", map { "($_)" } @where) : '';
 
2366   my $query = qq|SELECT * FROM customer $where_str ORDER BY name $limit_clause|;
 
2367   $self->{ $options->{key} } = selectall_hashref_query($self, $dbh, $query);
 
2369   $main::lxdebug->leave_sub();
 
2373   $main::lxdebug->enter_sub();
 
2375   my ($self, $dbh, $key) = @_;
 
2377   $key = "all_vendors" unless ($key);
 
2379   my $query = qq|SELECT * FROM vendor WHERE NOT obsolete ORDER BY name|;
 
2381   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2383   $main::lxdebug->leave_sub();
 
2386 sub _get_departments {
 
2387   $main::lxdebug->enter_sub();
 
2389   my ($self, $dbh, $key) = @_;
 
2391   $key = "all_departments" unless ($key);
 
2393   my $query = qq|SELECT * FROM department ORDER BY description|;
 
2395   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2397   $main::lxdebug->leave_sub();
 
2400 sub _get_warehouses {
 
2401   $main::lxdebug->enter_sub();
 
2403   my ($self, $dbh, $param) = @_;
 
2405   my ($key, $bins_key);
 
2407   if ('' eq ref $param) {
 
2411     $key      = $param->{key};
 
2412     $bins_key = $param->{bins};
 
2415   my $query = qq|SELECT w.* FROM warehouse w
 
2416                  WHERE (NOT w.invalid) AND
 
2417                    ((SELECT COUNT(b.*) FROM bin b WHERE b.warehouse_id = w.id) > 0)
 
2418                  ORDER BY w.sortkey|;
 
2420   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2423     $query = qq|SELECT id, description FROM bin WHERE warehouse_id = ?|;
 
2424     my $sth = prepare_query($self, $dbh, $query);
 
2426     foreach my $warehouse (@{ $self->{$key} }) {
 
2427       do_statement($self, $sth, $query, $warehouse->{id});
 
2428       $warehouse->{$bins_key} = [];
 
2430       while (my $ref = $sth->fetchrow_hashref()) {
 
2431         push @{ $warehouse->{$bins_key} }, $ref;
 
2437   $main::lxdebug->leave_sub();
 
2441   $main::lxdebug->enter_sub();
 
2443   my ($self, $dbh, $table, $key, $sortkey) = @_;
 
2445   my $query  = qq|SELECT * FROM $table|;
 
2446   $query    .= qq| ORDER BY $sortkey| if ($sortkey);
 
2448   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2450   $main::lxdebug->leave_sub();
 
2454 #  $main::lxdebug->enter_sub();
 
2456 #  my ($self, $dbh, $key) = @_;
 
2458 #  $key ||= "all_groups";
 
2460 #  my $groups = $main::auth->read_groups();
 
2462 #  $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
2464 #  $main::lxdebug->leave_sub();
 
2468   $main::lxdebug->enter_sub();
 
2473   my $dbh = $self->get_standard_dbh(\%main::myconfig);
 
2474   my ($sth, $query, $ref);
 
2476   my $vc = $self->{"vc"} eq "customer" ? "customer" : "vendor";
 
2477   my $vc_id = $self->{"${vc}_id"};
 
2479   if ($params{"contacts"}) {
 
2480     $self->_get_contacts($dbh, $vc_id, $params{"contacts"});
 
2483   if ($params{"shipto"}) {
 
2484     $self->_get_shipto($dbh, $vc_id, $params{"shipto"});
 
2487   if ($params{"projects"} || $params{"all_projects"}) {
 
2488     $self->_get_projects($dbh, $params{"all_projects"} ?
 
2489                          $params{"all_projects"} : $params{"projects"},
 
2490                          $params{"all_projects"} ? 1 : 0);
 
2493   if ($params{"printers"}) {
 
2494     $self->_get_printers($dbh, $params{"printers"});
 
2497   if ($params{"languages"}) {
 
2498     $self->_get_languages($dbh, $params{"languages"});
 
2501   if ($params{"charts"}) {
 
2502     $self->_get_charts($dbh, $params{"charts"});
 
2505   if ($params{"taxcharts"}) {
 
2506     $self->_get_taxcharts($dbh, $params{"taxcharts"});
 
2509   if ($params{"taxzones"}) {
 
2510     $self->_get_taxzones($dbh, $params{"taxzones"});
 
2513   if ($params{"employees"}) {
 
2514     $self->_get_employees($dbh, "all_employees", $params{"employees"});
 
2517   if ($params{"salesmen"}) {
 
2518     $self->_get_employees($dbh, "all_salesmen", $params{"salesmen"});
 
2521   if ($params{"business_types"}) {
 
2522     $self->_get_business_types($dbh, $params{"business_types"});
 
2525   if ($params{"dunning_configs"}) {
 
2526     $self->_get_dunning_configs($dbh, $params{"dunning_configs"});
 
2529   if($params{"currencies"}) {
 
2530     $self->_get_currencies($dbh, $params{"currencies"});
 
2533   if($params{"customers"}) {
 
2534     $self->_get_customers($dbh, $params{"customers"});
 
2537   if($params{"vendors"}) {
 
2538     if (ref $params{"vendors"} eq 'HASH') {
 
2539       $self->_get_vendors($dbh, $params{"vendors"}{key}, $params{"vendors"}{limit});
 
2541       $self->_get_vendors($dbh, $params{"vendors"});
 
2545   if($params{"payments"}) {
 
2546     $self->_get_payments($dbh, $params{"payments"});
 
2549   if($params{"departments"}) {
 
2550     $self->_get_departments($dbh, $params{"departments"});
 
2553   if ($params{price_factors}) {
 
2554     $self->_get_simple($dbh, 'price_factors', $params{price_factors}, 'sortkey');
 
2557   if ($params{warehouses}) {
 
2558     $self->_get_warehouses($dbh, $params{warehouses});
 
2561 #  if ($params{groups}) {
 
2562 #    $self->_get_groups($dbh, $params{groups});
 
2565   if ($params{partsgroup}) {
 
2566     $self->get_partsgroup(\%main::myconfig, { all => 1, target => $params{partsgroup} });
 
2569   $main::lxdebug->leave_sub();
 
2572 # this sub gets the id and name from $table
 
2574   $main::lxdebug->enter_sub();
 
2576   my ($self, $myconfig, $table) = @_;
 
2578   # connect to database
 
2579   my $dbh = $self->get_standard_dbh($myconfig);
 
2581   $table = $table eq "customer" ? "customer" : "vendor";
 
2582   my $arap = $self->{arap} eq "ar" ? "ar" : "ap";
 
2584   my ($query, @values);
 
2586   if (!$self->{openinvoices}) {
 
2588     if ($self->{customernumber} ne "") {
 
2589       $where = qq|(vc.customernumber ILIKE ?)|;
 
2590       push(@values, '%' . $self->{customernumber} . '%');
 
2592       $where = qq|(vc.name ILIKE ?)|;
 
2593       push(@values, '%' . $self->{$table} . '%');
 
2597       qq~SELECT vc.id, vc.name,
 
2598            vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
 
2600          WHERE $where AND (NOT vc.obsolete)
 
2604       qq~SELECT DISTINCT vc.id, vc.name,
 
2605            vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
 
2607          JOIN $table vc ON (a.${table}_id = vc.id)
 
2608          WHERE NOT (a.amount = a.paid) AND (vc.name ILIKE ?)
 
2610     push(@values, '%' . $self->{$table} . '%');
 
2613   $self->{name_list} = selectall_hashref_query($self, $dbh, $query, @values);
 
2615   $main::lxdebug->leave_sub();
 
2617   return scalar(@{ $self->{name_list} });
 
2620 # the selection sub is used in the AR, AP, IS, IR and OE module
 
2623   $main::lxdebug->enter_sub();
 
2625   my ($self, $myconfig, $table, $module) = @_;
 
2628   my $dbh = $self->get_standard_dbh;
 
2630   $table = $table eq "customer" ? "customer" : "vendor";
 
2632   my $query = qq|SELECT count(*) FROM $table|;
 
2633   my ($count) = selectrow_query($self, $dbh, $query);
 
2635   # build selection list
 
2636   if ($count <= $myconfig->{vclimit}) {
 
2637     $query = qq|SELECT id, name, salesman_id
 
2638                 FROM $table WHERE NOT obsolete
 
2640     $self->{"all_$table"} = selectall_hashref_query($self, $dbh, $query);
 
2644   $self->get_employee($dbh);
 
2646   # setup sales contacts
 
2647   $query = qq|SELECT e.id, e.name
 
2649               WHERE (e.sales = '1') AND (NOT e.id = ?)|;
 
2650   $self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
 
2653   push(@{ $self->{all_employees} },
 
2654        { id   => $self->{employee_id},
 
2655          name => $self->{employee} });
 
2657   # sort the whole thing
 
2658   @{ $self->{all_employees} } =
 
2659     sort { $a->{name} cmp $b->{name} } @{ $self->{all_employees} };
 
2661   if ($module eq 'AR') {
 
2663     # prepare query for departments
 
2664     $query = qq|SELECT id, description
 
2667                 ORDER BY description|;
 
2670     $query = qq|SELECT id, description
 
2672                 ORDER BY description|;
 
2675   $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
 
2678   $query = qq|SELECT id, description
 
2682   $self->{languages} = selectall_hashref_query($self, $dbh, $query);
 
2685   $query = qq|SELECT printer_description, id
 
2687               ORDER BY printer_description|;
 
2689   $self->{printers} = selectall_hashref_query($self, $dbh, $query);
 
2692   $query = qq|SELECT id, description
 
2696   $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
 
2698   $main::lxdebug->leave_sub();
 
2701 sub language_payment {
 
2702   $main::lxdebug->enter_sub();
 
2704   my ($self, $myconfig) = @_;
 
2706   my $dbh = $self->get_standard_dbh($myconfig);
 
2708   my $query = qq|SELECT id, description
 
2712   $self->{languages} = selectall_hashref_query($self, $dbh, $query);
 
2715   $query = qq|SELECT printer_description, id
 
2717               ORDER BY printer_description|;
 
2719   $self->{printers} = selectall_hashref_query($self, $dbh, $query);
 
2722   $query = qq|SELECT id, description
 
2726   $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
 
2728   # get buchungsgruppen
 
2729   $query = qq|SELECT id, description
 
2730               FROM buchungsgruppen|;
 
2732   $self->{BUCHUNGSGRUPPEN} = selectall_hashref_query($self, $dbh, $query);
 
2734   $main::lxdebug->leave_sub();
 
2737 # this is only used for reports
 
2738 sub all_departments {
 
2739   $main::lxdebug->enter_sub();
 
2741   my ($self, $myconfig, $table) = @_;
 
2743   my $dbh = $self->get_standard_dbh($myconfig);
 
2746   if ($table eq 'customer') {
 
2747     $where = "WHERE role = 'P' ";
 
2750   my $query = qq|SELECT id, description
 
2753                  ORDER BY description|;
 
2754   $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
 
2756   delete($self->{all_departments}) unless (@{ $self->{all_departments} || [] });
 
2758   $main::lxdebug->leave_sub();
 
2762   $main::lxdebug->enter_sub();
 
2764   my ($self, $module, $myconfig, $table, $provided_dbh) = @_;
 
2767   if ($table eq "customer") {
 
2776   $self->all_vc($myconfig, $table, $module);
 
2778   # get last customers or vendors
 
2779   my ($query, $sth, $ref);
 
2781   my $dbh = $provided_dbh ? $provided_dbh : $self->get_standard_dbh($myconfig);
 
2786     my $transdate = "current_date";
 
2787     if ($self->{transdate}) {
 
2788       $transdate = $dbh->quote($self->{transdate});
 
2791     # now get the account numbers
 
2792     $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
 
2793                 FROM chart c, taxkeys tk
 
2794                 WHERE (c.link LIKE ?) AND (c.id = tk.chart_id) AND tk.id =
 
2795                   (SELECT id FROM taxkeys WHERE (taxkeys.chart_id = c.id) AND (startdate <= $transdate) ORDER BY startdate DESC LIMIT 1)
 
2798     $sth = $dbh->prepare($query);
 
2800     do_statement($self, $sth, $query, '%' . $module . '%');
 
2802     $self->{accounts} = "";
 
2803     while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
2805       foreach my $key (split(/:/, $ref->{link})) {
 
2806         if ($key =~ /\Q$module\E/) {
 
2808           # cross reference for keys
 
2809           $xkeyref{ $ref->{accno} } = $key;
 
2811           push @{ $self->{"${module}_links"}{$key} },
 
2812             { accno       => $ref->{accno},
 
2813               description => $ref->{description},
 
2814               taxkey      => $ref->{taxkey_id},
 
2815               tax_id      => $ref->{tax_id} };
 
2817           $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
 
2823   # get taxkeys and description
 
2824   $query = qq|SELECT id, taxkey, taxdescription FROM tax|;
 
2825   $self->{TAXKEY} = selectall_hashref_query($self, $dbh, $query);
 
2827   if (($module eq "AP") || ($module eq "AR")) {
 
2828     # get tax rates and description
 
2829     $query = qq|SELECT * FROM tax|;
 
2830     $self->{TAX} = selectall_hashref_query($self, $dbh, $query);
 
2836            a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid,
 
2837            a.duedate, a.ordnumber, a.taxincluded, a.curr AS currency, a.notes,
 
2838            a.intnotes, a.department_id, a.amount AS oldinvtotal,
 
2839            a.paid AS oldtotalpaid, a.employee_id, a.gldate, a.type,
 
2841            d.description AS department,
 
2844          JOIN $table c ON (a.${table}_id = c.id)
 
2845          LEFT JOIN employee e ON (e.id = a.employee_id)
 
2846          LEFT JOIN department d ON (d.id = a.department_id)
 
2848     $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{id});
 
2850     foreach my $key (keys %$ref) {
 
2851       $self->{$key} = $ref->{$key};
 
2854     my $transdate = "current_date";
 
2855     if ($self->{transdate}) {
 
2856       $transdate = $dbh->quote($self->{transdate});
 
2859     # now get the account numbers
 
2860     $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
 
2862                 LEFT JOIN taxkeys tk ON (tk.chart_id = c.id)
 
2864                   AND (tk.id = (SELECT id FROM taxkeys WHERE taxkeys.chart_id = c.id AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1)
 
2865                     OR c.link LIKE '%_tax%' OR c.taxkey_id IS NULL)
 
2868     $sth = $dbh->prepare($query);
 
2869     do_statement($self, $sth, $query, "%$module%");
 
2871     $self->{accounts} = "";
 
2872     while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
2874       foreach my $key (split(/:/, $ref->{link})) {
 
2875         if ($key =~ /\Q$module\E/) {
 
2877           # cross reference for keys
 
2878           $xkeyref{ $ref->{accno} } = $key;
 
2880           push @{ $self->{"${module}_links"}{$key} },
 
2881             { accno       => $ref->{accno},
 
2882               description => $ref->{description},
 
2883               taxkey      => $ref->{taxkey_id},
 
2884               tax_id      => $ref->{tax_id} };
 
2886           $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
 
2892     # get amounts from individual entries
 
2895            c.accno, c.description,
 
2896            a.source, a.amount, a.memo, a.transdate, a.cleared, a.project_id, a.taxkey,
 
2900          LEFT JOIN chart c ON (c.id = a.chart_id)
 
2901          LEFT JOIN project p ON (p.id = a.project_id)
 
2902          LEFT JOIN tax t ON (t.id= (SELECT tk.tax_id FROM taxkeys tk
 
2903                                     WHERE (tk.taxkey_id=a.taxkey) AND
 
2904                                       ((CASE WHEN a.chart_id IN (SELECT chart_id FROM taxkeys WHERE taxkey_id = a.taxkey)
 
2905                                         THEN tk.chart_id = a.chart_id
 
2908                                        OR (c.link='%tax%')) AND
 
2909                                       (startdate <= a.transdate) ORDER BY startdate DESC LIMIT 1))
 
2910          WHERE a.trans_id = ?
 
2911          AND a.fx_transaction = '0'
 
2912          ORDER BY a.acc_trans_id, a.transdate|;
 
2913     $sth = $dbh->prepare($query);
 
2914     do_statement($self, $sth, $query, $self->{id});
 
2916     # get exchangerate for currency
 
2917     $self->{exchangerate} =
 
2918       $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
 
2921     # store amounts in {acc_trans}{$key} for multiple accounts
 
2922     while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
 
2923       $ref->{exchangerate} =
 
2924         $self->get_exchangerate($dbh, $self->{currency}, $ref->{transdate}, $fld);
 
2925       if (!($xkeyref{ $ref->{accno} } =~ /tax/)) {
 
2928       if (($xkeyref{ $ref->{accno} } =~ /paid/) && ($self->{type} eq "credit_note")) {
 
2929         $ref->{amount} *= -1;
 
2931       $ref->{index} = $index;
 
2933       push @{ $self->{acc_trans}{ $xkeyref{ $ref->{accno} } } }, $ref;
 
2939            d.curr AS currencies, d.closedto, d.revtrans,
 
2940            (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
 
2941            (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
 
2943     $ref = selectfirst_hashref_query($self, $dbh, $query);
 
2944     map { $self->{$_} = $ref->{$_} } keys %$ref;
 
2951             current_date AS transdate, d.curr AS currencies, d.closedto, d.revtrans,
 
2952             (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
 
2953             (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
 
2955     $ref = selectfirst_hashref_query($self, $dbh, $query);
 
2956     map { $self->{$_} = $ref->{$_} } keys %$ref;
 
2958     if ($self->{"$self->{vc}_id"}) {
 
2960       # only setup currency
 
2961       ($self->{currency}) = split(/:/, $self->{currencies});
 
2965       $self->lastname_used($dbh, $myconfig, $table, $module);
 
2967       # get exchangerate for currency
 
2968       $self->{exchangerate} =
 
2969         $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
 
2975   $main::lxdebug->leave_sub();
 
2979   $main::lxdebug->enter_sub();
 
2981   my ($self, $dbh, $myconfig, $table, $module) = @_;
 
2985   $table         = $table eq "customer" ? "customer" : "vendor";
 
2986   my %column_map = ("a.curr"                  => "currency",
 
2987                     "a.${table}_id"           => "${table}_id",
 
2988                     "a.department_id"         => "department_id",
 
2989                     "d.description"           => "department",
 
2990                     "ct.name"                 => $table,
 
2991                     "current_date + ct.terms" => "duedate",
 
2994   if ($self->{type} =~ /delivery_order/) {
 
2995     $arap  = 'delivery_orders';
 
2996     delete $column_map{"a.curr"};
 
2998   } elsif ($self->{type} =~ /_order/) {
 
3000     $where = "quotation = '0'";
 
3002   } elsif ($self->{type} =~ /_quotation/) {
 
3004     $where = "quotation = '1'";
 
3006   } elsif ($table eq 'customer') {
 
3014   $where           = "($where) AND" if ($where);
 
3015   my $query        = qq|SELECT MAX(id) FROM $arap
 
3016                         WHERE $where ${table}_id > 0|;
 
3017   my ($trans_id)   = selectrow_query($self, $dbh, $query);
 
3020   my $column_spec  = join(', ', map { "${_} AS $column_map{$_}" } keys %column_map);
 
3021   $query           = qq|SELECT $column_spec
 
3023                         LEFT JOIN $table     ct ON (a.${table}_id = ct.id)
 
3024                         LEFT JOIN department d  ON (a.department_id = d.id)
 
3026   my $ref          = selectfirst_hashref_query($self, $dbh, $query, $trans_id);
 
3028   map { $self->{$_} = $ref->{$_} } values %column_map;
 
3030   $main::lxdebug->leave_sub();
 
3034   $main::lxdebug->enter_sub();
 
3037   my $myconfig = shift || \%::myconfig;
 
3038   my ($thisdate, $days) = @_;
 
3040   my $dbh = $self->get_standard_dbh($myconfig);
 
3045     my $dateformat = $myconfig->{dateformat};
 
3046     $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
 
3047     $thisdate = $dbh->quote($thisdate);
 
3048     $query = qq|SELECT to_date($thisdate, '$dateformat') + $days AS thisdate|;
 
3050     $query = qq|SELECT current_date AS thisdate|;
 
3053   ($thisdate) = selectrow_query($self, $dbh, $query);
 
3055   $main::lxdebug->leave_sub();
 
3061   $main::lxdebug->enter_sub();
 
3063   my ($self, $string) = @_;
 
3065   if ($string !~ /%/) {
 
3066     $string = "%$string%";
 
3069   $string =~ s/\'/\'\'/g;
 
3071   $main::lxdebug->leave_sub();
 
3077   $main::lxdebug->enter_sub();
 
3079   my ($self, $flds, $new, $count, $numrows) = @_;
 
3083   map { push @ndx, { num => $new->[$_ - 1]->{runningnumber}, ndx => $_ } } 1 .. $count;
 
3088   foreach my $item (sort { $a->{num} <=> $b->{num} } @ndx) {
 
3090     my $j = $item->{ndx} - 1;
 
3091     map { $self->{"${_}_$i"} = $new->[$j]->{$_} } @{$flds};
 
3095   for $i ($count + 1 .. $numrows) {
 
3096     map { delete $self->{"${_}_$i"} } @{$flds};
 
3099   $main::lxdebug->leave_sub();
 
3103   $main::lxdebug->enter_sub();
 
3105   my ($self, $myconfig) = @_;
 
3109   my $dbh = $self->dbconnect_noauto($myconfig);
 
3111   my $query = qq|DELETE FROM status
 
3112                  WHERE (formname = ?) AND (trans_id = ?)|;
 
3113   my $sth = prepare_query($self, $dbh, $query);
 
3115   if ($self->{formname} =~ /(check|receipt)/) {
 
3116     for $i (1 .. $self->{rowcount}) {
 
3117       do_statement($self, $sth, $query, $self->{formname}, $self->{"id_$i"} * 1);
 
3120     do_statement($self, $sth, $query, $self->{formname}, $self->{id});
 
3124   my $printed = ($self->{printed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
 
3125   my $emailed = ($self->{emailed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
 
3127   my %queued = split / /, $self->{queued};
 
3130   if ($self->{formname} =~ /(check|receipt)/) {
 
3132     # this is a check or receipt, add one entry for each lineitem
 
3133     my ($accno) = split /--/, $self->{account};
 
3134     $query = qq|INSERT INTO status (trans_id, printed, spoolfile, formname, chart_id)
 
3135                 VALUES (?, ?, ?, ?, (SELECT c.id FROM chart c WHERE c.accno = ?))|;
 
3136     @values = ($printed, $queued{$self->{formname}}, $self->{prinform}, $accno);
 
3137     $sth = prepare_query($self, $dbh, $query);
 
3139     for $i (1 .. $self->{rowcount}) {
 
3140       if ($self->{"checked_$i"}) {
 
3141         do_statement($self, $sth, $query, $self->{"id_$i"}, @values);
 
3147     $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
 
3148                 VALUES (?, ?, ?, ?, ?)|;
 
3149     do_query($self, $dbh, $query, $self->{id}, $printed, $emailed,
 
3150              $queued{$self->{formname}}, $self->{formname});
 
3156   $main::lxdebug->leave_sub();
 
3160   $main::lxdebug->enter_sub();
 
3162   my ($self, $dbh) = @_;
 
3164   my ($query, $printed, $emailed);
 
3166   my $formnames  = $self->{printed};
 
3167   my $emailforms = $self->{emailed};
 
3169   $query = qq|DELETE FROM status
 
3170                  WHERE (formname = ?) AND (trans_id = ?)|;
 
3171   do_query($self, $dbh, $query, $self->{formname}, $self->{id});
 
3173   # this only applies to the forms
 
3174   # checks and receipts are posted when printed or queued
 
3176   if ($self->{queued}) {
 
3177     my %queued = split / /, $self->{queued};
 
3179     foreach my $formname (keys %queued) {
 
3180       $printed = ($self->{printed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
 
3181       $emailed = ($self->{emailed} =~ /\Q$self->{formname}\E/) ? "1" : "0";
 
3183       $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
 
3184                   VALUES (?, ?, ?, ?, ?)|;
 
3185       do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $queued{$formname}, $formname);
 
3187       $formnames  =~ s/\Q$self->{formname}\E//;
 
3188       $emailforms =~ s/\Q$self->{formname}\E//;
 
3193   # save printed, emailed info
 
3194   $formnames  =~ s/^ +//g;
 
3195   $emailforms =~ s/^ +//g;
 
3198   map { $status{$_}{printed} = 1 } split / +/, $formnames;
 
3199   map { $status{$_}{emailed} = 1 } split / +/, $emailforms;
 
3201   foreach my $formname (keys %status) {
 
3202     $printed = ($formnames  =~ /\Q$self->{formname}\E/) ? "1" : "0";
 
3203     $emailed = ($emailforms =~ /\Q$self->{formname}\E/) ? "1" : "0";
 
3205     $query = qq|INSERT INTO status (trans_id, printed, emailed, formname)
 
3206                 VALUES (?, ?, ?, ?)|;
 
3207     do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $formname);
 
3210   $main::lxdebug->leave_sub();
 
3214 # $main::locale->text('SAVED')
 
3215 # $main::locale->text('DELETED')
 
3216 # $main::locale->text('ADDED')
 
3217 # $main::locale->text('PAYMENT POSTED')
 
3218 # $main::locale->text('POSTED')
 
3219 # $main::locale->text('POSTED AS NEW')
 
3220 # $main::locale->text('ELSE')
 
3221 # $main::locale->text('SAVED FOR DUNNING')
 
3222 # $main::locale->text('DUNNING STARTED')
 
3223 # $main::locale->text('PRINTED')
 
3224 # $main::locale->text('MAILED')
 
3225 # $main::locale->text('SCREENED')
 
3226 # $main::locale->text('CANCELED')
 
3227 # $main::locale->text('invoice')
 
3228 # $main::locale->text('proforma')
 
3229 # $main::locale->text('sales_order')
 
3230 # $main::locale->text('pick_list')
 
3231 # $main::locale->text('purchase_order')
 
3232 # $main::locale->text('bin_list')
 
3233 # $main::locale->text('sales_quotation')
 
3234 # $main::locale->text('request_quotation')
 
3237   $main::lxdebug->enter_sub();
 
3240   my $dbh  = shift || $self->get_standard_dbh;
 
3242   if(!exists $self->{employee_id}) {
 
3243     &get_employee($self, $dbh);
 
3247    qq|INSERT INTO history_erp (trans_id, employee_id, addition, what_done, snumbers) | .
 
3248    qq|VALUES (?, (SELECT id FROM employee WHERE login = ?), ?, ?, ?)|;
 
3249   my @values = (conv_i($self->{id}), $self->{login},
 
3250                 $self->{addition}, $self->{what_done}, "$self->{snumbers}");
 
3251   do_query($self, $dbh, $query, @values);
 
3255   $main::lxdebug->leave_sub();
 
3259   $main::lxdebug->enter_sub();
 
3261   my ($self, $dbh, $trans_id, $restriction, $order) = @_;
 
3262   my ($orderBy, $desc) = split(/\-\-/, $order);
 
3263   $order = " ORDER BY " . ($order eq "" ? " h.itime " : ($desc == 1 ? $orderBy . " DESC " : $orderBy . " "));
 
3266   if ($trans_id ne "") {
 
3268       qq|SELECT h.employee_id, h.itime::timestamp(0) AS itime, h.addition, h.what_done, emp.name, h.snumbers, h.trans_id AS id | .
 
3269       qq|FROM history_erp h | .
 
3270       qq|LEFT JOIN employee emp ON (emp.id = h.employee_id) | .
 
3271       qq|WHERE (trans_id = | . $trans_id . qq|) $restriction | .
 
3274     my $sth = $dbh->prepare($query) || $self->dberror($query);
 
3276     $sth->execute() || $self->dberror("$query");
 
3278     while(my $hash_ref = $sth->fetchrow_hashref()) {
 
3279       $hash_ref->{addition} = $main::locale->text($hash_ref->{addition});
 
3280       $hash_ref->{what_done} = $main::locale->text($hash_ref->{what_done});
 
3281       $hash_ref->{snumbers} =~ s/^.+_(.*)$/$1/g;
 
3282       $tempArray[$i++] = $hash_ref;
 
3284     $main::lxdebug->leave_sub() and return \@tempArray
 
3285       if ($i > 0 && $tempArray[0] ne "");
 
3287   $main::lxdebug->leave_sub();
 
3291 sub update_defaults {
 
3292   $main::lxdebug->enter_sub();
 
3294   my ($self, $myconfig, $fld, $provided_dbh) = @_;
 
3297   if ($provided_dbh) {
 
3298     $dbh = $provided_dbh;
 
3300     $dbh = $self->dbconnect_noauto($myconfig);
 
3302   my $query = qq|SELECT $fld FROM defaults FOR UPDATE|;
 
3303   my $sth   = $dbh->prepare($query);
 
3305   $sth->execute || $self->dberror($query);
 
3306   my ($var) = $sth->fetchrow_array;
 
3309   if ($var =~ m/\d+$/) {
 
3310     my $new_var  = (substr $var, $-[0]) * 1 + 1;
 
3311     my $len_diff = length($var) - $-[0] - length($new_var);
 
3312     $var         = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
 
3318   $query = qq|UPDATE defaults SET $fld = ?|;
 
3319   do_query($self, $dbh, $query, $var);
 
3321   if (!$provided_dbh) {
 
3326   $main::lxdebug->leave_sub();
 
3331 sub update_business {
 
3332   $main::lxdebug->enter_sub();
 
3334   my ($self, $myconfig, $business_id, $provided_dbh) = @_;
 
3337   if ($provided_dbh) {
 
3338     $dbh = $provided_dbh;
 
3340     $dbh = $self->dbconnect_noauto($myconfig);
 
3343     qq|SELECT customernumberinit FROM business
 
3344        WHERE id = ? FOR UPDATE|;
 
3345   my ($var) = selectrow_query($self, $dbh, $query, $business_id);
 
3347   return undef unless $var;
 
3349   if ($var =~ m/\d+$/) {
 
3350     my $new_var  = (substr $var, $-[0]) * 1 + 1;
 
3351     my $len_diff = length($var) - $-[0] - length($new_var);
 
3352     $var         = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
 
3358   $query = qq|UPDATE business
 
3359               SET customernumberinit = ?
 
3361   do_query($self, $dbh, $query, $var, $business_id);
 
3363   if (!$provided_dbh) {
 
3368   $main::lxdebug->leave_sub();
 
3373 sub get_partsgroup {
 
3374   $main::lxdebug->enter_sub();
 
3376   my ($self, $myconfig, $p) = @_;
 
3377   my $target = $p->{target} || 'all_partsgroup';
 
3379   my $dbh = $self->get_standard_dbh($myconfig);
 
3381   my $query = qq|SELECT DISTINCT pg.id, pg.partsgroup
 
3383                  JOIN parts p ON (p.partsgroup_id = pg.id) |;
 
3386   if ($p->{searchitems} eq 'part') {
 
3387     $query .= qq|WHERE p.inventory_accno_id > 0|;
 
3389   if ($p->{searchitems} eq 'service') {
 
3390     $query .= qq|WHERE p.inventory_accno_id IS NULL|;
 
3392   if ($p->{searchitems} eq 'assembly') {
 
3393     $query .= qq|WHERE p.assembly = '1'|;
 
3395   if ($p->{searchitems} eq 'labor') {
 
3396     $query .= qq|WHERE (p.inventory_accno_id > 0) AND (p.income_accno_id IS NULL)|;
 
3399   $query .= qq|ORDER BY partsgroup|;
 
3402     $query = qq|SELECT id, partsgroup FROM partsgroup
 
3403                 ORDER BY partsgroup|;
 
3406   if ($p->{language_code}) {
 
3407     $query = qq|SELECT DISTINCT pg.id, pg.partsgroup,
 
3408                   t.description AS translation
 
3410                 JOIN parts p ON (p.partsgroup_id = pg.id)
 
3411                 LEFT JOIN translation t ON ((t.trans_id = pg.id) AND (t.language_code = ?))
 
3412                 ORDER BY translation|;
 
3413     @values = ($p->{language_code});
 
3416   $self->{$target} = selectall_hashref_query($self, $dbh, $query, @values);
 
3418   $main::lxdebug->leave_sub();
 
3421 sub get_pricegroup {
 
3422   $main::lxdebug->enter_sub();
 
3424   my ($self, $myconfig, $p) = @_;
 
3426   my $dbh = $self->get_standard_dbh($myconfig);
 
3428   my $query = qq|SELECT p.id, p.pricegroup
 
3431   $query .= qq| ORDER BY pricegroup|;
 
3434     $query = qq|SELECT id, pricegroup FROM pricegroup
 
3435                 ORDER BY pricegroup|;
 
3438   $self->{all_pricegroup} = selectall_hashref_query($self, $dbh, $query);
 
3440   $main::lxdebug->leave_sub();
 
3444 # usage $form->all_years($myconfig, [$dbh])
 
3445 # return list of all years where bookings found
 
3448   $main::lxdebug->enter_sub();
 
3450   my ($self, $myconfig, $dbh) = @_;
 
3452   $dbh ||= $self->get_standard_dbh($myconfig);
 
3455   my $query = qq|SELECT (SELECT MIN(transdate) FROM acc_trans),
 
3456                    (SELECT MAX(transdate) FROM acc_trans)|;
 
3457   my ($startdate, $enddate) = selectrow_query($self, $dbh, $query);
 
3459   if ($myconfig->{dateformat} =~ /^yy/) {
 
3460     ($startdate) = split /\W/, $startdate;
 
3461     ($enddate) = split /\W/, $enddate;
 
3463     (@_) = split /\W/, $startdate;
 
3465     (@_) = split /\W/, $enddate;
 
3470   $startdate = substr($startdate,0,4);
 
3471   $enddate = substr($enddate,0,4);
 
3473   while ($enddate >= $startdate) {
 
3474     push @all_years, $enddate--;
 
3479   $main::lxdebug->leave_sub();
 
3483   $main::lxdebug->enter_sub();
 
3487   map { $self->{_VAR_BACKUP}->{$_} = $self->{$_} if exists $self->{$_} } @vars;
 
3489   $main::lxdebug->leave_sub();
 
3493   $main::lxdebug->enter_sub();
 
3498   map { $self->{$_} = $self->{_VAR_BACKUP}->{$_} if exists $self->{_VAR_BACKUP}->{$_} } @vars;
 
3500   $main::lxdebug->leave_sub();
 
3509 SL::Form.pm - main data object.
 
3513 This is the main data object of Lx-Office.
 
3514 Unfortunately it also acts as a god object for certain data retrieval procedures used in the entry points.
 
3515 Points of interest for a beginner are:
 
3517  - $form->error            - renders a generic error in html. accepts an error message
 
3518  - $form->get_standard_dbh - returns a database connection for the
 
3520 =head1 SPECIAL FUNCTIONS
 
3522 =head2 C<_store_value()>
 
3524 parses a complex var name, and stores it in the form.
 
3527   $form->_store_value($key, $value);
 
3529 keys must start with a string, and can contain various tokens.
 
3530 supported key structures are:
 
3533   simple key strings work as expected
 
3538   separating two keys by a dot (.) will result in a hash lookup for the inner value
 
3539   this is similar to the behaviour of java and templating mechanisms.
 
3541   filter.description => $form->{filter}->{description}
 
3543 3. array+hashref access
 
3545   adding brackets ([]) before the dot will cause the next hash to be put into an array.
 
3546   using [+] instead of [] will force a new array index. this is useful for recurring
 
3547   data structures like part lists. put a [+] into the first varname, and use [] on the
 
3550   repeating these names in your template:
 
3553     invoice.items[].parts_id
 
3557     $form->{invoice}->{items}->[
 
3571   using brackets at the end of a name will result in a pure array to be created.
 
3572   note that you mustn't use [+], which is reserved for array+hash access and will
 
3573   result in undefined behaviour in array context.
 
3575   filter.status[]  => $form->{status}->[ val1, val2, ... ]
 
3577 =head2 C<update_business> PARAMS
 
3580  \%config,     - config hashref
 
3581  $business_id, - business id
 
3582  $dbh          - optional database handle
 
3584 handles business (thats customer/vendor types) sequences.
 
3586 special behaviour for empty strings in customerinitnumber field:
 
3587 will in this case not increase the value, and return undef.
 
3589 =head2 C<redirect_header> $url
 
3591 Generates a HTTP redirection header for the new C<$url>. Constructs an
 
3592 absolute URL including scheme, host name and port. If C<$url> is a
 
3593 relative URL then it is considered relative to Lx-Office base URL.
 
3595 This function C<die>s if headers have already been created with
 
3596 C<$::form-E<gt>header>.
 
3600   print $::form->redirect_header('oe.pl?action=edit&id=1234');
 
3601   print $::form->redirect_header('http://www.lx-office.org/');
 
3605 Generates a general purpose http/html header and includes most of the scripts
 
3606 ans stylesheets needed.
 
3608 Only one header will be generated. If the method was already called in this
 
3609 request it will not output anything and return undef. Also if no
 
3610 HTTP_USER_AGENT is found, no header is generated.
 
3612 Although header does not accept parameters itself, it will honor special
 
3613 hashkeys of its Form instance:
 
3621 If one of these is set, a http-equiv refresh is generated. Missing parameters
 
3622 default to 3 seconds and the refering url.
 
3628 If these are arrayrefs the contents will be inlined into the header.
 
3632 If true, a css snippet will be generated that sets the page in landscape mode.
 
3636 Used to override the default favicon.
 
3640 A html page title will be generated from this