Änderungen zur Unterstützung von anderen Zeichensätzen als ISO-8859-1(5) implementier...
[kivitendo-erp.git] / SL / Form.pm
1 #====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #=====================================================================
8 # SQL-Ledger Accounting
9 # Copyright (C) 1998-2002
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 # Contributors: Thomas Bayen <bayen@gmx.de>
16 #               Antti Kaihola <akaihola@siba.fi>
17 #               Moritz Bunkus (tex code)
18 #
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.
23 #
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
35 #
36 #======================================================================
37
38 package Form;
39 use Data::Dumper;
40
41 use Cwd;
42 use HTML::Template;
43 use SL::Template;
44 use CGI::Ajax;
45 use SL::DBUtils;
46 use SL::Menu;
47 use SL::User;
48 use SL::Common;
49 use CGI;
50
51 sub _input_to_hash {
52   $main::lxdebug->enter_sub(2);
53
54   my $input = $_[0];
55   my %in    = ();
56   my @pairs = split(/&/, $input);
57
58   foreach (@pairs) {
59     my ($name, $value) = split(/=/, $_, 2);
60     $in{$name} = unescape(undef, $value);
61   }
62
63   $main::lxdebug->leave_sub(2);
64
65   return %in;
66 }
67
68 sub _request_to_hash {
69   $main::lxdebug->enter_sub(2);
70
71   my ($input) = @_;
72   my ($i,        $loc,  $key,    $val);
73   my (%ATTACH,   $f,    $header, $header_body, $len, $buf);
74   my ($boundary, @list, $size,   $body, $x, $blah, $name);
75
76   if ($ENV{'CONTENT_TYPE'}
77       && ($ENV{'CONTENT_TYPE'} =~ /multipart\/form-data; boundary=(.+)$/)) {
78     $boundary = quotemeta('--' . $1);
79     @list     = split(/$boundary/, $input);
80
81     # For some reason there are always 2 extra, that are empty
82     $size = @list - 2;
83
84     for ($x = 1; $x <= $size; $x++) {
85       $header_body = $list[$x];
86       $header_body =~ /\r\n\r\n|\n\n/;
87
88       # Here we split the header and body
89       $header = $`;
90       $body   = $';    #'
91       $body =~ s/\r\n$//;
92
93       # Now we try to get the file name
94       $name = $header;
95       $name =~ /name=\"(.+)\"/;
96       ($name, $blah) = split(/\"/, $1);
97
98       # If the form name is not attach, then we need to parse this like
99       # regular form data
100       if ($name ne "attach") {
101         $body =~ s/%([0-9a-fA-Z]{2})/pack("c",hex($1))/eg;
102         $ATTACH{$name} = $body;
103
104         # Otherwise it is an attachment and we need to finish it up
105       } elsif ($name eq "attach") {
106         $header =~ /filename=\"(.+)\"/;
107         $ATTACH{'FILE_NAME'} = $1;
108         $ATTACH{'FILE_NAME'} =~ s/\"//g;
109         $ATTACH{'FILE_NAME'} =~ s/\s//g;
110         $ATTACH{'FILE_CONTENT'} = $body;
111
112         for ($i = $x; $list[$i]; $i++) {
113           $list[$i] =~ s/^.+name=$//;
114           $list[$i] =~ /\"(\w+)\"/;
115           $ATTACH{$1} = $';    #'
116         }
117       }
118     }
119
120     $main::lxdebug->leave_sub(2);
121     return %ATTACH;
122
123       } else {
124     $main::lxdebug->leave_sub(2);
125     return _input_to_hash($input);
126   }
127 }
128
129 sub new {
130   $main::lxdebug->enter_sub();
131
132   my $type = shift;
133
134   my $self = {};
135
136   if ($LXDebug::watch_form) {
137     require SL::Watchdog;
138     tie %{ $self }, 'SL::Watchdog';
139   }
140
141   read(STDIN, $_, $ENV{CONTENT_LENGTH});
142
143   if ($ENV{QUERY_STRING}) {
144     $_ = $ENV{QUERY_STRING};
145   }
146
147   if ($ARGV[0]) {
148     $_ = $ARGV[0];
149   }
150
151   my %parameters = _request_to_hash($_);
152   map({ $self->{$_} = $parameters{$_}; } keys(%parameters));
153
154   $self->{action} = lc $self->{action};
155   $self->{action} =~ s/( |-|,|\#)/_/g;
156
157   $self->{version}   = "2.4.2";
158
159   $main::lxdebug->leave_sub();
160
161   bless $self, $type;
162 }
163
164 sub debug {
165   $main::lxdebug->enter_sub();
166
167   my ($self) = @_;
168
169   print "\n";
170
171   map { print "$_ = $self->{$_}\n" } (sort keys %{$self});
172
173   $main::lxdebug->leave_sub();
174 }
175
176 sub escape {
177   $main::lxdebug->enter_sub(2);
178
179   my ($self, $str) = @_;
180
181   $str =~ s/([^a-zA-Z0-9_.-])/sprintf("%%%02x", ord($1))/ge;
182
183   $main::lxdebug->leave_sub(2);
184
185   return $str;
186 }
187
188 sub unescape {
189   $main::lxdebug->enter_sub(2);
190
191   my ($self, $str) = @_;
192
193   $str =~ tr/+/ /;
194   $str =~ s/\\$//;
195
196   $str =~ s/%([0-9a-fA-Z]{2})/pack("c",hex($1))/eg;
197
198   $main::lxdebug->leave_sub(2);
199
200   return $str;
201 }
202
203 sub quote {
204   my ($self, $str) = @_;
205
206   if ($str && !ref($str)) {
207     $str =~ s/\"/&quot;/g;
208   }
209
210   $str;
211
212 }
213
214 sub unquote {
215   my ($self, $str) = @_;
216
217   if ($str && !ref($str)) {
218     $str =~ s/&quot;/\"/g;
219   }
220
221   $str;
222
223 }
224
225 sub quote_html {
226   $main::lxdebug->enter_sub(2);
227
228   my ($self, $str) = @_;
229
230   my %replace =
231     ('order' => ['"', '<', '>'],
232      '<'             => '&lt;',
233      '>'             => '&gt;',
234      '"'             => '&quot;',
235     );
236
237   map({ $str =~ s/$_/$replace{$_}/g; } @{ $replace{"order"} });
238
239   $main::lxdebug->leave_sub(2);
240
241   return $str;
242 }
243
244 sub hide_form {
245   my $self = shift;
246
247   if (@_) {
248     map({ print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n"); } @_);
249   } else {
250     for (sort keys %$self) {
251       next if (($_ eq "header") || (ref($self->{$_}) ne ""));
252       print($main::cgi->hidden("-name" => $_, "-default" => $self->{$_}) . "\n");
253     }
254   }
255
256 }
257
258 sub error {
259   $main::lxdebug->enter_sub();
260
261   my ($self, $msg) = @_;
262   if ($ENV{HTTP_USER_AGENT}) {
263     $msg =~ s/\n/<br>/g;
264     $self->show_generic_error($msg);
265
266   } else {
267
268     if ($self->{error_function}) {
269       &{ $self->{error_function} }($msg);
270     } else {
271       die "Error: $msg\n";
272     }
273   }
274
275   $main::lxdebug->leave_sub();
276 }
277
278 sub info {
279   $main::lxdebug->enter_sub();
280
281   my ($self, $msg) = @_;
282
283   if ($ENV{HTTP_USER_AGENT}) {
284     $msg =~ s/\n/<br>/g;
285
286     if (!$self->{header}) {
287       $self->header;
288       print qq|
289       <body>|;
290     }
291
292     print qq|
293
294     <p><b>$msg</b>
295     |;
296
297   } else {
298
299     if ($self->{info_function}) {
300       &{ $self->{info_function} }($msg);
301     } else {
302       print "$msg\n";
303     }
304   }
305
306   $main::lxdebug->leave_sub();
307 }
308
309 sub numtextrows {
310   $main::lxdebug->enter_sub();
311
312   my ($self, $str, $cols, $maxrows) = @_;
313
314   my $rows = 0;
315
316   map { $rows += int(((length) - 2) / $cols) + 1 } split /\r/, $str;
317
318   $maxrows = $rows unless defined $maxrows;
319
320   $main::lxdebug->leave_sub();
321
322   return ($rows > $maxrows) ? $maxrows : $rows;
323 }
324
325 sub dberror {
326   $main::lxdebug->enter_sub();
327
328   my ($self, $msg) = @_;
329
330   $self->error("$msg\n" . $DBI::errstr);
331
332   $main::lxdebug->leave_sub();
333 }
334
335 sub isblank {
336   $main::lxdebug->enter_sub();
337
338   my ($self, $name, $msg) = @_;
339
340   if ($self->{$name} =~ /^\s*$/) {
341     $self->error($msg);
342   }
343   $main::lxdebug->leave_sub();
344 }
345
346 sub header {
347   $main::lxdebug->enter_sub();
348
349   my ($self, $extra_code) = @_;
350
351   if ($self->{header}) {
352     $main::lxdebug->leave_sub();
353     return;
354   }
355
356   my ($stylesheet, $favicon);
357
358   if ($ENV{HTTP_USER_AGENT}) {
359
360     if ($self->{stylesheet} && (-f "css/$self->{stylesheet}")) {
361       $stylesheet =
362         qq|<LINK REL="stylesheet" HREF="css/$self->{stylesheet}" TYPE="text/css" TITLE="Lx-Office stylesheet">
363  |;
364     }
365
366     $self->{favicon}    = "favicon.ico" unless $self->{favicon};
367
368     if ($self->{favicon} && (-f "$self->{favicon}")) {
369       $favicon =
370         qq|<LINK REL="shortcut icon" HREF="$self->{favicon}" TYPE="image/x-icon">
371   |;
372     }
373
374     my $db_charset = $main::dbcharset ? $main::dbcharset : Common::DEFAULT_CHARSET;
375
376     if ($self->{landscape}) {
377       $pagelayout = qq|<style type="text/css">
378                         \@page { size:landscape; }
379                         </style>|;
380     }
381
382     my $fokus = qq|  document.$self->{fokus}.focus();| if ($self->{"fokus"});
383
384     #Set Calendar
385     my $jsscript = "";
386     if ($self->{jsscript} == 1) {
387
388       $jsscript = qq|
389         <style type="text/css">\@import url(js/jscalendar/calendar-win2k-1.css);</style>
390         <script type="text/javascript" src="js/jscalendar/calendar.js"></script>
391         <script type="text/javascript" src="js/jscalendar/lang/calendar-de.js"></script>
392         <script type="text/javascript" src="js/jscalendar/calendar-setup.js"></script>
393         $self->{javascript}
394        |;
395     }
396
397     $self->{titlebar} =
398       ($self->{title})
399       ? "$self->{title} - $self->{titlebar}"
400       : $self->{titlebar};
401     my $ajax = "";
402     foreach $item (@ { $self->{AJAX} }) {
403       $ajax .= $item->show_javascript();
404     }
405     print qq|Content-Type: text/html; charset=${db_charset};
406
407 <html>
408 <head>
409   <title>$self->{titlebar}</title>
410   $stylesheet
411   $pagelayout
412   $favicon
413   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=${db_charset}">
414   $jsscript
415   $ajax
416
417   <script type="text/javascript">
418   <!--
419     function fokus() {
420       $fokus
421     }
422   //-->
423   </script>
424
425   <meta name="robots" content="noindex,nofollow" />
426   <script type="text/javascript" src="js/highlight_input.js"></script>
427   <link rel="stylesheet" type="text/css" href="css/tabcontent.css" />
428
429   <script type="text/javascript" src="js/tabcontent.js">
430
431   /***********************************************
432   * Tab Content script- Dynamic Drive DHTML code library (www.dynamicdrive.com)
433   * This notice MUST stay intact for legal use
434   * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
435   ***********************************************/
436
437   </script>
438
439   $extra_code
440 </head>
441
442 |;
443   }
444   $self->{header} = 1;
445
446   $main::lxdebug->leave_sub();
447 }
448
449 sub parse_html_template {
450   $main::lxdebug->enter_sub();
451
452   my ($self, $file, $additional_params) = @_;
453   my $language;
454
455   if (!defined(%main::myconfig) || !defined($main::myconfig{"countrycode"})) {
456     $language = $main::language;
457   } else {
458     $language = $main::myconfig{"countrycode"};
459   }
460   $language = "de" unless ($language);
461
462   if (-f "templates/webpages/${file}_${language}.html") {
463     if ((-f ".developer") &&
464         (-f "templates/webpages/${file}_master.html") &&
465         ((stat("templates/webpages/${file}_master.html"))[9] >
466          (stat("templates/webpages/${file}_${language}.html"))[9])) {
467       my $info = "Developer information: templates/webpages/${file}_master.html is newer than the localized version.\n" .
468         "Please re-run 'locales.pl' in 'locale/${language}'.";
469       print(qq|<pre>$info</pre>|);
470       die($info);
471     }
472
473     $file = "templates/webpages/${file}_${language}.html";
474   } elsif (-f "templates/webpages/${file}.html") {
475     $file = "templates/webpages/${file}.html";
476   } else {
477     my $info = "Web page template '${file}' not found.\n" .
478       "Please re-run 'locales.pl' in 'locale/${language}'.";
479     print(qq|<pre>$info</pre>|);
480     die($info);
481   }
482
483   my $template = HTML::Template->new("filename" => $file,
484                                      "die_on_bad_params" => 0,
485                                      "strict" => 0,
486                                      "case_sensitive" => 1,
487                                      "loop_context_vars" => 1,
488                                      "global_vars" => 1);
489
490   $additional_params = {} unless ($additional_params);
491   if ($self->{"DEBUG"}) {
492     $additional_params->{"DEBUG"} = $self->{"DEBUG"};
493   }
494
495   if ($additional_params->{"DEBUG"}) {
496     $additional_params->{"DEBUG"} =
497       "<br><em>DEBUG INFORMATION:</em><pre>" . $additional_params->{"DEBUG"} . "</pre>";
498   }
499
500   if (%main::myconfig) {
501     map({ $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys(%main::myconfig));
502     my $jsc_dateformat = $main::myconfig{"dateformat"};
503     $jsc_dateformat =~ s/d+/\%d/gi;
504     $jsc_dateformat =~ s/m+/\%m/gi;
505     $jsc_dateformat =~ s/y+/\%Y/gi;
506     $additional_params->{"myconfig_jsc_dateformat"} = $jsc_dateformat;
507   }
508
509   $additional_params->{"conf_jscalendar"} = $main::jscalendar;
510   $additional_params->{"conf_lizenzen"} = $main::lizenzen;
511   $additional_params->{"conf_latex_templates"} = $main::latex;
512   $additional_params->{"conf_opendocument_templates"} = $main::opendocument_templates;
513
514   my @additional_param_names = keys(%{$additional_params});
515   foreach my $key ($template->param()) {
516     my $param = $self->{$key};
517     $param = $additional_params->{$key} if (grep(/^${key}$/, @additional_param_names));
518     $param = [] if (($template->query("name" => $key) eq "LOOP") && (ref($param) ne "ARRAY"));
519     $template->param($key => $param);
520   }
521
522   my $output = $template->output();
523
524   $main::lxdebug->leave_sub();
525
526   return $output;
527 }
528
529 sub show_generic_error {
530   my ($self, $error, $title, $action) = @_;
531
532   my $add_params = {};
533   $add_params->{"title"} = $title if ($title);
534   $self->{"label_error"} = $error;
535
536   my @vars;
537   if ($action) {
538     map({ delete($self->{$_}); } qw(action));
539     map({ push(@vars, { "name" => $_, "value" => $self->{$_} })
540             if (!ref($self->{$_})); }
541         keys(%{$self}));
542     $add_params->{"SHOW_BUTTON"} = 1;
543     $add_params->{"BUTTON_LABEL"} = $action;
544   }
545   $add_params->{"VARIABLES"} = \@vars;
546
547   $self->header();
548   print($self->parse_html_template("generic/error", $add_params));
549
550   die("Error: $error\n");
551 }
552
553 sub show_generic_information {
554   my ($self, $error, $title) = @_;
555
556   my $add_params = {};
557   $add_params->{"title"} = $title if ($title);
558   $self->{"label_information"} = $error;
559
560   $self->header();
561   print($self->parse_html_template("generic/information", $add_params));
562
563   die("Information: $error\n");
564 }
565
566 # write Trigger JavaScript-Code ($qty = quantity of Triggers)
567 # changed it to accept an arbitrary number of triggers - sschoeling
568 sub write_trigger {
569   $main::lxdebug->enter_sub();
570
571   my $self     = shift;
572   my $myconfig = shift;
573   my $qty      = shift;
574
575   # set dateform for jsscript
576   # default
577   my %dateformats = (
578     "dd.mm.yy" => "%d.%m.%Y",
579     "dd-mm-yy" => "%d-%m-%Y",
580     "dd/mm/yy" => "%d/%m/%Y",
581     "mm/dd/yy" => "%m/%d/%Y",
582     "mm-dd-yy" => "%m-%d-%Y",
583     "yyyy-mm-dd" => "%Y-%m-%d",
584     );
585
586   my $ifFormat = defined($dateformats{$myconfig{"dateformat"}}) ?
587     $dateformats{$myconfig{"dateformat"}} : "%d.%m.%Y";
588
589   my @triggers;
590   while ($#_ >= 2) {
591     push @triggers, qq|
592        Calendar.setup(
593       {
594       inputField : "| . (shift) . qq|",
595       ifFormat :"$ifFormat",
596       align : "| .  (shift) . qq|",
597       button : "| . (shift) . qq|"
598       }
599       );
600        |;
601   }
602   my $jsscript = qq|
603        <script type="text/javascript">
604        <!--| . join("", @triggers) . qq|//-->
605         </script>
606         |;
607
608   $main::lxdebug->leave_sub();
609
610   return $jsscript;
611 }    #end sub write_trigger
612
613 sub redirect {
614   $main::lxdebug->enter_sub();
615
616   my ($self, $msg) = @_;
617
618   if ($self->{callback}) {
619
620     ($script, $argv) = split(/\?/, $self->{callback});
621     exec("perl", "$script", $argv);
622
623   } else {
624
625     $self->info($msg);
626     exit;
627   }
628
629   $main::lxdebug->leave_sub();
630 }
631
632 # sort of columns removed - empty sub
633 sub sort_columns {
634   $main::lxdebug->enter_sub();
635
636   my ($self, @columns) = @_;
637
638   $main::lxdebug->leave_sub();
639
640   return @columns;
641 }
642 #
643 sub format_amount {
644   $main::lxdebug->enter_sub(2);
645
646   my ($self, $myconfig, $amount, $places, $dash) = @_;
647
648   if ($amount eq "") {
649     $amount = 0;
650   }
651   my $neg = ($amount =~ s/-//);
652
653   if (defined($places) && ($places ne '')) {
654     if ($places < 0) {
655       $amount *= 1;
656       $places *= -1;
657
658       my ($actual_places) = ($amount =~ /\.(\d+)/);
659       $actual_places = length($actual_places);
660       $places = $actual_places > $places ? $actual_places : $places;
661     }
662
663     $amount = $self->round_amount($amount, $places);
664   }
665
666   my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars
667   my @p = split(/\./, $amount); # split amount at decimal point
668
669   $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters
670
671   $amount = $p[0];
672   $amount .= $d[0].$p[1].(0 x ($places - length $p[1])) if ($places || $p[1] ne '');
673
674   $amount = do {
675     ($dash =~ /-/)    ? ($neg ? "($amount)"  : "$amount" )    :
676     ($dash =~ /DRCR/) ? ($neg ? "$amount DR" : "$amount CR" ) :
677                         ($neg ? "-$amount"   : "$amount" )    ;
678   };
679
680
681   $main::lxdebug->leave_sub(2);
682   return $amount;
683 }
684 #
685 sub parse_amount {
686   $main::lxdebug->enter_sub(2);
687
688   my ($self, $myconfig, $amount) = @_;
689
690   if (   ($myconfig->{numberformat} eq '1.000,00')
691       || ($myconfig->{numberformat} eq '1000,00')) {
692     $amount =~ s/\.//g;
693     $amount =~ s/,/\./;
694   }
695
696   if ($myconfig->{numberformat} eq "1'000.00") {
697     $amount =~ s/\'//g;
698   }
699
700   $amount =~ s/,//g;
701
702   $main::lxdebug->leave_sub(2);
703
704   return ($amount * 1);
705 }
706
707 sub round_amount {
708   $main::lxdebug->enter_sub(2);
709
710   my ($self, $amount, $places) = @_;
711   my $round_amount;
712
713   # Rounding like "Kaufmannsrunden"
714   # Descr. http://de.wikipedia.org/wiki/Rundung
715   # Inspired by
716   # http://www.perl.com/doc/FAQs/FAQ/oldfaq-html/Q4.13.html
717   # Solves Bug: 189
718   # Udo Spallek
719   $amount = $amount * (10**($places));
720   $round_amount = int($amount + .5 * ($amount <=> 0)) / (10**($places));
721
722   $main::lxdebug->leave_sub(2);
723
724   return $round_amount;
725
726 }
727
728 sub parse_template {
729   $main::lxdebug->enter_sub();
730
731   my ($self, $myconfig, $userspath) = @_;
732   my $template;
733
734   $self->{"cwd"} = getcwd();
735   $self->{"tmpdir"} = $self->{cwd} . "/${userspath}";
736
737   if ($self->{"format"} =~ /(opendocument|oasis)/i) {
738     $template = OpenDocumentTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
739   } elsif ($self->{"format"} =~ /(postscript|pdf)/i) {
740     $ENV{"TEXINPUTS"} = ".:" . getcwd() . "/" . $myconfig->{"templates"} . ":" . $ENV{"TEXINPUTS"};
741     $template = LaTeXTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
742   } elsif (($self->{"format"} =~ /html/i) ||
743            (!$self->{"format"} && ($self->{"IN"} =~ /html$/i))) {
744     $template = HTMLTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
745   } elsif (($self->{"format"} =~ /xml/i) ||
746              (!$self->{"format"} && ($self->{"IN"} =~ /xml$/i))) {
747     $template = XMLTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
748   } elsif ( $self->{"format"} =~ /elsterwinston/i ) {
749     $template = XMLTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
750   } elsif ( $self->{"format"} =~ /elstertaxbird/i ) {
751     $template = XMLTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
752   } elsif ( defined $self->{'format'}) {
753     $self->error("Outputformat not defined. This may be a future feature: $self->{'format'}");
754   } elsif ( $self->{'format'} eq '' ) {
755     $self->error("No Outputformat given: $self->{'format'}");
756   } else { #Catch the rest
757     $self->error("Outputformat not defined: $self->{'format'}");
758   }
759
760   # Copy the notes from the invoice/sales order etc. back to the variable "notes" because that is where most templates expect it to be.
761   $self->{"notes"} = $self->{ $self->{"formname"} . "notes" };
762
763   map({ $self->{"employee_${_}"} = $myconfig->{$_}; }
764       qw(email tel fax name signature company address businessnumber
765          co_ustid taxnumber duns));
766   map({ $self->{"employee_${_}"} =~ s/\\n/\n/g; }
767       qw(company address signature));
768   map({ $self->{$_} =~ s/\\n/\n/g; } qw(company address signature));
769
770   $self->{copies} = 1 if (($self->{copies} *= 1) <= 0);
771
772   # OUT is used for the media, screen, printer, email
773   # for postscript we store a copy in a temporary file
774   my $fileid = time;
775   $self->{tmpfile} ||= "$userspath/${fileid}.$self->{IN}";
776   if ($template->uses_temp_file() || $self->{media} eq 'email') {
777     $out = $self->{OUT};
778     $self->{OUT} = ">$self->{tmpfile}";
779   }
780
781   if ($self->{OUT}) {
782     open(OUT, "$self->{OUT}") or $self->error("$self->{OUT} : $!");
783   } else {
784     open(OUT, ">-") or $self->error("STDOUT : $!");
785     $self->header;
786   }
787
788   if (!$template->parse(*OUT)) {
789     $self->cleanup();
790     $self->error("$self->{IN} : " . $template->get_error());
791   }
792
793   close(OUT);
794
795   if ($template->uses_temp_file() || $self->{media} eq 'email') {
796
797     if ($self->{media} eq 'email') {
798
799       use SL::Mailer;
800
801       my $mail = new Mailer;
802
803       map { $mail->{$_} = $self->{$_} }
804         qw(cc bcc subject message version format);
805       $mail->{charset} = $main::dbcharset ? $main::dbcharset : Common::DEFAULT_CHARSET;
806       $mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
807       $mail->{from}   = qq|"$myconfig->{name}" <$myconfig->{email}>|;
808       $mail->{fileid} = "$fileid.";
809       $myconfig->{signature} =~ s/\\r\\n/\\n/g;
810
811       # if we send html or plain text inline
812       if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
813         $mail->{contenttype} = "text/html";
814
815         $mail->{message}       =~ s/\r\n/<br>\n/g;
816         $myconfig->{signature} =~ s/\\n/<br>\n/g;
817         $mail->{message} .= "<br>\n-- <br>\n$myconfig->{signature}\n<br>";
818
819         open(IN, $self->{tmpfile})
820           or $self->error($self->cleanup . "$self->{tmpfile} : $!");
821         while (<IN>) {
822           $mail->{message} .= $_;
823         }
824
825         close(IN);
826
827       } else {
828
829         if (!$self->{"do_not_attach"}) {
830           @{ $mail->{attachments} } =
831             ({ "filename" => $self->{"tmpfile"},
832                "name" => $self->{"attachment_filename"} ?
833                  $self->{"attachment_filename"} : $self->{"tmpfile"} });
834         }
835
836         $mail->{message}       =~ s/\r\n/\n/g;
837         $myconfig->{signature} =~ s/\\n/\n/g;
838         $mail->{message} .= "\n-- \n$myconfig->{signature}";
839
840       }
841
842       my $err = $mail->send($out);
843       $self->error($self->cleanup . "$err") if ($err);
844
845     } else {
846
847       $self->{OUT} = $out;
848
849       my $numbytes = (-s $self->{tmpfile});
850       open(IN, $self->{tmpfile})
851         or $self->error($self->cleanup . "$self->{tmpfile} : $!");
852
853       $self->{copies} = 1 unless $self->{media} eq 'printer';
854
855       chdir("$self->{cwd}");
856       #print(STDERR "Kopien $self->{copies}\n");
857       #print(STDERR "OUT $self->{OUT}\n");
858       for my $i (1 .. $self->{copies}) {
859         if ($self->{OUT}) {
860           open(OUT, $self->{OUT})
861             or $self->error($self->cleanup . "$self->{OUT} : $!");
862         } else {
863           $self->{attachment_filename} = $self->generate_attachment_filename();
864
865           # launch application
866           print qq|Content-Type: | . $template->get_mime_type() . qq|
867 Content-Disposition: attachment; filename="$self->{attachment_filename}"
868 Content-Length: $numbytes
869
870 |;
871
872           open(OUT, ">-") or $self->error($self->cleanup . "$!: STDOUT");
873
874         }
875
876         while (<IN>) {
877           print OUT $_;
878         }
879
880         close(OUT);
881
882         seek IN, 0, 0;
883       }
884
885       close(IN);
886     }
887
888   }
889
890   $self->cleanup;
891
892   chdir("$self->{cwd}");
893   $main::lxdebug->leave_sub();
894 }
895
896 sub generate_attachment_filename {
897   my ($self) = @_;
898
899   my %formname_translations = (
900      bin_list            => $main::locale->text('Bin List'),
901      credit_note         => $main::locale->text('Credit Note'),
902      invoice             => $main::locale->text('Invoice'),
903      packing_list        => $main::locale->text('Packing List'),
904      pick_list           => $main::locale->text('Pick List'),
905      proforma            => $main::locale->text('Proforma Invoice'),
906      purchase_order      => $main::locale->text('Purchase Order'),
907      request_quotation   => $main::locale->text('RFQ'),
908      sales_order         => $main::locale->text('Confirmation'),
909      sales_quotation     => $main::locale->text('Quotation'),
910      storno_invoice      => $main::locale->text('Storno Invoice'),
911      storno_packing_list => $main::locale->text('Storno Packing List'),
912   );
913
914   my $attachment_filename = $formname_translations{$self->{"formname"}};
915   my $prefix = 
916       (grep { $self->{"type"} eq $_ } qw(invoice credit_note)) ? "inv"
917     : ($self->{"type"} =~ /_quotation$/)                       ? "quo"
918     :                                                            "ord";
919
920   if ($attachment_filename && $self->{"${prefix}number"}) {
921     $attachment_filename .= "_" . $self->{"${prefix}number"}
922                             . (  $self->{format} =~ /pdf/i          ? ".pdf"
923                                : $self->{format} =~ /postscript/i   ? ".ps"
924                                : $self->{format} =~ /opendocument/i ? ".odt"
925                                : $self->{format} =~ /html/i         ? ".html"
926                                :                                      "");
927     $attachment_filename =~ s/ /_/g;
928     my %umlaute = ( "ä" => "ae", "ö" => "oe", "ü" => "ue", 
929                     "Ä" => "Ae", "Ö" => "Oe", "Ü" => "Ue", "ß" => "ss");
930     map { $attachment_filename =~ s/$_/$umlaute{$_}/g } keys %umlaute;
931   } else {
932     $attachment_filename = "";
933   }
934
935   return $attachment_filename;
936 }
937
938 sub cleanup {
939   $main::lxdebug->enter_sub();
940
941   my $self = shift;
942
943   chdir("$self->{tmpdir}");
944
945   my @err = ();
946   if (-f "$self->{tmpfile}.err") {
947     open(FH, "$self->{tmpfile}.err");
948     @err = <FH>;
949     close(FH);
950   }
951
952   if ($self->{tmpfile}) {
953     $self->{tmpfile} =~ s|.*/||g;
954     # strip extension
955     $self->{tmpfile} =~ s/\.\w+$//g;
956     my $tmpfile = $self->{tmpfile};
957     unlink(<$tmpfile.*>);
958   }
959
960   chdir("$self->{cwd}");
961
962   $main::lxdebug->leave_sub();
963
964   return "@err";
965 }
966
967 sub datetonum {
968   $main::lxdebug->enter_sub();
969
970   my ($self, $date, $myconfig) = @_;
971
972   if ($date && $date =~ /\D/) {
973
974     if ($myconfig->{dateformat} =~ /^yy/) {
975       ($yy, $mm, $dd) = split /\D/, $date;
976     }
977     if ($myconfig->{dateformat} =~ /^mm/) {
978       ($mm, $dd, $yy) = split /\D/, $date;
979     }
980     if ($myconfig->{dateformat} =~ /^dd/) {
981       ($dd, $mm, $yy) = split /\D/, $date;
982     }
983
984     $dd *= 1;
985     $mm *= 1;
986     $yy = ($yy < 70) ? $yy + 2000 : $yy;
987     $yy = ($yy >= 70 && $yy <= 99) ? $yy + 1900 : $yy;
988
989     $dd = "0$dd" if ($dd < 10);
990     $mm = "0$mm" if ($mm < 10);
991
992     $date = "$yy$mm$dd";
993   }
994
995   $main::lxdebug->leave_sub();
996
997   return $date;
998 }
999
1000 # Database routines used throughout
1001
1002 sub dbconnect {
1003   $main::lxdebug->enter_sub(2);
1004
1005   my ($self, $myconfig) = @_;
1006
1007   # connect to database
1008   my $dbh =
1009     DBI->connect($myconfig->{dbconnect},
1010                  $myconfig->{dbuser}, $myconfig->{dbpasswd})
1011     or $self->dberror;
1012
1013   # set db options
1014   if ($myconfig->{dboptions}) {
1015     $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
1016   }
1017
1018   $main::lxdebug->leave_sub(2);
1019
1020   return $dbh;
1021 }
1022
1023 sub dbconnect_noauto {
1024   $main::lxdebug->enter_sub();
1025
1026   my ($self, $myconfig) = @_;
1027
1028   # connect to database
1029   $dbh =
1030     DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser},
1031                  $myconfig->{dbpasswd}, { AutoCommit => 0 })
1032     or $self->dberror;
1033
1034   # set db options
1035   if ($myconfig->{dboptions}) {
1036     $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
1037   }
1038
1039   $main::lxdebug->leave_sub();
1040
1041   return $dbh;
1042 }
1043
1044 sub update_balance {
1045   $main::lxdebug->enter_sub();
1046
1047   my ($self, $dbh, $table, $field, $where, $value, @values) = @_;
1048
1049   # if we have a value, go do it
1050   if ($value != 0) {
1051
1052     # retrieve balance from table
1053     my $query = "SELECT $field FROM $table WHERE $where FOR UPDATE";
1054     my $sth = prepare_execute_query($self, $dbh, $query, @values);
1055     my ($balance) = $sth->fetchrow_array;
1056     $sth->finish;
1057
1058     $balance += $value;
1059
1060     # update balance
1061     $query = "UPDATE $table SET $field = $balance WHERE $where";
1062     do_query($self, $dbh, $query, @values);
1063   }
1064   $main::lxdebug->leave_sub();
1065 }
1066
1067 sub update_exchangerate {
1068   $main::lxdebug->enter_sub();
1069
1070   my ($self, $dbh, $curr, $transdate, $buy, $sell) = @_;
1071
1072   # some sanity check for currency
1073   if ($curr eq '') {
1074     $main::lxdebug->leave_sub();
1075     return;
1076   }
1077
1078   my $query = qq|SELECT e.curr FROM exchangerate e
1079                  WHERE e.curr = ? AND e.transdate = ?
1080                  FOR UPDATE|;
1081   my $sth = prepare_execute_query($self, $dbh, $query, $curr, $transdate);
1082
1083   my $set;
1084   if ($buy != 0 && $sell != 0) {
1085     $set = "buy = $buy, sell = $sell";
1086   } elsif ($buy != 0) {
1087     $set = "buy = $buy";
1088   } elsif ($sell != 0) {
1089     $set = "sell = $sell";
1090   }
1091
1092   if ($sth->fetchrow_array) {
1093     $query = qq|UPDATE exchangerate
1094                 SET $set
1095                 WHERE curr = ?
1096                 AND transdate = ?|;
1097   } else {
1098     $query = qq|INSERT INTO exchangerate (curr, buy, sell, transdate)
1099                 VALUES (?, $buy, $sell, ?)|;
1100   }
1101   $sth->finish;
1102   do_query($self, $dbh, $query, $curr, $transdate);
1103
1104   $main::lxdebug->leave_sub();
1105 }
1106
1107 sub save_exchangerate {
1108   $main::lxdebug->enter_sub();
1109
1110   my ($self, $myconfig, $currency, $transdate, $rate, $fld) = @_;
1111
1112   my $dbh = $self->dbconnect($myconfig);
1113
1114   my ($buy, $sell) = (0, 0);
1115   $buy  = $rate if $fld eq 'buy';
1116   $sell = $rate if $fld eq 'sell';
1117
1118   $self->update_exchangerate($dbh, $currency, $transdate, $buy, $sell);
1119
1120   $dbh->disconnect;
1121
1122   $main::lxdebug->leave_sub();
1123 }
1124
1125 sub get_exchangerate {
1126   $main::lxdebug->enter_sub();
1127
1128   my ($self, $dbh, $curr, $transdate, $fld) = @_;
1129
1130   unless ($transdate) {
1131     $main::lxdebug->leave_sub();
1132     return 1;
1133   }
1134
1135   my $query = qq|SELECT e.$fld FROM exchangerate e
1136                  WHERE e.curr = ? AND e.transdate = ?|;
1137   my ($exchangerate) = selectrow_query($self, $dbh, $query, $curr, $transdate);
1138
1139   if (!$exchangerate) {
1140     $exchangerate = 1;
1141   }
1142
1143   $main::lxdebug->leave_sub();
1144
1145   return $exchangerate;
1146 }
1147
1148 sub check_exchangerate {
1149   $main::lxdebug->enter_sub();
1150
1151   my ($self, $myconfig, $currency, $transdate, $fld) = @_;
1152
1153   unless ($transdate) {
1154     $main::lxdebug->leave_sub();
1155     return "";
1156   }
1157
1158   my $dbh = $self->dbconnect($myconfig);
1159
1160   my $query = qq|SELECT e.$fld FROM exchangerate e
1161                  WHERE e.curr = ? AND e.transdate = ?|;
1162   my ($exchangerate) = selectrow_query($self, $dbh, $query, $currency, $transdate);
1163   $dbh->disconnect;
1164
1165   $main::lxdebug->leave_sub();
1166
1167   return $exchangerate;
1168 }
1169
1170 sub set_payment_options {
1171   $main::lxdebug->enter_sub();
1172
1173   my ($self, $myconfig, $transdate) = @_;
1174
1175   if ($self->{payment_id}) {
1176
1177     my $dbh = $self->dbconnect($myconfig);
1178
1179     my $query =
1180       qq|SELECT p.terms_netto, p.terms_skonto, p.percent_skonto, p.description_long | .
1181       qq|FROM payment_terms p | .
1182       qq|WHERE p.id = ?|;
1183
1184     ($self->{terms_netto}, $self->{terms_skonto}, $self->{percent_skonto},
1185      $self->{payment_terms}) =
1186        selectrow_query($self, $dbh, $query, $self->{payment_id});
1187
1188     if ($transdate eq "") {
1189       if ($self->{invdate}) {
1190         $transdate = $self->{invdate};
1191       } else {
1192         $transdate = $self->{transdate};
1193       }
1194     }
1195
1196     $query =
1197       qq|SELECT ?::date + ?::integer AS netto_date, ?::date + ?::integer AS skonto_date | .
1198       qq|FROM payment_terms|;
1199     ($self->{netto_date}, $self->{skonto_date}) =
1200       selectrow_query($self, $dbh, $query, $transdate, $self->{terms_netto}, $transdate, $self->{terms_skonto});
1201
1202     my $total = ($self->{invtotal}) ? $self->{invtotal} : $self->{ordtotal};
1203     my $skonto_amount = $self->parse_amount($myconfig, $total) *
1204       $self->{percent_skonto};
1205
1206     $self->{skonto_amount} =
1207       $self->format_amount($myconfig, $skonto_amount, 2);
1208
1209     if ($self->{"language_id"}) {
1210       $query =
1211         qq|SELECT t.description_long, l.output_numberformat, l.output_dateformat, l.output_longdates | .
1212         qq|FROM translation_payment_terms t | .
1213         qq|LEFT JOIN language l ON t.language_id = l.id | .
1214         qq|WHERE (t.language_id = ?) AND (t.payment_terms_id = ?)|;
1215       my ($description_long, $output_numberformat, $output_dateformat,
1216         $output_longdates) =
1217         selectrow_query($self, $dbh, $query,
1218                         $self->{"language_id"}, $self->{"payment_id"});
1219
1220       $self->{payment_terms} = $description_long if ($description_long);
1221
1222       if ($output_dateformat) {
1223         foreach my $key (qw(netto_date skonto_date)) {
1224           $self->{$key} =
1225             $main::locale->reformat_date($myconfig, $self->{$key},
1226                                          $output_dateformat,
1227                                          $output_longdates);
1228         }
1229       }
1230
1231       if ($output_numberformat &&
1232           ($output_numberformat ne $myconfig->{"numberformat"})) {
1233         my $saved_numberformat = $myconfig->{"numberformat"};
1234         $myconfig->{"numberformat"} = $output_numberformat;
1235         $self->{skonto_amount} =
1236           $self->format_amount($myconfig, $skonto_amount, 2);
1237         $myconfig->{"numberformat"} = $saved_numberformat;
1238       }
1239     }
1240
1241     $self->{payment_terms} =~ s/<%netto_date%>/$self->{netto_date}/g;
1242     $self->{payment_terms} =~ s/<%skonto_date%>/$self->{skonto_date}/g;
1243     $self->{payment_terms} =~ s/<%skonto_amount%>/$self->{skonto_amount}/g;
1244     $self->{payment_terms} =~ s/<%total%>/$self->{total}/g;
1245     $self->{payment_terms} =~ s/<%invtotal%>/$self->{invtotal}/g;
1246     $self->{payment_terms} =~ s/<%currency%>/$self->{currency}/g;
1247     $self->{payment_terms} =~ s/<%terms_netto%>/$self->{terms_netto}/g;
1248     $self->{payment_terms} =~ s/<%account_number%>/$self->{account_number}/g;
1249     $self->{payment_terms} =~ s/<%bank%>/$self->{bank}/g;
1250     $self->{payment_terms} =~ s/<%bank_code%>/$self->{bank_code}/g;
1251
1252     $dbh->disconnect;
1253   }
1254
1255   $main::lxdebug->leave_sub();
1256
1257 }
1258
1259 sub get_template_language {
1260   $main::lxdebug->enter_sub();
1261
1262   my ($self, $myconfig) = @_;
1263
1264   my $template_code = "";
1265
1266   if ($self->{language_id}) {
1267     my $dbh = $self->dbconnect($myconfig);
1268     my $query = qq|SELECT template_code FROM language WHERE id = ?|;
1269     ($template_code) = selectrow_query($self, $dbh, $query, $self->{language_id});
1270     $dbh->disconnect;
1271   }
1272
1273   $main::lxdebug->leave_sub();
1274
1275   return $template_code;
1276 }
1277
1278 sub get_printer_code {
1279   $main::lxdebug->enter_sub();
1280
1281   my ($self, $myconfig) = @_;
1282
1283   my $template_code = "";
1284
1285   if ($self->{printer_id}) {
1286     my $dbh = $self->dbconnect($myconfig);
1287     my $query = qq|SELECT template_code, printer_command FROM printers WHERE id = ?|;
1288     ($template_code, $self->{printer_command}) = selectrow_query($self, $dbh, $query, $self->{printer_id});
1289     $dbh->disconnect;
1290   }
1291
1292   $main::lxdebug->leave_sub();
1293
1294   return $template_code;
1295 }
1296
1297 sub get_shipto {
1298   $main::lxdebug->enter_sub();
1299
1300   my ($self, $myconfig) = @_;
1301
1302   my $template_code = "";
1303
1304   if ($self->{shipto_id}) {
1305     my $dbh = $self->dbconnect($myconfig);
1306     my $query = qq|SELECT * FROM shipto WHERE shipto_id = ?|;
1307     my $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{shipto_id});
1308     map({ $self->{$_} = $ref->{$_} } keys(%$ref));
1309     $dbh->disconnect;
1310   }
1311
1312   $main::lxdebug->leave_sub();
1313 }
1314
1315 sub add_shipto {
1316   $main::lxdebug->enter_sub();
1317
1318   my ($self, $dbh, $id, $module) = @_;
1319
1320   my $shipto;
1321   my @values;
1322   foreach my $item (qw(name department_1 department_2 street zipcode city country
1323                        contact phone fax email)) {
1324     if ($self->{"shipto$item"}) {
1325       $shipto = 1 if ($self->{$item} ne $self->{"shipto$item"});
1326     }
1327     push(@values, $self->{"shipto${item}"});
1328   }
1329   if ($shipto) {
1330     if ($self->{shipto_id}) {
1331       my $query = qq|UPDATE shipto set
1332                        shiptoname = ?,
1333                        shiptodepartment_1 = ?,
1334                        shiptodepartment_2 = ?,
1335                        shiptostreet = ?,
1336                        shiptozipcode = ?,
1337                        shiptocity = ?,
1338                        shiptocountry = ?,
1339                        shiptocontact = ?,
1340                        shiptophone = ?,
1341                        shiptofax = ?,
1342                        shiptoemail = ?
1343                      WHERE shipto_id = ?|;
1344       do_query($self, $dbh, $query, @values, $self->{shipto_id});
1345     } else {
1346       my $query = qq|SELECT * FROM shipto
1347                      WHERE shiptoname = ? AND
1348                        shiptodepartment_1 = ? AND
1349                        shiptodepartment_2 = ? AND
1350                        shiptostreet = ? AND
1351                        shiptozipcode = ? AND
1352                        shiptocity = ? AND
1353                        shiptocountry = ? AND
1354                        shiptocontact = ? AND
1355                        shiptophone = ? AND
1356                        shiptofax = ? AND
1357                        shiptoemail = ?|;
1358       my $insert_check = selectfirst_hashref_query($self, $dbh, $query, @values);
1359       if(!$insert_check){
1360         $query =
1361           qq|INSERT INTO shipto (trans_id, shiptoname, shiptodepartment_1, shiptodepartment_2,
1362                                  shiptostreet, shiptozipcode, shiptocity, shiptocountry,
1363                                  shiptocontact, shiptophone, shiptofax, shiptoemail, module)
1364              VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|;
1365         do_query($self, $dbh, $query, $id, @values, $module);
1366      }
1367     }
1368   }
1369
1370   $main::lxdebug->leave_sub();
1371 }
1372
1373 sub get_employee {
1374   $main::lxdebug->enter_sub();
1375
1376   my ($self, $dbh) = @_;
1377
1378   my $query = qq|SELECT id, name FROM employee WHERE login = ?|;
1379   ($self->{employee_id}, $self->{employee}) = selectrow_query($self, $dbh, $query, $self->{login});
1380   $self->{employee_id} *= 1;
1381
1382   $main::lxdebug->leave_sub();
1383 }
1384
1385 sub get_salesman {
1386   $main::lxdebug->enter_sub();
1387
1388   my ($self, $myconfig, $salesman_id) = @_;
1389
1390   $main::lxdebug->leave_sub() and return unless $salesman_id;
1391
1392   my $dbh = $self->dbconnect($myconfig);
1393
1394   my ($login) =
1395     selectrow_query($self, $dbh, qq|SELECT login FROM employee WHERE id = ?|,
1396                     $salesman_id);
1397
1398   if ($login) {
1399     my $user = new User($main::memberfile, $login);
1400     map({ $self->{"salesman_$_"} = $user->{$_}; }
1401         qw(address businessnumber co_ustid company duns email fax name
1402            taxnumber tel));
1403     $self->{salesman_login} = $login;
1404
1405     $self->{salesman_name} = $login
1406       if ($self->{salesman_name} eq "");
1407
1408     map({ $self->{"salesman_$_"} =~ s/\\n/\n/g; } qw(address company));
1409   }
1410
1411   $dbh->disconnect();
1412
1413   $main::lxdebug->leave_sub();
1414 }
1415
1416 sub get_duedate {
1417   $main::lxdebug->enter_sub();
1418
1419   my ($self, $myconfig) = @_;
1420
1421   my $dbh = $self->dbconnect($myconfig);
1422   my $query = qq|SELECT current_date + terms_netto FROM payment_terms WHERE id = ?|;
1423   ($self->{duedate}) = selectrow_query($self, $dbh, $query, $self->{payment_id});
1424   $dbh->disconnect();
1425
1426   $main::lxdebug->leave_sub();
1427 }
1428
1429 sub _get_contacts {
1430   $main::lxdebug->enter_sub();
1431
1432   my ($self, $dbh, $id, $key) = @_;
1433
1434   $key = "all_contacts" unless ($key);
1435
1436   my $query =
1437     qq|SELECT cp_id, cp_cv_id, cp_name, cp_givenname, cp_abteilung | .
1438     qq|FROM contacts | .
1439     qq|WHERE cp_cv_id = ? | .
1440     qq|ORDER BY lower(cp_name)|;
1441
1442   $self->{$key} = selectall_hashref_query($self, $dbh, $query, $id);
1443
1444   $main::lxdebug->leave_sub();
1445 }
1446
1447 sub _get_projects {
1448   $main::lxdebug->enter_sub();
1449
1450   my ($self, $dbh, $key) = @_;
1451
1452   my ($all, $old_id, $where, @values);
1453
1454   if (ref($key) eq "HASH") {
1455     my $params = $key;
1456
1457     $key = "ALL_PROJECTS";
1458
1459     foreach my $p (keys(%{$params})) {
1460       if ($p eq "all") {
1461         $all = $params->{$p};
1462       } elsif ($p eq "old_id") {
1463         $old_id = $params->{$p};
1464       } elsif ($p eq "key") {
1465         $key = $params->{$p};
1466       }
1467     }
1468   }
1469
1470   if (!$all) {
1471     $where = "WHERE active ";
1472     if ($old_id) {
1473       if (ref($old_id) eq "ARRAY") {
1474         my @ids = grep({ $_ } @{$old_id});
1475         if (@ids) {
1476           $where .= " OR id IN (" . join(",", map({ "?" } @ids)) . ") ";
1477           push(@values, @ids);
1478         }
1479       } else {
1480         $where .= " OR (id = ?) ";
1481         push(@values, $old_id);
1482       }
1483     }
1484   }
1485
1486   my $query =
1487     qq|SELECT id, projectnumber, description, active | .
1488     qq|FROM project | .
1489     $where .
1490     qq|ORDER BY lower(projectnumber)|;
1491
1492   $self->{$key} = selectall_hashref_query($self, $dbh, $query, @values);
1493
1494   $main::lxdebug->leave_sub();
1495 }
1496
1497 sub _get_shipto {
1498   $main::lxdebug->enter_sub();
1499
1500   my ($self, $dbh, $vc_id, $key) = @_;
1501
1502   $key = "all_shipto" unless ($key);
1503
1504   # get shipping addresses
1505   my $query =
1506     qq|SELECT shipto_id, shiptoname, shiptodepartment_1 | .
1507     qq|FROM shipto | .
1508     qq|WHERE trans_id = ?|;
1509
1510   $self->{$key} = selectall_hashref_query($self, $dbh, $query, $vc_id);
1511
1512   $main::lxdebug->leave_sub();
1513 }
1514
1515 sub _get_printers {
1516   $main::lxdebug->enter_sub();
1517
1518   my ($self, $dbh, $key) = @_;
1519
1520   $key = "all_printers" unless ($key);
1521
1522   my $query = qq|SELECT id, printer_description, printer_command, template_code FROM printers|;
1523
1524   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
1525
1526   $main::lxdebug->leave_sub();
1527 }
1528
1529 sub _get_charts {
1530   $main::lxdebug->enter_sub();
1531
1532   my ($self, $dbh, $params) = @_;
1533
1534   $key = $params->{key};
1535   $key = "all_charts" unless ($key);
1536
1537   my $transdate = quote_db_date($params->{transdate});
1538
1539   my $query =
1540     qq|SELECT c.accno, c.description, c.link, tk.taxkey_id, tk.tax_id | .
1541     qq|FROM chart c | .
1542     qq|LEFT JOIN taxkeys tk ON | .
1543     qq|(tk.id = (SELECT id FROM taxkeys | .
1544     qq|          WHERE taxkeys.chart_id = c.id AND startdate <= $transdate | .
1545     qq|          ORDER BY startdate DESC LIMIT 1)) | .
1546     qq|ORDER BY c.accno|;
1547
1548   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
1549
1550   $main::lxdebug->leave_sub();
1551 }
1552
1553 sub _get_taxcharts {
1554   $main::lxdebug->enter_sub();
1555
1556   my ($self, $dbh, $key) = @_;
1557
1558   $key = "all_taxcharts" unless ($key);
1559
1560   my $query = qq|SELECT * FROM tax ORDER BY taxkey|;
1561
1562   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
1563
1564   $main::lxdebug->leave_sub();
1565 }
1566
1567 sub _get_taxzones {
1568   $main::lxdebug->enter_sub();
1569
1570   my ($self, $dbh, $key) = @_;
1571
1572   $key = "all_taxzones" unless ($key);
1573
1574   my $query = qq|SELECT * FROM tax_zones ORDER BY id|;
1575
1576   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
1577
1578   $main::lxdebug->leave_sub();
1579 }
1580
1581 sub _get_employees {
1582   $main::lxdebug->enter_sub();
1583
1584   my ($self, $dbh, $key) = @_;
1585
1586   $key = "all_employees" unless ($key);
1587   $self->{$key} =
1588     selectall_hashref_query($self, $dbh, qq|SELECT * FROM employee|);
1589
1590   $main::lxdebug->leave_sub();
1591 }
1592
1593 sub _get_business_types {
1594   $main::lxdebug->enter_sub();
1595
1596   my ($self, $dbh, $key) = @_;
1597
1598   $key = "all_business_types" unless ($key);
1599   $self->{$key} =
1600     selectall_hashref_query($self, $dbh, qq|SELECT * FROM business|);
1601
1602   $main::lxdebug->leave_sub();
1603 }
1604
1605 sub _get_languages {
1606   $main::lxdebug->enter_sub();
1607
1608   my ($self, $dbh, $key) = @_;
1609
1610   $key = "all_languages" unless ($key);
1611
1612   my $query = qq|SELECT * FROM language ORDER BY id|;
1613
1614   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
1615
1616   $main::lxdebug->leave_sub();
1617 }
1618
1619 sub _get_dunning_configs {
1620   $main::lxdebug->enter_sub();
1621
1622   my ($self, $dbh, $key) = @_;
1623
1624   $key = "all_dunning_configs" unless ($key);
1625
1626   my $query = qq|SELECT * FROM dunning_config ORDER BY dunning_level|;
1627
1628   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
1629
1630   $main::lxdebug->leave_sub();
1631 }
1632
1633 sub _get_currencies {
1634 $main::lxdebug->enter_sub();
1635
1636   my ($self, $dbh, $key) = @_;
1637
1638   $key = "all_currencies" unless ($key);
1639
1640   my $query = qq|SELECT curr AS currency FROM defaults|;
1641  
1642   $self->{$key} = [split(/\:/ , selectfirst_hashref_query($self, $dbh, $query)->{currency})];
1643
1644   $main::lxdebug->leave_sub();
1645 }
1646
1647 sub _get_customers {
1648   $main::lxdebug->enter_sub();
1649
1650   my ($self, $dbh, $key) = @_;
1651
1652   $key = "all_customers" unless ($key);
1653
1654   my $query = qq|SELECT * FROM customer LIMIT $main::myconfig{vclimit}|;
1655
1656   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
1657
1658   $main::lxdebug->leave_sub();
1659 }
1660
1661 sub _get_vendors {
1662   $main::lxdebug->enter_sub();
1663
1664   my ($self, $dbh, $key) = @_;
1665
1666   $key = "all_vendors" unless ($key);
1667
1668   my $query = qq|SELECT * FROM vendor|; # LIMIT $main::myconfig{vclimit}|;
1669
1670   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
1671
1672   $main::lxdebug->leave_sub();
1673 }
1674
1675 sub get_lists {
1676   $main::lxdebug->enter_sub();
1677
1678   my $self = shift;
1679   my %params = @_;
1680
1681   my $dbh = $self->dbconnect(\%main::myconfig);
1682   my ($sth, $query, $ref);
1683
1684   my $vc = $self->{"vc"} eq "customer" ? "customer" : "vendor";
1685   my $vc_id = $self->{"${vc}_id"};
1686
1687   if ($params{"contacts"}) {
1688     $self->_get_contacts($dbh, $vc_id, $params{"contacts"});
1689   }
1690
1691   if ($params{"shipto"}) {
1692     $self->_get_shipto($dbh, $vc_id, $params{"shipto"});
1693   }
1694
1695   if ($params{"projects"} || $params{"all_projects"}) {
1696     $self->_get_projects($dbh, $params{"all_projects"} ?
1697                          $params{"all_projects"} : $params{"projects"},
1698                          $params{"all_projects"} ? 1 : 0);
1699   }
1700
1701   if ($params{"printers"}) {
1702     $self->_get_printers($dbh, $params{"printers"});
1703   }
1704
1705   if ($params{"languages"}) {
1706     $self->_get_languages($dbh, $params{"languages"});
1707   }
1708
1709   if ($params{"charts"}) {
1710     $self->_get_charts($dbh, $params{"charts"});
1711   }
1712
1713   if ($params{"taxcharts"}) {
1714     $self->_get_taxcharts($dbh, $params{"taxcharts"});
1715   }
1716
1717   if ($params{"taxzones"}) {
1718     $self->_get_taxzones($dbh, $params{"taxzones"});
1719   }
1720
1721   if ($params{"employees"}) {
1722     $self->_get_employees($dbh, $params{"employees"});
1723   }
1724
1725   if ($params{"business_types"}) {
1726     $self->_get_business_types($dbh, $params{"business_types"});
1727   }
1728
1729   if ($params{"dunning_configs"}) {
1730     $self->_get_dunning_configs($dbh, $params{"dunning_configs"});
1731   }
1732   
1733   if($params{"currencies"}) {
1734     $self->_get_currencies($dbh, $params{"currencies"});
1735   }
1736   
1737   if($params{"customers"}) {
1738     $self->_get_customers($dbh, $params{"customers"});
1739   }
1740   
1741   if($params{"vendors"}) {
1742     $self->_get_vendors($dbh, $params{"vendors"});
1743   }
1744
1745   $dbh->disconnect();
1746
1747   $main::lxdebug->leave_sub();
1748 }
1749
1750 # this sub gets the id and name from $table
1751 sub get_name {
1752   $main::lxdebug->enter_sub();
1753
1754   my ($self, $myconfig, $table) = @_;
1755
1756   # connect to database
1757   my $dbh = $self->dbconnect($myconfig);
1758
1759   $table = $table eq "customer" ? "customer" : "vendor";
1760   my $arap = $self->{arap} eq "ar" ? "ar" : "ap";
1761
1762   my ($query, @values);
1763
1764   if (!$self->{openinvoices}) {
1765     my $where;
1766     if ($self->{customernumber} ne "") {
1767       $where = qq|(vc.customernumber ILIKE ?)|;
1768       push(@values, '%' . $self->{customernumber} . '%');
1769     } else {
1770       $where = qq|(vc.name ILIKE ?)|;
1771       push(@values, '%' . $self->{$table} . '%');
1772     }
1773
1774     $query =
1775       qq~SELECT vc.id, vc.name,
1776            vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
1777          FROM $table vc
1778          WHERE $where AND (NOT vc.obsolete)
1779          ORDER BY vc.name~;
1780   } else {
1781     $query =
1782       qq~SELECT DISTINCT vc.id, vc.name,
1783            vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
1784          FROM $arap a
1785          JOIN $table vc ON (a.${table}_id = vc.id)
1786          WHERE NOT (a.amount = a.paid) AND (vc.name ILIKE ?)
1787          ORDER BY vc.name~;
1788     push(@values, '%' . $self->{$table} . '%');
1789   }
1790
1791   $self->{name_list} = selectall_hashref_query($self, $dbh, $query, @values);
1792
1793   $main::lxdebug->leave_sub();
1794
1795   return scalar(@{ $self->{name_list} });
1796 }
1797
1798 # the selection sub is used in the AR, AP, IS, IR and OE module
1799 #
1800 sub all_vc {
1801   $main::lxdebug->enter_sub();
1802
1803   my ($self, $myconfig, $table, $module) = @_;
1804
1805   my $ref;
1806   my $dbh = $self->dbconnect($myconfig);
1807
1808   $table = $table eq "customer" ? "customer" : "vendor";
1809
1810   my $query = qq|SELECT count(*) FROM $table|;
1811   my ($count) = selectrow_query($self, $dbh, $query);
1812
1813   # build selection list
1814   if ($count < $myconfig->{vclimit}) {
1815     $query = qq|SELECT id, name, salesman_id
1816                 FROM $table WHERE NOT obsolete
1817                 ORDER BY name|;
1818     $self->{"all_$table"} = selectall_hashref_query($self, $dbh, $query);
1819   }
1820
1821   # get self
1822   $self->get_employee($dbh);
1823
1824   # setup sales contacts
1825   $query = qq|SELECT e.id, e.name
1826               FROM employee e
1827               WHERE (e.sales = '1') AND (NOT e.id = ?)|;
1828   $self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
1829
1830   # this is for self
1831   push(@{ $self->{all_employees} },
1832        { id   => $self->{employee_id},
1833          name => $self->{employee} });
1834
1835   # sort the whole thing
1836   @{ $self->{all_employees} } =
1837     sort { $a->{name} cmp $b->{name} } @{ $self->{all_employees} };
1838
1839   if ($module eq 'AR') {
1840
1841     # prepare query for departments
1842     $query = qq|SELECT id, description
1843                 FROM department
1844                 WHERE role = 'P'
1845                 ORDER BY description|;
1846
1847   } else {
1848     $query = qq|SELECT id, description
1849                 FROM department
1850                 ORDER BY description|;
1851   }
1852
1853   $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
1854
1855   # get languages
1856   $query = qq|SELECT id, description
1857               FROM language
1858               ORDER BY id|;
1859
1860   $self->{languages} = selectall_hashref_query($self, $dbh, $query);
1861
1862   # get printer
1863   $query = qq|SELECT printer_description, id
1864               FROM printers
1865               ORDER BY printer_description|;
1866
1867   $self->{printers} = selectall_hashref_query($self, $dbh, $query);
1868
1869   # get payment terms
1870   $query = qq|SELECT id, description
1871               FROM payment_terms
1872               ORDER BY sortkey|;
1873
1874   $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
1875
1876   $dbh->disconnect;
1877
1878   $main::lxdebug->leave_sub();
1879 }
1880
1881 sub language_payment {
1882   $main::lxdebug->enter_sub();
1883
1884   my ($self, $myconfig) = @_;
1885
1886   my $dbh = $self->dbconnect($myconfig);
1887   # get languages
1888   my $query = qq|SELECT id, description
1889                  FROM language
1890                  ORDER BY id|;
1891
1892   $self->{languages} = selectall_hashref_query($self, $dbh, $query);
1893
1894   # get printer
1895   $query = qq|SELECT printer_description, id
1896               FROM printers
1897               ORDER BY printer_description|;
1898
1899   $self->{printers} = selectall_hashref_query($self, $dbh, $query);
1900
1901   # get payment terms
1902   $query = qq|SELECT id, description
1903               FROM payment_terms
1904               ORDER BY sortkey|;
1905
1906   $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
1907
1908   # get buchungsgruppen
1909   $query = qq|SELECT id, description
1910               FROM buchungsgruppen|;
1911
1912   $self->{BUCHUNGSGRUPPEN} = selectall_hashref_query($self, $dbh, $query);
1913
1914   $dbh->disconnect;
1915   $main::lxdebug->leave_sub();
1916 }
1917
1918 # this is only used for reports
1919 sub all_departments {
1920   $main::lxdebug->enter_sub();
1921
1922   my ($self, $myconfig, $table) = @_;
1923
1924   my $dbh = $self->dbconnect($myconfig);
1925   my $where;
1926
1927   if ($table eq 'customer') {
1928     $where = "WHERE role = 'P' ";
1929   }
1930
1931   my $query = qq|SELECT id, description
1932                  FROM department
1933                  $where
1934                  ORDER BY description|;
1935   $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
1936
1937   delete($self->{all_departments}) unless (@{ $self->{all_departments} });
1938
1939   $dbh->disconnect;
1940
1941   $main::lxdebug->leave_sub();
1942 }
1943
1944 sub create_links {
1945   $main::lxdebug->enter_sub();
1946
1947   my ($self, $module, $myconfig, $table) = @_;
1948
1949   my ($fld, $arap);
1950   if ($table eq "customer") {
1951     $fld = "buy";
1952     $arap = "ar";
1953   } else {
1954     $table = "vendor";
1955     $fld = "sell";
1956     $arap = "ap";
1957   }
1958
1959   $self->all_vc($myconfig, $table, $module);
1960
1961   # get last customers or vendors
1962   my ($query, $sth, $ref);
1963
1964   my $dbh = $self->dbconnect($myconfig);
1965   my %xkeyref = ();
1966
1967   if (!$self->{id}) {
1968
1969     my $transdate = "current_date";
1970     if ($self->{transdate}) {
1971       $transdate = $dbh->quote($self->{transdate});
1972     }
1973
1974     # now get the account numbers
1975     $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
1976                 FROM chart c, taxkeys tk
1977                 WHERE (c.link LIKE ?) AND (c.id = tk.chart_id) AND tk.id =
1978                   (SELECT id FROM taxkeys WHERE (taxkeys.chart_id = c.id) AND (startdate <= $transdate) ORDER BY startdate DESC LIMIT 1)
1979                 ORDER BY c.accno|;
1980
1981     $sth = $dbh->prepare($query);
1982
1983     do_statement($self, $sth, $query, '%' . $module . '%');
1984
1985     $self->{accounts} = "";
1986     while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
1987
1988       foreach my $key (split(/:/, $ref->{link})) {
1989         if ($key =~ /$module/) {
1990
1991           # cross reference for keys
1992           $xkeyref{ $ref->{accno} } = $key;
1993
1994           push @{ $self->{"${module}_links"}{$key} },
1995             { accno       => $ref->{accno},
1996               description => $ref->{description},
1997               taxkey      => $ref->{taxkey_id},
1998               tax_id      => $ref->{tax_id} };
1999
2000           $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
2001         }
2002       }
2003     }
2004   }
2005
2006   # get taxkeys and description
2007   $query = qq|SELECT id, taxkey, taxdescription FROM tax|;
2008   $self->{TAXKEY} = selectall_hashref_query($self, $dbh, $query);
2009
2010   if (($module eq "AP") || ($module eq "AR")) {
2011     # get tax rates and description
2012     $query = qq|SELECT * FROM tax|;
2013     $self->{TAX} = selectall_hashref_query($self, $dbh, $query);
2014   }
2015
2016   if ($self->{id}) {
2017     $query =
2018       qq|SELECT
2019            a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid,
2020            a.duedate, a.ordnumber, a.taxincluded, a.curr AS currency, a.notes,
2021            a.intnotes, a.department_id, a.amount AS oldinvtotal,
2022            a.paid AS oldtotalpaid, a.employee_id, a.gldate, a.type,
2023            c.name AS $table,
2024            d.description AS department,
2025            e.name AS employee
2026          FROM $arap a
2027          JOIN $table c ON (a.${table}_id = c.id)
2028          LEFT JOIN employee e ON (e.id = a.employee_id)
2029          LEFT JOIN department d ON (d.id = a.department_id)
2030          WHERE a.id = ?|;
2031     $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{id});
2032
2033     foreach $key (keys %$ref) {
2034       $self->{$key} = $ref->{$key};
2035     }
2036
2037     my $transdate = "current_date";
2038     if ($self->{transdate}) {
2039       $transdate = $dbh->quote($self->{transdate});
2040     }
2041
2042     # now get the account numbers
2043     $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
2044                 FROM chart c
2045                 LEFT JOIN taxkeys tk ON (tk.chart_id = c.id)
2046                 WHERE c.link LIKE ?
2047                   AND (tk.id = (SELECT id FROM taxkeys WHERE taxkeys.chart_id = c.id AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1)
2048                     OR c.link LIKE '%_tax%')
2049                 ORDER BY c.accno|;
2050
2051     $sth = $dbh->prepare($query);
2052     do_statement($self, $sth, $query, "%$module%");
2053
2054     $self->{accounts} = "";
2055     while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
2056
2057       foreach my $key (split(/:/, $ref->{link})) {
2058         if ($key =~ /$module/) {
2059
2060           # cross reference for keys
2061           $xkeyref{ $ref->{accno} } = $key;
2062
2063           push @{ $self->{"${module}_links"}{$key} },
2064             { accno       => $ref->{accno},
2065               description => $ref->{description},
2066               taxkey      => $ref->{taxkey_id},
2067               tax_id      => $ref->{tax_id} };
2068
2069           $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
2070         }
2071       }
2072     }
2073
2074
2075     # get amounts from individual entries
2076     $query =
2077       qq|SELECT
2078            c.accno, c.description,
2079            a.source, a.amount, a.memo, a.transdate, a.cleared, a.project_id, a.taxkey,
2080            p.projectnumber,
2081            t.rate, t.id
2082          FROM acc_trans a
2083          LEFT JOIN chart c ON (c.id = a.chart_id)
2084          LEFT JOIN project p ON (p.id = a.project_id)
2085          LEFT JOIN tax t ON (t.id= (SELECT tk.tax_id FROM taxkeys tk
2086                                     WHERE (tk.taxkey_id=a.taxkey) AND
2087                                       ((CASE WHEN a.chart_id IN (SELECT chart_id FROM taxkeys WHERE taxkey_id = a.taxkey)
2088                                         THEN tk.chart_id = a.chart_id
2089                                         ELSE 1 = 1
2090                                         END)
2091                                        OR (c.link='%tax%')) AND
2092                                       (startdate <= a.transdate) ORDER BY startdate DESC LIMIT 1))
2093          WHERE a.trans_id = ?
2094          AND a.fx_transaction = '0'
2095          ORDER BY a.oid, a.transdate|;
2096     $sth = $dbh->prepare($query);
2097     do_statement($self, $sth, $query, $self->{id});
2098
2099     # get exchangerate for currency
2100     $self->{exchangerate} =
2101       $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
2102     my $index = 0;
2103
2104     # store amounts in {acc_trans}{$key} for multiple accounts
2105     while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
2106       $ref->{exchangerate} =
2107         $self->get_exchangerate($dbh, $self->{currency}, $ref->{transdate}, $fld);
2108       if (!($xkeyref{ $ref->{accno} } =~ /tax/)) {
2109         $index++;
2110       }
2111       if (($xkeyref{ $ref->{accno} } =~ /paid/) && ($self->{type} eq "credit_note")) {
2112         $ref->{amount} *= -1;
2113       }
2114       $ref->{index} = $index;
2115
2116       push @{ $self->{acc_trans}{ $xkeyref{ $ref->{accno} } } }, $ref;
2117     }
2118
2119     $sth->finish;
2120     $query =
2121       qq|SELECT
2122            d.curr AS currencies, d.closedto, d.revtrans,
2123            (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
2124            (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
2125          FROM defaults d|;
2126     $ref = selectfirst_hashref_query($self, $dbh, $query);
2127     map { $self->{$_} = $ref->{$_} } keys %$ref;
2128
2129   } else {
2130
2131     # get date
2132     $query =
2133        qq|SELECT
2134             current_date AS transdate, d.curr AS currencies, d.closedto, d.revtrans,
2135             (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
2136             (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
2137           FROM defaults d|;
2138     $ref = selectfirst_hashref_query($self, $dbh, $query);
2139     map { $self->{$_} = $ref->{$_} } keys %$ref;
2140
2141     if ($self->{"$self->{vc}_id"}) {
2142
2143       # only setup currency
2144       ($self->{currency}) = split(/:/, $self->{currencies});
2145
2146     } else {
2147
2148       $self->lastname_used($dbh, $myconfig, $table, $module);
2149
2150       # get exchangerate for currency
2151       $self->{exchangerate} =
2152         $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
2153
2154     }
2155
2156   }
2157
2158   $dbh->disconnect;
2159
2160   $main::lxdebug->leave_sub();
2161 }
2162
2163 sub lastname_used {
2164   $main::lxdebug->enter_sub();
2165
2166   my ($self, $dbh, $myconfig, $table, $module) = @_;
2167
2168   my $arap  = ($table eq 'customer') ? "ar" : "ap";
2169   $table = $table eq "customer" ? "customer" : "vendor";
2170   my $where = "1 = 1";
2171
2172   if ($self->{type} =~ /_order/) {
2173     $arap  = 'oe';
2174     $where = "quotation = '0'";
2175   }
2176   if ($self->{type} =~ /_quotation/) {
2177     $arap  = 'oe';
2178     $where = "quotation = '1'";
2179   }
2180
2181   my $query = qq|SELECT MAX(id) FROM $arap
2182                  WHERE $where AND ${table}_id > 0|;
2183   my ($trans_id) = selectrow_query($self, $dbh, $query);
2184
2185   $trans_id *= 1;
2186   $query =
2187     qq|SELECT
2188          a.curr, a.${table}_id, a.department_id,
2189          d.description AS department,
2190          ct.name, current_date + ct.terms AS duedate
2191        FROM $arap a
2192        LEFT JOIN $table ct ON (a.${table}_id = ct.id)
2193        LEFT JOIN department d ON (a.department_id = d.id)
2194        WHERE a.id = ?|;
2195   ($self->{currency},   $self->{"${table}_id"}, $self->{department_id},
2196    $self->{department}, $self->{$table},        $self->{duedate})
2197     = selectrow_query($self, $dbh, $query, $trans_id);
2198
2199   $main::lxdebug->leave_sub();
2200 }
2201
2202 sub current_date {
2203   $main::lxdebug->enter_sub();
2204
2205   my ($self, $myconfig, $thisdate, $days) = @_;
2206
2207   my $dbh = $self->dbconnect($myconfig);
2208   my $query;
2209
2210   $days *= 1;
2211   if ($thisdate) {
2212     my $dateformat = $myconfig->{dateformat};
2213     $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
2214     $thisdate = $dbh->quote($thisdate);
2215     $query = qq|SELECT to_date($thisdate, '$dateformat') + $days AS thisdate|;
2216   } else {
2217     $query = qq|SELECT current_date AS thisdate|;
2218   }
2219
2220   ($thisdate) = selectrow_query($self, $dbh, $query);
2221
2222   $dbh->disconnect;
2223
2224   $main::lxdebug->leave_sub();
2225
2226   return $thisdate;
2227 }
2228
2229 sub like {
2230   $main::lxdebug->enter_sub();
2231
2232   my ($self, $string) = @_;
2233
2234   if ($string !~ /%/) {
2235     $string = "%$string%";
2236   }
2237
2238   $string =~ s/\'/\'\'/g;
2239
2240   $main::lxdebug->leave_sub();
2241
2242   return $string;
2243 }
2244
2245 sub redo_rows {
2246   $main::lxdebug->enter_sub();
2247
2248   my ($self, $flds, $new, $count, $numrows) = @_;
2249
2250   my @ndx = ();
2251
2252   map { push @ndx, { num => $new->[$_ - 1]->{runningnumber}, ndx => $_ } }
2253     (1 .. $count);
2254
2255   my $i = 0;
2256
2257   # fill rows
2258   foreach my $item (sort { $a->{num} <=> $b->{num} } @ndx) {
2259     $i++;
2260     $j = $item->{ndx} - 1;
2261     map { $self->{"${_}_$i"} = $new->[$j]->{$_} } @{$flds};
2262   }
2263
2264   # delete empty rows
2265   for $i ($count + 1 .. $numrows) {
2266     map { delete $self->{"${_}_$i"} } @{$flds};
2267   }
2268
2269   $main::lxdebug->leave_sub();
2270 }
2271
2272 sub update_status {
2273   $main::lxdebug->enter_sub();
2274
2275   my ($self, $myconfig) = @_;
2276
2277   my ($i, $id);
2278
2279   my $dbh = $self->dbconnect_noauto($myconfig);
2280
2281   my $query = qq|DELETE FROM status
2282                  WHERE (formname = ?) AND (trans_id = ?)|;
2283   my $sth = prepare_query($self, $dbh, $query);
2284
2285   if ($self->{formname} =~ /(check|receipt)/) {
2286     for $i (1 .. $self->{rowcount}) {
2287       do_statement($self, $sth, $query, $self->{formname}, $self->{"id_$i"} * 1);
2288     }
2289   } else {
2290     do_statement($self, $sth, $query, $self->{formname}, $self->{id});
2291   }
2292   $sth->finish();
2293
2294   my $printed = ($self->{printed} =~ /$self->{formname}/) ? "1" : "0";
2295   my $emailed = ($self->{emailed} =~ /$self->{formname}/) ? "1" : "0";
2296
2297   my %queued = split / /, $self->{queued};
2298   my @values;
2299
2300   if ($self->{formname} =~ /(check|receipt)/) {
2301
2302     # this is a check or receipt, add one entry for each lineitem
2303     my ($accno) = split /--/, $self->{account};
2304     $query = qq|INSERT INTO status (trans_id, printed, spoolfile, formname, chart_id)
2305                 VALUES (?, ?, ?, ?, (SELECT c.id FROM chart c WHERE c.accno = ?))|;
2306     @values = ($printed, $queued{$self->{formname}}, $self->{prinform}, $accno);
2307     $sth = prepare_query($self, $dbh, $query);
2308
2309     for $i (1 .. $self->{rowcount}) {
2310       if ($self->{"checked_$i"}) {
2311         do_statement($self, $sth, $query, $self->{"id_$i"}, @values);
2312       }
2313     }
2314     $sth->finish();
2315
2316   } else {
2317     $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
2318                 VALUES (?, ?, ?, ?, ?)|;
2319     do_query($self, $dbh, $query, $self->{id}, $printed, $emailed,
2320              $queued{$self->{formname}}, $self->{formname});
2321   }
2322
2323   $dbh->commit;
2324   $dbh->disconnect;
2325
2326   $main::lxdebug->leave_sub();
2327 }
2328
2329 sub save_status {
2330   $main::lxdebug->enter_sub();
2331
2332   my ($self, $dbh) = @_;
2333
2334   my ($query, $printed, $emailed);
2335
2336   my $formnames  = $self->{printed};
2337   my $emailforms = $self->{emailed};
2338
2339   my $query = qq|DELETE FROM status
2340                  WHERE (formname = ?) AND (trans_id = ?)|;
2341   do_query($self, $dbh, $query, $self->{formname}, $self->{id});
2342
2343   # this only applies to the forms
2344   # checks and receipts are posted when printed or queued
2345
2346   if ($self->{queued}) {
2347     my %queued = split / /, $self->{queued};
2348
2349     foreach my $formname (keys %queued) {
2350       $printed = ($self->{printed} =~ /$self->{formname}/) ? "1" : "0";
2351       $emailed = ($self->{emailed} =~ /$self->{formname}/) ? "1" : "0";
2352
2353       $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
2354                   VALUES (?, ?, ?, ?, ?)|;
2355       do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $queued{$formname}, $formname);
2356
2357       $formnames  =~ s/$self->{formname}//;
2358       $emailforms =~ s/$self->{formname}//;
2359
2360     }
2361   }
2362
2363   # save printed, emailed info
2364   $formnames  =~ s/^ +//g;
2365   $emailforms =~ s/^ +//g;
2366
2367   my %status = ();
2368   map { $status{$_}{printed} = 1 } split / +/, $formnames;
2369   map { $status{$_}{emailed} = 1 } split / +/, $emailforms;
2370
2371   foreach my $formname (keys %status) {
2372     $printed = ($formnames  =~ /$self->{formname}/) ? "1" : "0";
2373     $emailed = ($emailforms =~ /$self->{formname}/) ? "1" : "0";
2374
2375     $query = qq|INSERT INTO status (trans_id, printed, emailed, formname)
2376                 VALUES (?, ?, ?, ?)|;
2377     do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $formname);
2378   }
2379
2380   $main::lxdebug->leave_sub();
2381 }
2382
2383 #--- 4 locale ---#
2384 # $main::locale->text('SAVED')
2385 # $main::locale->text('DELETED')
2386 # $main::locale->text('ADDED')
2387 # $main::locale->text('PAYMENT POSTED')
2388 # $main::locale->text('POSTED')
2389 # $main::locale->text('POSTED AS NEW')
2390 # $main::locale->text('ELSE')
2391 # $main::locale->text('SAVED FOR DUNNING')
2392 # $main::locale->text('DUNNING STARTED')
2393 # $main::locale->text('PRINTED')
2394 # $main::locale->text('MAILED')
2395 # $main::locale->text('SCREENED')
2396 # $main::locale->text('CANCELED')
2397 # $main::locale->text('invoice')
2398 # $main::locale->text('proforma')
2399 # $main::locale->text('sales_order')
2400 # $main::locale->text('packing_list')
2401 # $main::locale->text('pick_list')
2402 # $main::locale->text('purchase_order')
2403 # $main::locale->text('bin_list')
2404 # $main::locale->text('sales_quotation')
2405 # $main::locale->text('request_quotation')
2406
2407 sub save_history {
2408   $main::lxdebug->enter_sub();
2409
2410   my $self = shift();
2411   my $dbh = shift();
2412
2413   if(!exists $self->{employee_id}) {
2414     &get_employee($self, $dbh);
2415   }
2416
2417   my $query =
2418     qq|INSERT INTO history_erp (trans_id, employee_id, addition, what_done, snumbers) | .
2419     qq|VALUES (?, ?, ?, ?, ?)|;
2420   my @values = (conv_i($self->{id}), conv_i($self->{employee_id}),
2421                 $self->{addition}, $self->{what_done}, "$self->{snumbers}");
2422   do_query($self, $dbh, $query, @values);
2423
2424   $main::lxdebug->leave_sub();
2425 }
2426
2427 sub get_history {
2428   $main::lxdebug->enter_sub();
2429
2430   my $self = shift();
2431   my $dbh = shift();
2432   my $trans_id = shift();
2433   my $restriction = shift();
2434   my @tempArray;
2435   my $i = 0;
2436   if ($trans_id ne "") {
2437     my $query =
2438       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 | .
2439       qq|FROM history_erp h | .
2440       qq|LEFT JOIN employee emp ON (emp.id = h.employee_id) | .
2441       qq|WHERE trans_id = ? |
2442       . $restriction;
2443
2444     my $sth = $dbh->prepare($query) || $self->dberror($query);
2445
2446     $sth->execute($trans_id) || $self->dberror("$query ($trans_id)");
2447
2448     while(my $hash_ref = $sth->fetchrow_hashref()) {
2449       $hash_ref->{addition} = $main::locale->text($hash_ref->{addition});
2450       $hash_ref->{what_done} = $main::locale->text($hash_ref->{what_done});
2451       $hash_ref->{snumbers} =~ s/^.+_(.*)$/$1/g;
2452       $tempArray[$i++] = $hash_ref;
2453     }
2454     $main::lxdebug->leave_sub() and return \@tempArray 
2455       if ($i > 0 && $tempArray[0] ne "");
2456   }
2457   $main::lxdebug->leave_sub();
2458   return 0;
2459 }
2460
2461 sub update_defaults {
2462   $main::lxdebug->enter_sub();
2463
2464   my ($self, $myconfig, $fld, $provided_dbh) = @_;
2465
2466   my $dbh;
2467   if ($provided_dbh) {
2468     $dbh = $provided_dbh;
2469   } else {
2470     $dbh = $self->dbconnect_noauto($myconfig);
2471   }
2472   my $query = qq|SELECT $fld FROM defaults FOR UPDATE|;
2473   my $sth   = $dbh->prepare($query);
2474
2475   $sth->execute || $self->dberror($query);
2476   my ($var) = $sth->fetchrow_array;
2477   $sth->finish;
2478
2479   $var =~ s/\d+$/ sprintf '%0*d', length($&), $&+1 /e;
2480   $var ||= 1;
2481
2482   $query = qq|UPDATE defaults SET $fld = ?|;
2483   do_query($self, $dbh, $query, $var);
2484
2485   if (!$provided_dbh) {
2486     $dbh->commit;
2487     $dbh->disconnect;
2488   }
2489
2490   $main::lxdebug->leave_sub();
2491
2492   return $var;
2493 }
2494
2495 sub update_business {
2496   $main::lxdebug->enter_sub();
2497
2498   my ($self, $myconfig, $business_id, $provided_dbh) = @_;
2499
2500   my $dbh;
2501   if ($provided_dbh) {
2502     $dbh = $provided_dbh;
2503   } else {
2504     $dbh = $self->dbconnect_noauto($myconfig);
2505   }
2506   my $query =
2507     qq|SELECT customernumberinit FROM business
2508        WHERE id = ? FOR UPDATE|;
2509   my ($var) = selectrow_query($self, $dbh, $query, $business_id);
2510
2511   $var =~ s/\d+$/ sprintf '%0*d', length($&), $&+1 /e;
2512   
2513   $query = qq|UPDATE business
2514               SET customernumberinit = ?
2515               WHERE id = ?|;
2516   do_query($self, $dbh, $query, $var, $business_id);
2517
2518   if (!$provided_dbh) {
2519     $dbh->commit;
2520     $dbh->disconnect;
2521   }
2522
2523   $main::lxdebug->leave_sub();
2524
2525   return $var;
2526 }
2527
2528 sub get_partsgroup {
2529   $main::lxdebug->enter_sub();
2530
2531   my ($self, $myconfig, $p) = @_;
2532
2533   my $dbh = $self->dbconnect($myconfig);
2534
2535   my $query = qq|SELECT DISTINCT pg.id, pg.partsgroup
2536                  FROM partsgroup pg
2537                  JOIN parts p ON (p.partsgroup_id = pg.id) |;
2538   my @values;
2539
2540   if ($p->{searchitems} eq 'part') {
2541     $query .= qq|WHERE p.inventory_accno_id > 0|;
2542   }
2543   if ($p->{searchitems} eq 'service') {
2544     $query .= qq|WHERE p.inventory_accno_id IS NULL|;
2545   }
2546   if ($p->{searchitems} eq 'assembly') {
2547     $query .= qq|WHERE p.assembly = '1'|;
2548   }
2549   if ($p->{searchitems} eq 'labor') {
2550     $query .= qq|WHERE (p.inventory_accno_id > 0) AND (p.income_accno_id IS NULL)|;
2551   }
2552
2553   $query .= qq|ORDER BY partsgroup|;
2554
2555   if ($p->{all}) {
2556     $query = qq|SELECT id, partsgroup FROM partsgroup
2557                 ORDER BY partsgroup|;
2558   }
2559
2560   if ($p->{language_code}) {
2561     $query = qq|SELECT DISTINCT pg.id, pg.partsgroup,
2562                   t.description AS translation
2563                 FROM partsgroup pg
2564                 JOIN parts p ON (p.partsgroup_id = pg.id)
2565                 LEFT JOIN translation t ON ((t.trans_id = pg.id) AND (t.language_code = ?))
2566                 ORDER BY translation|;
2567     @values = ($p->{language_code});
2568   }
2569
2570   $self->{all_partsgroup} = selectall_hashref_query($self, $dbh, $query, @values);
2571
2572   $dbh->disconnect;
2573   $main::lxdebug->leave_sub();
2574 }
2575
2576 sub get_pricegroup {
2577   $main::lxdebug->enter_sub();
2578
2579   my ($self, $myconfig, $p) = @_;
2580
2581   my $dbh = $self->dbconnect($myconfig);
2582
2583   my $query = qq|SELECT p.id, p.pricegroup
2584                  FROM pricegroup p|;
2585
2586   $query .= qq| ORDER BY pricegroup|;
2587
2588   if ($p->{all}) {
2589     $query = qq|SELECT id, pricegroup FROM pricegroup
2590                 ORDER BY pricegroup|;
2591   }
2592
2593   $self->{all_pricegroup} = selectall_hashref_query($self, $dbh, $query);
2594
2595   $dbh->disconnect;
2596
2597   $main::lxdebug->leave_sub();
2598 }
2599
2600 sub all_years {
2601 # usage $form->all_years($myconfig, [$dbh])
2602 # return list of all years where bookings found
2603 # (@all_years)
2604
2605   $main::lxdebug->enter_sub();
2606
2607   my ($self, $myconfig, $dbh) = @_;
2608
2609   my $disconnect = 0;
2610   if (! $dbh) {
2611     $dbh = $self->dbconnect($myconfig);
2612     $disconnect = 1;
2613   }
2614
2615   # get years
2616   my $query = qq|SELECT (SELECT MIN(transdate) FROM acc_trans),
2617                    (SELECT MAX(transdate) FROM acc_trans)|;
2618   my ($startdate, $enddate) = selectrow_query($self, $dbh, $query);
2619
2620   if ($myconfig->{dateformat} =~ /^yy/) {
2621     ($startdate) = split /\W/, $startdate;
2622     ($enddate) = split /\W/, $enddate;
2623   } else {
2624     (@_) = split /\W/, $startdate;
2625     $startdate = $_[2];
2626     (@_) = split /\W/, $enddate;
2627     $enddate = $_[2];
2628   }
2629
2630   my @all_years;
2631   $startdate = substr($startdate,0,4);
2632   $enddate = substr($enddate,0,4);
2633
2634   while ($enddate >= $startdate) {
2635     push @all_years, $enddate--;
2636   }
2637
2638   $dbh->disconnect if $disconnect;
2639
2640   return @all_years;
2641
2642   $main::lxdebug->leave_sub();
2643 }
2644
2645
2646 1;