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