6aedf1d6e5a6131b3995791449e19b15deee1f47
[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_payments {
1648 $main::lxdebug->enter_sub();
1649
1650   my ($self, $dbh, $key) = @_;
1651
1652   $key = "all_payments" unless ($key);
1653
1654   my $query = qq|SELECT * FROM payment_terms ORDER BY id|;
1655  
1656   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
1657
1658   $main::lxdebug->leave_sub();
1659 }
1660
1661 sub _get_customers {
1662   $main::lxdebug->enter_sub();
1663
1664   my ($self, $dbh, $key) = @_;
1665
1666   $key = "all_customers" unless ($key);
1667
1668   my $query = qq|SELECT * FROM customer 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_vendors {
1676   $main::lxdebug->enter_sub();
1677
1678   my ($self, $dbh, $key) = @_;
1679
1680   $key = "all_vendors" unless ($key);
1681
1682   my $query = qq|SELECT * FROM vendor|; # LIMIT $main::myconfig{vclimit}|;
1683
1684   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
1685
1686   $main::lxdebug->leave_sub();
1687 }
1688
1689 sub get_lists {
1690   $main::lxdebug->enter_sub();
1691
1692   my $self = shift;
1693   my %params = @_;
1694
1695   my $dbh = $self->dbconnect(\%main::myconfig);
1696   my ($sth, $query, $ref);
1697
1698   my $vc = $self->{"vc"} eq "customer" ? "customer" : "vendor";
1699   my $vc_id = $self->{"${vc}_id"};
1700
1701   if ($params{"contacts"}) {
1702     $self->_get_contacts($dbh, $vc_id, $params{"contacts"});
1703   }
1704
1705   if ($params{"shipto"}) {
1706     $self->_get_shipto($dbh, $vc_id, $params{"shipto"});
1707   }
1708
1709   if ($params{"projects"} || $params{"all_projects"}) {
1710     $self->_get_projects($dbh, $params{"all_projects"} ?
1711                          $params{"all_projects"} : $params{"projects"},
1712                          $params{"all_projects"} ? 1 : 0);
1713   }
1714
1715   if ($params{"printers"}) {
1716     $self->_get_printers($dbh, $params{"printers"});
1717   }
1718
1719   if ($params{"languages"}) {
1720     $self->_get_languages($dbh, $params{"languages"});
1721   }
1722
1723   if ($params{"charts"}) {
1724     $self->_get_charts($dbh, $params{"charts"});
1725   }
1726
1727   if ($params{"taxcharts"}) {
1728     $self->_get_taxcharts($dbh, $params{"taxcharts"});
1729   }
1730
1731   if ($params{"taxzones"}) {
1732     $self->_get_taxzones($dbh, $params{"taxzones"});
1733   }
1734
1735   if ($params{"employees"}) {
1736     $self->_get_employees($dbh, $params{"employees"});
1737   }
1738
1739   if ($params{"business_types"}) {
1740     $self->_get_business_types($dbh, $params{"business_types"});
1741   }
1742
1743   if ($params{"dunning_configs"}) {
1744     $self->_get_dunning_configs($dbh, $params{"dunning_configs"});
1745   }
1746   
1747   if($params{"currencies"}) {
1748     $self->_get_currencies($dbh, $params{"currencies"});
1749   }
1750   
1751   if($params{"customers"}) {
1752     $self->_get_customers($dbh, $params{"customers"});
1753   }
1754   
1755   if($params{"vendors"}) {
1756     $self->_get_vendors($dbh, $params{"vendors"});
1757   }
1758   
1759   if($params{"payments"}) {
1760     $self->_get_payments($dbh, $params{"payments"});
1761   }
1762
1763   $dbh->disconnect();
1764
1765   $main::lxdebug->leave_sub();
1766 }
1767
1768 # this sub gets the id and name from $table
1769 sub get_name {
1770   $main::lxdebug->enter_sub();
1771
1772   my ($self, $myconfig, $table) = @_;
1773
1774   # connect to database
1775   my $dbh = $self->dbconnect($myconfig);
1776
1777   $table = $table eq "customer" ? "customer" : "vendor";
1778   my $arap = $self->{arap} eq "ar" ? "ar" : "ap";
1779
1780   my ($query, @values);
1781
1782   if (!$self->{openinvoices}) {
1783     my $where;
1784     if ($self->{customernumber} ne "") {
1785       $where = qq|(vc.customernumber ILIKE ?)|;
1786       push(@values, '%' . $self->{customernumber} . '%');
1787     } else {
1788       $where = qq|(vc.name ILIKE ?)|;
1789       push(@values, '%' . $self->{$table} . '%');
1790     }
1791
1792     $query =
1793       qq~SELECT vc.id, vc.name,
1794            vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
1795          FROM $table vc
1796          WHERE $where AND (NOT vc.obsolete)
1797          ORDER BY vc.name~;
1798   } else {
1799     $query =
1800       qq~SELECT DISTINCT vc.id, vc.name,
1801            vc.street || ' ' || vc.zipcode || ' ' || vc.city || ' ' || vc.country AS address
1802          FROM $arap a
1803          JOIN $table vc ON (a.${table}_id = vc.id)
1804          WHERE NOT (a.amount = a.paid) AND (vc.name ILIKE ?)
1805          ORDER BY vc.name~;
1806     push(@values, '%' . $self->{$table} . '%');
1807   }
1808
1809   $self->{name_list} = selectall_hashref_query($self, $dbh, $query, @values);
1810
1811   $main::lxdebug->leave_sub();
1812
1813   return scalar(@{ $self->{name_list} });
1814 }
1815
1816 # the selection sub is used in the AR, AP, IS, IR and OE module
1817 #
1818 sub all_vc {
1819   $main::lxdebug->enter_sub();
1820
1821   my ($self, $myconfig, $table, $module) = @_;
1822
1823   my $ref;
1824   my $dbh = $self->dbconnect($myconfig);
1825
1826   $table = $table eq "customer" ? "customer" : "vendor";
1827
1828   my $query = qq|SELECT count(*) FROM $table|;
1829   my ($count) = selectrow_query($self, $dbh, $query);
1830
1831   # build selection list
1832   if ($count < $myconfig->{vclimit}) {
1833     $query = qq|SELECT id, name, salesman_id
1834                 FROM $table WHERE NOT obsolete
1835                 ORDER BY name|;
1836     $self->{"all_$table"} = selectall_hashref_query($self, $dbh, $query);
1837   }
1838
1839   # get self
1840   $self->get_employee($dbh);
1841
1842   # setup sales contacts
1843   $query = qq|SELECT e.id, e.name
1844               FROM employee e
1845               WHERE (e.sales = '1') AND (NOT e.id = ?)|;
1846   $self->{all_employees} = selectall_hashref_query($self, $dbh, $query, $self->{employee_id});
1847
1848   # this is for self
1849   push(@{ $self->{all_employees} },
1850        { id   => $self->{employee_id},
1851          name => $self->{employee} });
1852
1853   # sort the whole thing
1854   @{ $self->{all_employees} } =
1855     sort { $a->{name} cmp $b->{name} } @{ $self->{all_employees} };
1856
1857   if ($module eq 'AR') {
1858
1859     # prepare query for departments
1860     $query = qq|SELECT id, description
1861                 FROM department
1862                 WHERE role = 'P'
1863                 ORDER BY description|;
1864
1865   } else {
1866     $query = qq|SELECT id, description
1867                 FROM department
1868                 ORDER BY description|;
1869   }
1870
1871   $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
1872
1873   # get languages
1874   $query = qq|SELECT id, description
1875               FROM language
1876               ORDER BY id|;
1877
1878   $self->{languages} = selectall_hashref_query($self, $dbh, $query);
1879
1880   # get printer
1881   $query = qq|SELECT printer_description, id
1882               FROM printers
1883               ORDER BY printer_description|;
1884
1885   $self->{printers} = selectall_hashref_query($self, $dbh, $query);
1886
1887   # get payment terms
1888   $query = qq|SELECT id, description
1889               FROM payment_terms
1890               ORDER BY sortkey|;
1891
1892   $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
1893
1894   $dbh->disconnect;
1895
1896   $main::lxdebug->leave_sub();
1897 }
1898
1899 sub language_payment {
1900   $main::lxdebug->enter_sub();
1901
1902   my ($self, $myconfig) = @_;
1903
1904   my $dbh = $self->dbconnect($myconfig);
1905   # get languages
1906   my $query = qq|SELECT id, description
1907                  FROM language
1908                  ORDER BY id|;
1909
1910   $self->{languages} = selectall_hashref_query($self, $dbh, $query);
1911
1912   # get printer
1913   $query = qq|SELECT printer_description, id
1914               FROM printers
1915               ORDER BY printer_description|;
1916
1917   $self->{printers} = selectall_hashref_query($self, $dbh, $query);
1918
1919   # get payment terms
1920   $query = qq|SELECT id, description
1921               FROM payment_terms
1922               ORDER BY sortkey|;
1923
1924   $self->{payment_terms} = selectall_hashref_query($self, $dbh, $query);
1925
1926   # get buchungsgruppen
1927   $query = qq|SELECT id, description
1928               FROM buchungsgruppen|;
1929
1930   $self->{BUCHUNGSGRUPPEN} = selectall_hashref_query($self, $dbh, $query);
1931
1932   $dbh->disconnect;
1933   $main::lxdebug->leave_sub();
1934 }
1935
1936 # this is only used for reports
1937 sub all_departments {
1938   $main::lxdebug->enter_sub();
1939
1940   my ($self, $myconfig, $table) = @_;
1941
1942   my $dbh = $self->dbconnect($myconfig);
1943   my $where;
1944
1945   if ($table eq 'customer') {
1946     $where = "WHERE role = 'P' ";
1947   }
1948
1949   my $query = qq|SELECT id, description
1950                  FROM department
1951                  $where
1952                  ORDER BY description|;
1953   $self->{all_departments} = selectall_hashref_query($self, $dbh, $query);
1954
1955   delete($self->{all_departments}) unless (@{ $self->{all_departments} });
1956
1957   $dbh->disconnect;
1958
1959   $main::lxdebug->leave_sub();
1960 }
1961
1962 sub create_links {
1963   $main::lxdebug->enter_sub();
1964
1965   my ($self, $module, $myconfig, $table) = @_;
1966
1967   my ($fld, $arap);
1968   if ($table eq "customer") {
1969     $fld = "buy";
1970     $arap = "ar";
1971   } else {
1972     $table = "vendor";
1973     $fld = "sell";
1974     $arap = "ap";
1975   }
1976
1977   $self->all_vc($myconfig, $table, $module);
1978
1979   # get last customers or vendors
1980   my ($query, $sth, $ref);
1981
1982   my $dbh = $self->dbconnect($myconfig);
1983   my %xkeyref = ();
1984
1985   if (!$self->{id}) {
1986
1987     my $transdate = "current_date";
1988     if ($self->{transdate}) {
1989       $transdate = $dbh->quote($self->{transdate});
1990     }
1991
1992     # now get the account numbers
1993     $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
1994                 FROM chart c, taxkeys tk
1995                 WHERE (c.link LIKE ?) AND (c.id = tk.chart_id) AND tk.id =
1996                   (SELECT id FROM taxkeys WHERE (taxkeys.chart_id = c.id) AND (startdate <= $transdate) ORDER BY startdate DESC LIMIT 1)
1997                 ORDER BY c.accno|;
1998
1999     $sth = $dbh->prepare($query);
2000
2001     do_statement($self, $sth, $query, '%' . $module . '%');
2002
2003     $self->{accounts} = "";
2004     while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
2005
2006       foreach my $key (split(/:/, $ref->{link})) {
2007         if ($key =~ /$module/) {
2008
2009           # cross reference for keys
2010           $xkeyref{ $ref->{accno} } = $key;
2011
2012           push @{ $self->{"${module}_links"}{$key} },
2013             { accno       => $ref->{accno},
2014               description => $ref->{description},
2015               taxkey      => $ref->{taxkey_id},
2016               tax_id      => $ref->{tax_id} };
2017
2018           $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
2019         }
2020       }
2021     }
2022   }
2023
2024   # get taxkeys and description
2025   $query = qq|SELECT id, taxkey, taxdescription FROM tax|;
2026   $self->{TAXKEY} = selectall_hashref_query($self, $dbh, $query);
2027
2028   if (($module eq "AP") || ($module eq "AR")) {
2029     # get tax rates and description
2030     $query = qq|SELECT * FROM tax|;
2031     $self->{TAX} = selectall_hashref_query($self, $dbh, $query);
2032   }
2033
2034   if ($self->{id}) {
2035     $query =
2036       qq|SELECT
2037            a.cp_id, a.invnumber, a.transdate, a.${table}_id, a.datepaid,
2038            a.duedate, a.ordnumber, a.taxincluded, a.curr AS currency, a.notes,
2039            a.intnotes, a.department_id, a.amount AS oldinvtotal,
2040            a.paid AS oldtotalpaid, a.employee_id, a.gldate, a.type,
2041            c.name AS $table,
2042            d.description AS department,
2043            e.name AS employee
2044          FROM $arap a
2045          JOIN $table c ON (a.${table}_id = c.id)
2046          LEFT JOIN employee e ON (e.id = a.employee_id)
2047          LEFT JOIN department d ON (d.id = a.department_id)
2048          WHERE a.id = ?|;
2049     $ref = selectfirst_hashref_query($self, $dbh, $query, $self->{id});
2050
2051     foreach $key (keys %$ref) {
2052       $self->{$key} = $ref->{$key};
2053     }
2054
2055     my $transdate = "current_date";
2056     if ($self->{transdate}) {
2057       $transdate = $dbh->quote($self->{transdate});
2058     }
2059
2060     # now get the account numbers
2061     $query = qq|SELECT c.accno, c.description, c.link, c.taxkey_id, tk.tax_id
2062                 FROM chart c
2063                 LEFT JOIN taxkeys tk ON (tk.chart_id = c.id)
2064                 WHERE c.link LIKE ?
2065                   AND (tk.id = (SELECT id FROM taxkeys WHERE taxkeys.chart_id = c.id AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1)
2066                     OR c.link LIKE '%_tax%')
2067                 ORDER BY c.accno|;
2068
2069     $sth = $dbh->prepare($query);
2070     do_statement($self, $sth, $query, "%$module%");
2071
2072     $self->{accounts} = "";
2073     while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
2074
2075       foreach my $key (split(/:/, $ref->{link})) {
2076         if ($key =~ /$module/) {
2077
2078           # cross reference for keys
2079           $xkeyref{ $ref->{accno} } = $key;
2080
2081           push @{ $self->{"${module}_links"}{$key} },
2082             { accno       => $ref->{accno},
2083               description => $ref->{description},
2084               taxkey      => $ref->{taxkey_id},
2085               tax_id      => $ref->{tax_id} };
2086
2087           $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
2088         }
2089       }
2090     }
2091
2092
2093     # get amounts from individual entries
2094     $query =
2095       qq|SELECT
2096            c.accno, c.description,
2097            a.source, a.amount, a.memo, a.transdate, a.cleared, a.project_id, a.taxkey,
2098            p.projectnumber,
2099            t.rate, t.id
2100          FROM acc_trans a
2101          LEFT JOIN chart c ON (c.id = a.chart_id)
2102          LEFT JOIN project p ON (p.id = a.project_id)
2103          LEFT JOIN tax t ON (t.id= (SELECT tk.tax_id FROM taxkeys tk
2104                                     WHERE (tk.taxkey_id=a.taxkey) AND
2105                                       ((CASE WHEN a.chart_id IN (SELECT chart_id FROM taxkeys WHERE taxkey_id = a.taxkey)
2106                                         THEN tk.chart_id = a.chart_id
2107                                         ELSE 1 = 1
2108                                         END)
2109                                        OR (c.link='%tax%')) AND
2110                                       (startdate <= a.transdate) ORDER BY startdate DESC LIMIT 1))
2111          WHERE a.trans_id = ?
2112          AND a.fx_transaction = '0'
2113          ORDER BY a.oid, a.transdate|;
2114     $sth = $dbh->prepare($query);
2115     do_statement($self, $sth, $query, $self->{id});
2116
2117     # get exchangerate for currency
2118     $self->{exchangerate} =
2119       $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
2120     my $index = 0;
2121
2122     # store amounts in {acc_trans}{$key} for multiple accounts
2123     while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
2124       $ref->{exchangerate} =
2125         $self->get_exchangerate($dbh, $self->{currency}, $ref->{transdate}, $fld);
2126       if (!($xkeyref{ $ref->{accno} } =~ /tax/)) {
2127         $index++;
2128       }
2129       if (($xkeyref{ $ref->{accno} } =~ /paid/) && ($self->{type} eq "credit_note")) {
2130         $ref->{amount} *= -1;
2131       }
2132       $ref->{index} = $index;
2133
2134       push @{ $self->{acc_trans}{ $xkeyref{ $ref->{accno} } } }, $ref;
2135     }
2136
2137     $sth->finish;
2138     $query =
2139       qq|SELECT
2140            d.curr AS currencies, d.closedto, d.revtrans,
2141            (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
2142            (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
2143          FROM defaults d|;
2144     $ref = selectfirst_hashref_query($self, $dbh, $query);
2145     map { $self->{$_} = $ref->{$_} } keys %$ref;
2146
2147   } else {
2148
2149     # get date
2150     $query =
2151        qq|SELECT
2152             current_date AS transdate, d.curr AS currencies, d.closedto, d.revtrans,
2153             (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
2154             (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
2155           FROM defaults d|;
2156     $ref = selectfirst_hashref_query($self, $dbh, $query);
2157     map { $self->{$_} = $ref->{$_} } keys %$ref;
2158
2159     if ($self->{"$self->{vc}_id"}) {
2160
2161       # only setup currency
2162       ($self->{currency}) = split(/:/, $self->{currencies});
2163
2164     } else {
2165
2166       $self->lastname_used($dbh, $myconfig, $table, $module);
2167
2168       # get exchangerate for currency
2169       $self->{exchangerate} =
2170         $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
2171
2172     }
2173
2174   }
2175
2176   $dbh->disconnect;
2177
2178   $main::lxdebug->leave_sub();
2179 }
2180
2181 sub lastname_used {
2182   $main::lxdebug->enter_sub();
2183
2184   my ($self, $dbh, $myconfig, $table, $module) = @_;
2185
2186   my $arap  = ($table eq 'customer') ? "ar" : "ap";
2187   $table = $table eq "customer" ? "customer" : "vendor";
2188   my $where = "1 = 1";
2189
2190   if ($self->{type} =~ /_order/) {
2191     $arap  = 'oe';
2192     $where = "quotation = '0'";
2193   }
2194   if ($self->{type} =~ /_quotation/) {
2195     $arap  = 'oe';
2196     $where = "quotation = '1'";
2197   }
2198
2199   my $query = qq|SELECT MAX(id) FROM $arap
2200                  WHERE $where AND ${table}_id > 0|;
2201   my ($trans_id) = selectrow_query($self, $dbh, $query);
2202
2203   $trans_id *= 1;
2204   $query =
2205     qq|SELECT
2206          a.curr, a.${table}_id, a.department_id,
2207          d.description AS department,
2208          ct.name, current_date + ct.terms AS duedate
2209        FROM $arap a
2210        LEFT JOIN $table ct ON (a.${table}_id = ct.id)
2211        LEFT JOIN department d ON (a.department_id = d.id)
2212        WHERE a.id = ?|;
2213   ($self->{currency},   $self->{"${table}_id"}, $self->{department_id},
2214    $self->{department}, $self->{$table},        $self->{duedate})
2215     = selectrow_query($self, $dbh, $query, $trans_id);
2216
2217   $main::lxdebug->leave_sub();
2218 }
2219
2220 sub current_date {
2221   $main::lxdebug->enter_sub();
2222
2223   my ($self, $myconfig, $thisdate, $days) = @_;
2224
2225   my $dbh = $self->dbconnect($myconfig);
2226   my $query;
2227
2228   $days *= 1;
2229   if ($thisdate) {
2230     my $dateformat = $myconfig->{dateformat};
2231     $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
2232     $thisdate = $dbh->quote($thisdate);
2233     $query = qq|SELECT to_date($thisdate, '$dateformat') + $days AS thisdate|;
2234   } else {
2235     $query = qq|SELECT current_date AS thisdate|;
2236   }
2237
2238   ($thisdate) = selectrow_query($self, $dbh, $query);
2239
2240   $dbh->disconnect;
2241
2242   $main::lxdebug->leave_sub();
2243
2244   return $thisdate;
2245 }
2246
2247 sub like {
2248   $main::lxdebug->enter_sub();
2249
2250   my ($self, $string) = @_;
2251
2252   if ($string !~ /%/) {
2253     $string = "%$string%";
2254   }
2255
2256   $string =~ s/\'/\'\'/g;
2257
2258   $main::lxdebug->leave_sub();
2259
2260   return $string;
2261 }
2262
2263 sub redo_rows {
2264   $main::lxdebug->enter_sub();
2265
2266   my ($self, $flds, $new, $count, $numrows) = @_;
2267
2268   my @ndx = ();
2269
2270   map { push @ndx, { num => $new->[$_ - 1]->{runningnumber}, ndx => $_ } }
2271     (1 .. $count);
2272
2273   my $i = 0;
2274
2275   # fill rows
2276   foreach my $item (sort { $a->{num} <=> $b->{num} } @ndx) {
2277     $i++;
2278     $j = $item->{ndx} - 1;
2279     map { $self->{"${_}_$i"} = $new->[$j]->{$_} } @{$flds};
2280   }
2281
2282   # delete empty rows
2283   for $i ($count + 1 .. $numrows) {
2284     map { delete $self->{"${_}_$i"} } @{$flds};
2285   }
2286
2287   $main::lxdebug->leave_sub();
2288 }
2289
2290 sub update_status {
2291   $main::lxdebug->enter_sub();
2292
2293   my ($self, $myconfig) = @_;
2294
2295   my ($i, $id);
2296
2297   my $dbh = $self->dbconnect_noauto($myconfig);
2298
2299   my $query = qq|DELETE FROM status
2300                  WHERE (formname = ?) AND (trans_id = ?)|;
2301   my $sth = prepare_query($self, $dbh, $query);
2302
2303   if ($self->{formname} =~ /(check|receipt)/) {
2304     for $i (1 .. $self->{rowcount}) {
2305       do_statement($self, $sth, $query, $self->{formname}, $self->{"id_$i"} * 1);
2306     }
2307   } else {
2308     do_statement($self, $sth, $query, $self->{formname}, $self->{id});
2309   }
2310   $sth->finish();
2311
2312   my $printed = ($self->{printed} =~ /$self->{formname}/) ? "1" : "0";
2313   my $emailed = ($self->{emailed} =~ /$self->{formname}/) ? "1" : "0";
2314
2315   my %queued = split / /, $self->{queued};
2316   my @values;
2317
2318   if ($self->{formname} =~ /(check|receipt)/) {
2319
2320     # this is a check or receipt, add one entry for each lineitem
2321     my ($accno) = split /--/, $self->{account};
2322     $query = qq|INSERT INTO status (trans_id, printed, spoolfile, formname, chart_id)
2323                 VALUES (?, ?, ?, ?, (SELECT c.id FROM chart c WHERE c.accno = ?))|;
2324     @values = ($printed, $queued{$self->{formname}}, $self->{prinform}, $accno);
2325     $sth = prepare_query($self, $dbh, $query);
2326
2327     for $i (1 .. $self->{rowcount}) {
2328       if ($self->{"checked_$i"}) {
2329         do_statement($self, $sth, $query, $self->{"id_$i"}, @values);
2330       }
2331     }
2332     $sth->finish();
2333
2334   } else {
2335     $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
2336                 VALUES (?, ?, ?, ?, ?)|;
2337     do_query($self, $dbh, $query, $self->{id}, $printed, $emailed,
2338              $queued{$self->{formname}}, $self->{formname});
2339   }
2340
2341   $dbh->commit;
2342   $dbh->disconnect;
2343
2344   $main::lxdebug->leave_sub();
2345 }
2346
2347 sub save_status {
2348   $main::lxdebug->enter_sub();
2349
2350   my ($self, $dbh) = @_;
2351
2352   my ($query, $printed, $emailed);
2353
2354   my $formnames  = $self->{printed};
2355   my $emailforms = $self->{emailed};
2356
2357   my $query = qq|DELETE FROM status
2358                  WHERE (formname = ?) AND (trans_id = ?)|;
2359   do_query($self, $dbh, $query, $self->{formname}, $self->{id});
2360
2361   # this only applies to the forms
2362   # checks and receipts are posted when printed or queued
2363
2364   if ($self->{queued}) {
2365     my %queued = split / /, $self->{queued};
2366
2367     foreach my $formname (keys %queued) {
2368       $printed = ($self->{printed} =~ /$self->{formname}/) ? "1" : "0";
2369       $emailed = ($self->{emailed} =~ /$self->{formname}/) ? "1" : "0";
2370
2371       $query = qq|INSERT INTO status (trans_id, printed, emailed, spoolfile, formname)
2372                   VALUES (?, ?, ?, ?, ?)|;
2373       do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $queued{$formname}, $formname);
2374
2375       $formnames  =~ s/$self->{formname}//;
2376       $emailforms =~ s/$self->{formname}//;
2377
2378     }
2379   }
2380
2381   # save printed, emailed info
2382   $formnames  =~ s/^ +//g;
2383   $emailforms =~ s/^ +//g;
2384
2385   my %status = ();
2386   map { $status{$_}{printed} = 1 } split / +/, $formnames;
2387   map { $status{$_}{emailed} = 1 } split / +/, $emailforms;
2388
2389   foreach my $formname (keys %status) {
2390     $printed = ($formnames  =~ /$self->{formname}/) ? "1" : "0";
2391     $emailed = ($emailforms =~ /$self->{formname}/) ? "1" : "0";
2392
2393     $query = qq|INSERT INTO status (trans_id, printed, emailed, formname)
2394                 VALUES (?, ?, ?, ?)|;
2395     do_query($self, $dbh, $query, $self->{id}, $printed, $emailed, $formname);
2396   }
2397
2398   $main::lxdebug->leave_sub();
2399 }
2400
2401 #--- 4 locale ---#
2402 # $main::locale->text('SAVED')
2403 # $main::locale->text('DELETED')
2404 # $main::locale->text('ADDED')
2405 # $main::locale->text('PAYMENT POSTED')
2406 # $main::locale->text('POSTED')
2407 # $main::locale->text('POSTED AS NEW')
2408 # $main::locale->text('ELSE')
2409 # $main::locale->text('SAVED FOR DUNNING')
2410 # $main::locale->text('DUNNING STARTED')
2411 # $main::locale->text('PRINTED')
2412 # $main::locale->text('MAILED')
2413 # $main::locale->text('SCREENED')
2414 # $main::locale->text('CANCELED')
2415 # $main::locale->text('invoice')
2416 # $main::locale->text('proforma')
2417 # $main::locale->text('sales_order')
2418 # $main::locale->text('packing_list')
2419 # $main::locale->text('pick_list')
2420 # $main::locale->text('purchase_order')
2421 # $main::locale->text('bin_list')
2422 # $main::locale->text('sales_quotation')
2423 # $main::locale->text('request_quotation')
2424
2425 sub save_history {
2426   $main::lxdebug->enter_sub();
2427
2428   my $self = shift();
2429   my $dbh = shift();
2430
2431   if(!exists $self->{employee_id}) {
2432     &get_employee($self, $dbh);
2433   }
2434
2435   my $query =
2436     qq|INSERT INTO history_erp (trans_id, employee_id, addition, what_done, snumbers) | .
2437     qq|VALUES (?, ?, ?, ?, ?)|;
2438   my @values = (conv_i($self->{id}), conv_i($self->{employee_id}),
2439                 $self->{addition}, $self->{what_done}, "$self->{snumbers}");
2440   do_query($self, $dbh, $query, @values);
2441
2442   $main::lxdebug->leave_sub();
2443 }
2444
2445 sub get_history {
2446   $main::lxdebug->enter_sub();
2447
2448   my $self = shift();
2449   my $dbh = shift();
2450   my $trans_id = shift();
2451   my $restriction = shift();
2452   my @tempArray;
2453   my $i = 0;
2454   if ($trans_id ne "") {
2455     my $query =
2456       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 | .
2457       qq|FROM history_erp h | .
2458       qq|LEFT JOIN employee emp ON (emp.id = h.employee_id) | .
2459       qq|WHERE trans_id = ? |
2460       . $restriction;
2461
2462     my $sth = $dbh->prepare($query) || $self->dberror($query);
2463
2464     $sth->execute($trans_id) || $self->dberror("$query ($trans_id)");
2465
2466     while(my $hash_ref = $sth->fetchrow_hashref()) {
2467       $hash_ref->{addition} = $main::locale->text($hash_ref->{addition});
2468       $hash_ref->{what_done} = $main::locale->text($hash_ref->{what_done});
2469       $hash_ref->{snumbers} =~ s/^.+_(.*)$/$1/g;
2470       $tempArray[$i++] = $hash_ref;
2471     }
2472     $main::lxdebug->leave_sub() and return \@tempArray 
2473       if ($i > 0 && $tempArray[0] ne "");
2474   }
2475   $main::lxdebug->leave_sub();
2476   return 0;
2477 }
2478
2479 sub update_defaults {
2480   $main::lxdebug->enter_sub();
2481
2482   my ($self, $myconfig, $fld, $provided_dbh) = @_;
2483
2484   my $dbh;
2485   if ($provided_dbh) {
2486     $dbh = $provided_dbh;
2487   } else {
2488     $dbh = $self->dbconnect_noauto($myconfig);
2489   }
2490   my $query = qq|SELECT $fld FROM defaults FOR UPDATE|;
2491   my $sth   = $dbh->prepare($query);
2492
2493   $sth->execute || $self->dberror($query);
2494   my ($var) = $sth->fetchrow_array;
2495   $sth->finish;
2496
2497   $var =~ s/\d+$/ sprintf '%0*d', length($&), $&+1 /e;
2498   $var ||= 1;
2499
2500   $query = qq|UPDATE defaults SET $fld = ?|;
2501   do_query($self, $dbh, $query, $var);
2502
2503   if (!$provided_dbh) {
2504     $dbh->commit;
2505     $dbh->disconnect;
2506   }
2507
2508   $main::lxdebug->leave_sub();
2509
2510   return $var;
2511 }
2512
2513 sub update_business {
2514   $main::lxdebug->enter_sub();
2515
2516   my ($self, $myconfig, $business_id, $provided_dbh) = @_;
2517
2518   my $dbh;
2519   if ($provided_dbh) {
2520     $dbh = $provided_dbh;
2521   } else {
2522     $dbh = $self->dbconnect_noauto($myconfig);
2523   }
2524   my $query =
2525     qq|SELECT customernumberinit FROM business
2526        WHERE id = ? FOR UPDATE|;
2527   my ($var) = selectrow_query($self, $dbh, $query, $business_id);
2528
2529   $var =~ s/\d+$/ sprintf '%0*d', length($&), $&+1 /e;
2530   
2531   $query = qq|UPDATE business
2532               SET customernumberinit = ?
2533               WHERE id = ?|;
2534   do_query($self, $dbh, $query, $var, $business_id);
2535
2536   if (!$provided_dbh) {
2537     $dbh->commit;
2538     $dbh->disconnect;
2539   }
2540
2541   $main::lxdebug->leave_sub();
2542
2543   return $var;
2544 }
2545
2546 sub get_partsgroup {
2547   $main::lxdebug->enter_sub();
2548
2549   my ($self, $myconfig, $p) = @_;
2550
2551   my $dbh = $self->dbconnect($myconfig);
2552
2553   my $query = qq|SELECT DISTINCT pg.id, pg.partsgroup
2554                  FROM partsgroup pg
2555                  JOIN parts p ON (p.partsgroup_id = pg.id) |;
2556   my @values;
2557
2558   if ($p->{searchitems} eq 'part') {
2559     $query .= qq|WHERE p.inventory_accno_id > 0|;
2560   }
2561   if ($p->{searchitems} eq 'service') {
2562     $query .= qq|WHERE p.inventory_accno_id IS NULL|;
2563   }
2564   if ($p->{searchitems} eq 'assembly') {
2565     $query .= qq|WHERE p.assembly = '1'|;
2566   }
2567   if ($p->{searchitems} eq 'labor') {
2568     $query .= qq|WHERE (p.inventory_accno_id > 0) AND (p.income_accno_id IS NULL)|;
2569   }
2570
2571   $query .= qq|ORDER BY partsgroup|;
2572
2573   if ($p->{all}) {
2574     $query = qq|SELECT id, partsgroup FROM partsgroup
2575                 ORDER BY partsgroup|;
2576   }
2577
2578   if ($p->{language_code}) {
2579     $query = qq|SELECT DISTINCT pg.id, pg.partsgroup,
2580                   t.description AS translation
2581                 FROM partsgroup pg
2582                 JOIN parts p ON (p.partsgroup_id = pg.id)
2583                 LEFT JOIN translation t ON ((t.trans_id = pg.id) AND (t.language_code = ?))
2584                 ORDER BY translation|;
2585     @values = ($p->{language_code});
2586   }
2587
2588   $self->{all_partsgroup} = selectall_hashref_query($self, $dbh, $query, @values);
2589
2590   $dbh->disconnect;
2591   $main::lxdebug->leave_sub();
2592 }
2593
2594 sub get_pricegroup {
2595   $main::lxdebug->enter_sub();
2596
2597   my ($self, $myconfig, $p) = @_;
2598
2599   my $dbh = $self->dbconnect($myconfig);
2600
2601   my $query = qq|SELECT p.id, p.pricegroup
2602                  FROM pricegroup p|;
2603
2604   $query .= qq| ORDER BY pricegroup|;
2605
2606   if ($p->{all}) {
2607     $query = qq|SELECT id, pricegroup FROM pricegroup
2608                 ORDER BY pricegroup|;
2609   }
2610
2611   $self->{all_pricegroup} = selectall_hashref_query($self, $dbh, $query);
2612
2613   $dbh->disconnect;
2614
2615   $main::lxdebug->leave_sub();
2616 }
2617
2618 sub all_years {
2619 # usage $form->all_years($myconfig, [$dbh])
2620 # return list of all years where bookings found
2621 # (@all_years)
2622
2623   $main::lxdebug->enter_sub();
2624
2625   my ($self, $myconfig, $dbh) = @_;
2626
2627   my $disconnect = 0;
2628   if (! $dbh) {
2629     $dbh = $self->dbconnect($myconfig);
2630     $disconnect = 1;
2631   }
2632
2633   # get years
2634   my $query = qq|SELECT (SELECT MIN(transdate) FROM acc_trans),
2635                    (SELECT MAX(transdate) FROM acc_trans)|;
2636   my ($startdate, $enddate) = selectrow_query($self, $dbh, $query);
2637
2638   if ($myconfig->{dateformat} =~ /^yy/) {
2639     ($startdate) = split /\W/, $startdate;
2640     ($enddate) = split /\W/, $enddate;
2641   } else {
2642     (@_) = split /\W/, $startdate;
2643     $startdate = $_[2];
2644     (@_) = split /\W/, $enddate;
2645     $enddate = $_[2];
2646   }
2647
2648   my @all_years;
2649   $startdate = substr($startdate,0,4);
2650   $enddate = substr($enddate,0,4);
2651
2652   while ($enddate >= $startdate) {
2653     push @all_years, $enddate--;
2654   }
2655
2656   $dbh->disconnect if $disconnect;
2657
2658   return @all_years;
2659
2660   $main::lxdebug->leave_sub();
2661 }
2662
2663
2664 1;