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