Merge branch 'master' of github.com:kivitendo/kivitendo-erp
[kivitendo-erp.git] / SL / RP.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: Benjamin Lee <benjaminlee@consultant.com>
16 #
17 # This program is free software; you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License as published by
19 # the Free Software Foundation; either version 2 of the License, or
20 # (at your option) any later version.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 # GNU General Public License for more details.
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #======================================================================
30 #
31 # backend code for reports
32 #
33 #======================================================================
34
35 package RP;
36
37 use SL::DBUtils;
38 use Data::Dumper;
39 use List::Util qw(sum);
40
41 # use warnings;
42 use strict;
43
44 # new implementation of balance sheet
45 # readme!
46 #
47 # stuff missing from the original implementation:
48 # - bold stuff
49 # - subdescription
50 # - proper testing for heading charts
51 # - transmission from $form to TMPL realm is not as clear as i'd like
52
53 sub get_openbalance_date {
54   my ($closedto, $target) = map { $::locale->parse_date_to_object(\%::myconfig, $_) } @_;
55
56   return unless $closedto;
57
58   $closedto->subtract(years => 1) while ($target - $closedto)->is_negative;
59   $closedto->add(days => 1);
60   return $::locale->format_date(\%::myconfig, $closedto);
61 }
62
63 sub balance_sheet {
64   $main::lxdebug->enter_sub();
65
66   my $myconfig = \%main::myconfig;
67   my $form     = $main::form;
68   my $dbh      = $::form->get_standard_dbh;
69
70   my $last_period = 0;
71   my @categories  = qw(A C L Q);
72
73   # if there are any dates construct a where
74   if ($form->{asofdate}) {
75     $form->{period} = $form->{this_period} = conv_dateq($form->{asofdate});
76   }
77
78   # get end of financial year and convert to Date format
79   my ($closedto) = selectfirst_array_query($form, $dbh, 'SELECT closedto FROM defaults');
80
81   # get date of last opening balance
82   my $startdate = get_openbalance_date($closedto, $form->{asofdate});
83
84   get_accounts($dbh, $last_period, $startdate, $form->{asofdate}, $form, \@categories);
85
86   # if there are any compare dates
87   if ($form->{compareasofdate}) {
88     $last_period = 1;
89
90     $startdate = get_openbalance_date($closedto, $form->{compareasofdate});
91
92     get_accounts($dbh, $last_period, $startdate, $form->{compareasofdate}, $form, \@categories);
93     $form->{last_period} = conv_dateq($form->{compareasofdate});
94   }
95
96   # now we got $form->{A}{accno}{ }    assets
97   # and $form->{L}{accno}{ }           liabilities
98   # and $form->{Q}{accno}{ }           equity
99   # build asset accounts
100
101   my %account = ('A' => { 'ml'     => -1 },
102                  'L' => { 'ml'     =>  1 },
103                  'Q' => { 'ml'     =>  1 });
104
105   my $TMPL_DATA = {};
106
107   foreach my $category (grep { !/C/ } @categories) {
108
109     $TMPL_DATA->{$category} = [];
110     my $ml  = $account{$category}{ml};
111
112     foreach my $key (sort keys %{ $form->{$category} }) {
113
114       my $row = { %{ $form->{$category}{$key} } };
115
116       # if charttype "heading" - calculate this entry, start a new batch of charts belonging to this heading and skip the rest bo the loop
117       # header charts are not real charts. start a sub aggregation with them, but don't calculate anything with them
118       if ($row->{charttype} eq "H") {
119         if ($account{$category}{subtotal} && $form->{l_subtotal}) {
120           $row->{subdescription} = $account{$category}{subdescription};
121           $row->{this}           = $account{$category}{subthis} * $ml;                   # format: $dec, $dash
122           $row->{last}           = $account{$category}{sublast} * $ml if $last_period;   # format: $dec, $dash
123         }
124
125         $row->{subheader} = 1;
126         $account{$category}{subthis}        = $row->{this};
127         $account{$category}{sublast}        = $row->{last};
128         $account{$category}{subdescription} = $row->{description};
129         $account{$category}{subtotal} = 1;
130
131         $row->{this} = 0;
132         $row->{last} = 0;
133
134         next unless $form->{l_heading};
135       }
136
137       for my $period (qw(this last)) {
138         next if ($period eq 'last' && !$last_period);
139         # only add assets
140         $row->{$period}                    *= $ml;
141       }
142
143       push @{ $TMPL_DATA->{$category} }, $row;
144     } # foreach
145
146     # resolve heading/subtotal
147     if ($account{$category}{subtotal} && $form->{l_subtotal}) {
148       $TMPL_DATA->{$category}[-1]{subdescription} = $account{$category}{subdescription};
149       $TMPL_DATA->{$category}[-1]{this}           = $account{$category}{subthis} * $ml;                   # format: $dec, $dash
150       $TMPL_DATA->{$category}[-1]{last}           = $account{$category}{sublast} * $ml if $last_period;   # format: $dec, $dash
151     }
152
153     $TMPL_DATA->{total}{$category}{this} = sum map { $_->{this} } @{ $TMPL_DATA->{$category} };
154     $TMPL_DATA->{total}{$category}{last} = sum map { $_->{last} } @{ $TMPL_DATA->{$category} };
155   }
156
157   for my $period (qw(this last)) {
158     next if ($period eq 'last' && !$last_period);
159
160     $form->{E}{$period}             = $TMPL_DATA->{total}{A}{$period} - $TMPL_DATA->{total}{L}{$period} - $TMPL_DATA->{total}{Q}{$period};
161     $TMPL_DATA->{total}{Q}{$period}     += $form->{E}{$period};
162     $TMPL_DATA->{total}{$period}    = $TMPL_DATA->{total}{L}{$period} + $TMPL_DATA->{total}{Q}{$period};
163   }
164     $form->{E}{description}='nicht verbuchter Gewinn/Verlust';
165   push @{ $TMPL_DATA->{Q} }, $form->{E};
166
167   $main::lxdebug->leave_sub();
168
169   return $TMPL_DATA;
170 }
171
172 sub get_accounts {
173   $main::lxdebug->enter_sub();
174
175   my ($dbh, $last_period, $fromdate, $todate, $form, $categories) = @_;
176
177   my ($null, $department_id) = split /--/, $form->{department};
178
179   my $query;
180   my $dpt_where = '';
181   my $dpt_where_without_arapgl = '';
182   my $project   = '';
183   my $where     = "1 = 1";
184   my $glwhere   = "";
185   my $subwhere  = "";
186   my $item;
187   my $sth;
188   my $dec = $form->{decimalplaces};
189
190   my $category = qq| AND (| . join(" OR ", map({ "(c.category = " . $dbh->quote($_) . ")" } @{$categories})) . qq|) |;
191
192   # get headings
193   $query =
194     qq|SELECT c.accno, c.description, c.category
195        FROM chart c
196        WHERE (c.charttype = 'H')
197          $category
198        ORDER by c.accno|;
199
200   $sth = prepare_execute_query($form, $dbh, $query);
201
202   my @headingaccounts = ();
203   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
204     $form->{ $ref->{category} }{ $ref->{accno} }{description} =
205       "$ref->{description}";
206     $form->{ $ref->{category} }{ $ref->{accno} }{charttype} = "H";
207     $form->{ $ref->{category} }{ $ref->{accno} }{accno}     = $ref->{accno};
208
209     push @headingaccounts, $ref->{accno};
210   }
211
212   $sth->finish;
213
214   # if l_ob is selected l_cb is always ignored
215   if ( $form->{l_ob} ) {
216     $where .= ' AND ac.ob_transaction is true  '
217   } elsif ( not $form->{l_cb} ) {
218     $where .= ' AND ac.cb_transaction is false ';
219   };
220
221   if ($fromdate) {
222     $fromdate = conv_dateq($fromdate);
223     if ($form->{method} eq 'cash') {
224       $subwhere .= " AND (transdate >= $fromdate)";
225       $glwhere = " AND (ac.transdate >= $fromdate)";
226     } else {
227       $where .= " AND (ac.transdate >= $fromdate)";
228     }
229   }
230
231   if ($todate) {
232     $todate = conv_dateq($todate);
233     $where    .= " AND (ac.transdate <= $todate)";
234     $subwhere .= " AND (transdate <= $todate)";
235   }
236
237   if ($department_id) {
238     $dpt_where = qq| AND (a.department_id = | . conv_i($department_id, 'NULL') . qq|)|;
239   }
240
241   if ($form->{project_id}) {
242     # Diese Bedingung wird derzeit niemals wahr sein, da man in Bericht->Bilanz keine
243     # Projekte auswählen kann
244     $project = qq| AND (ac.project_id = | . conv_i($form->{project_id}, 'NULL') . qq|) |;
245   }
246
247   if ($form->{method} eq 'cash') {
248     $query =
249       qq|SELECT c.accno, sum(ac.amount) AS amount, c.description, c.category
250          FROM acc_trans ac
251          JOIN chart c ON (c.id = ac.chart_id)
252          JOIN ar a ON (a.id = ac.trans_id)
253          WHERE $where
254            $dpt_where
255            $category
256            AND ac.trans_id IN
257              (
258                SELECT trans_id
259                FROM acc_trans a
260                WHERE (a.chart_link LIKE '%AR_paid%')
261                $subwhere
262              )
263            $project
264          GROUP BY c.accno, c.description, c.category
265
266          UNION ALL
267
268          SELECT c.accno, sum(ac.amount) AS amount, c.description, c.category
269          FROM acc_trans ac
270          JOIN chart c ON (c.id = ac.chart_id)
271          JOIN ap a ON (a.id = ac.trans_id)
272          WHERE $where
273            $dpt_where
274            $category
275            AND ac.trans_id IN
276              (
277                SELECT trans_id
278                FROM acc_trans a
279                WHERE (a.chart_link LIKE '%AP_paid%')
280                $subwhere
281              )
282            $project
283          GROUP BY c.accno, c.description, c.category
284
285          UNION ALL
286
287          SELECT c.accno, sum(ac.amount) AS amount, c.description, c.category
288          FROM acc_trans ac
289          JOIN chart c ON (c.id = ac.chart_id)
290          JOIN gl a ON (a.id = ac.trans_id)
291          WHERE $where
292            $glwhere
293            $dpt_where
294            $category
295              AND NOT ((ac.chart_link = 'AR') OR (ac.chart_link = 'AP'))
296            $project
297          GROUP BY c.accno, c.description, c.category |;
298
299     if ($form->{project_id}) {
300       # s.o. keine Projektauswahl in Bilanz
301       $query .=
302         qq|
303          UNION ALL
304
305          SELECT c.accno AS accno, SUM(ac.sellprice * ac.qty) AS amount, c.description AS description, c.category
306          FROM invoice ac
307          JOIN ar a ON (a.id = ac.trans_id)
308          JOIN parts p ON (ac.parts_id = p.id)
309          JOIN chart c on (p.income_accno_id = c.id)
310          -- use transdate from subwhere
311          WHERE (c.category = 'I')
312            $subwhere
313            $dpt_where
314            AND ac.trans_id IN
315              (
316                SELECT trans_id
317                FROM acc_trans a
318                WHERE (a.chart_link LIKE '%AR_paid%')
319                $subwhere
320              )
321            $project
322          GROUP BY c.accno, c.description, c.category
323
324          UNION ALL
325
326          SELECT c.accno AS accno, SUM(ac.sellprice) AS amount, c.description AS description, c.category
327          FROM invoice ac
328          JOIN ap a ON (a.id = ac.trans_id)
329          JOIN parts p ON (ac.parts_id = p.id)
330          JOIN chart c on (p.expense_accno_id = c.id)
331          WHERE (c.category = 'E')
332            $subwhere
333            $dpt_where
334            AND ac.trans_id IN
335              (
336                SELECT trans_id
337                FROM acc_trans a
338                WHERE a.chart_link LIKE '%AP_paid%'
339                $subwhere
340              )
341            $project
342          GROUP BY c.accno, c.description, c.category |;
343     }
344
345   } else {                      # if ($form->{method} eq 'cash')
346     if ($department_id) {
347       $dpt_where = qq| AND a.department_id = | . conv_i($department_id);
348       $dpt_where_without_arapgl = qq| AND COALESCE((SELECT department_id FROM ar WHERE ar.id=ac.trans_id),
349                                                    (SELECT department_id FROM gl WHERE gl.id=ac.trans_id),
350                                                    (SELECT department_id FROM ap WHERE ap.id=ac.trans_id)) = | . conv_i($department_id);
351     }
352
353     $query = qq|
354       SELECT c.accno, sum(ac.amount) AS amount, c.description, c.category
355       FROM acc_trans ac
356       JOIN chart c ON (c.id = ac.chart_id)
357       WHERE $where
358         $dpt_where_without_arapgl
359         $category
360         $project
361       GROUP BY c.accno, c.description, c.category |;
362
363     if ($form->{project_id}) {
364       # s.o. keine Projektauswahl in Bilanz
365       $query .= qq|
366       UNION ALL
367
368       SELECT c.accno AS accno, SUM(ac.sellprice * ac.qty) AS amount, c.description AS description, c.category
369       FROM invoice ac
370       JOIN ar a ON (a.id = ac.trans_id)
371       JOIN parts p ON (ac.parts_id = p.id)
372       JOIN chart c on (p.income_accno_id = c.id)
373       -- use transdate from subwhere
374       WHERE (c.category = 'I')
375         $subwhere
376         $dpt_where
377         $project
378       GROUP BY c.accno, c.description, c.category
379
380       UNION ALL
381
382       SELECT c.accno AS accno, SUM(ac.sellprice * ac.qty) * -1 AS amount, c.description AS description, c.category
383       FROM invoice ac
384       JOIN ap a ON (a.id = ac.trans_id)
385       JOIN parts p ON (ac.parts_id = p.id)
386       JOIN chart c on (p.expense_accno_id = c.id)
387       WHERE (c.category = 'E')
388         $subwhere
389         $dpt_where
390         $project
391       GROUP BY c.accno, c.description, c.category |;
392     }
393   }
394
395   my @accno;
396   my $accno;
397   my $ref;
398
399   $sth = prepare_execute_query($form, $dbh, $query);
400
401   while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
402
403     if ($ref->{category} eq 'C') {
404       $ref->{category} = 'A';
405     }
406
407     # get last heading account
408     @accno = grep { $_ le "$ref->{accno}" } @headingaccounts;
409     $accno = pop @accno;
410     if ($accno) {
411       if ($last_period) {
412         $form->{ $ref->{category} }{$accno}{last} += $ref->{amount};
413       } else {
414         $form->{ $ref->{category} }{$accno}{this} += $ref->{amount};
415       }
416     }
417
418     $form->{ $ref->{category} }{ $ref->{accno} }{accno}       = $ref->{accno};
419     $form->{ $ref->{category} }{ $ref->{accno} }{description} = $ref->{description};
420     $form->{ $ref->{category} }{ $ref->{accno} }{charttype} = "A";
421
422     if ($last_period) {
423       $form->{ $ref->{category} }{ $ref->{accno} }{last} += $ref->{amount};
424     } else {
425       $form->{ $ref->{category} }{ $ref->{accno} }{this} += $ref->{amount};
426     }
427   }
428   $sth->finish;
429
430   # remove accounts with zero balance
431   foreach $category (@{$categories}) {
432     foreach $accno (keys %{ $form->{$category} }) {
433       $form->{$category}{$accno}{last} = $form->round_amount($form->{$category}{$accno}{last}, $dec);
434       $form->{$category}{$accno}{this} = $form->round_amount($form->{$category}{$accno}{this}, $dec);
435
436       delete $form->{$category}{$accno}
437         if (   $form->{$category}{$accno}{this} == 0
438             && $form->{$category}{$accno}{last} == 0);
439     }
440   }
441
442   $main::lxdebug->leave_sub();
443 }
444
445 sub get_accounts_g {
446   $main::lxdebug->enter_sub();
447
448   my ($dbh, $last_period, $fromdate, $todate, $form, $category) = @_;
449
450   my ($null, $department_id) = split /--/, $form->{department};
451
452   my $query;
453   my $dpt_where;
454   my $dpt_where_without_arapgl;
455   my $project;
456   my $where    = "1 = 1";
457   my $glwhere  = "";
458   my $prwhere  = "";
459   my $subwhere = "";
460   my $inwhere = "";
461   my $item;
462
463   $where .= ' AND ac.cb_transaction is false ' unless $form->{l_cb};
464
465   if ($fromdate) {
466     $fromdate = conv_dateq($fromdate);
467     if ($form->{method} eq 'cash') {
468       $subwhere .= " AND (transdate    >= $fromdate)";
469       $glwhere   = " AND (ac.transdate >= $fromdate)";
470       $prwhere   = " AND (a.transdate  >= $fromdate)";
471       $inwhere   = " AND (acc.transdate >= $fromdate)";
472     } else {
473       $where    .= " AND (ac.transdate >= $fromdate)";
474     }
475   }
476
477   if ($todate) {
478     $todate = conv_dateq($todate);
479     $subwhere   .= " AND (transdate    <= $todate)";
480     $where      .= " AND (ac.transdate <= $todate)";
481     $prwhere    .= " AND (a.transdate  <= $todate)";
482     $inwhere    .= " AND (acc.transdate <= $todate)";
483   }
484
485   if ($department_id) {
486     $dpt_where = qq| AND (a.department_id = | . conv_i($department_id, 'NULL') . qq|) |;
487   }
488
489   if ($form->{project_id}) {
490     $project = qq| AND (ac.project_id = | . conv_i($form->{project_id}) . qq|) |;
491   }
492
493 #
494 # GUV patch by Ronny Rentner (Bug 1190)
495 #
496 # GUV IST-Versteuerung
497 #
498 # Alle tatsaechlichen _Zahlungseingaenge_
499 # im Zeitraum erfassen
500 # (Teilzahlungen werden prozentual auf verschiedene Steuern aufgeteilt)
501 #
502 #
503
504   if ($form->{method} eq 'cash') {
505     $query =
506       qq|
507        SELECT SUM( ac.amount * CASE WHEN COALESCE((SELECT amount FROM ar WHERE id = ac.trans_id), 0) != 0 THEN
508             /* ar amount is not zero, so we can divide by amount   */
509                     (SELECT SUM(acc.amount) * -1
510                      FROM acc_trans acc
511                      WHERE 1=1 $inwhere
512                      AND acc.trans_id = ac.trans_id
513                      AND acc.chart_link LIKE '%AR_paid%')
514                   / (SELECT amount FROM ar WHERE id = ac.trans_id)
515             ELSE 0
516             /* ar amount is zero, or we are checking with a non-ar-transaction, so we return 0 in both cases as multiplicator of ac.amount */
517             END
518                 ) AS amount, c.$category
519        FROM acc_trans ac
520        LEFT JOIN chart c ON (c.id  = ac.chart_id)
521        LEFT JOIN ar      ON (ar.id = ac.trans_id)
522        LEFT JOIN taxkeys tk ON (tk.id = (
523                                   SELECT id FROM taxkeys
524                                   WHERE chart_id = ac.chart_id
525                                   AND startdate <= COALESCE(ar.deliverydate,ar.transdate)
526                                   ORDER BY startdate DESC LIMIT 1
527                                   )
528                                 )
529       WHERE ac.trans_id IN (SELECT DISTINCT trans_id FROM acc_trans WHERE 1=1 $subwhere)
530
531       GROUP BY c.$category
532
533 /*
534        SELECT SUM(ac.amount * chart_category_to_sgn(c.category)) AS amount, c.$category
535          FROM acc_trans ac
536          JOIN chart c ON (c.id = ac.chart_id)
537          JOIN ar a ON (a.id = ac.trans_id)
538          WHERE $where $dpt_where
539            AND ac.trans_id IN ( SELECT trans_id FROM acc_trans a WHERE (a.chart_link LIKE '%AR_paid%') $subwhere)
540            $project
541          GROUP BY c.$category
542 */
543          UNION
544
545          SELECT SUM(ac.amount * chart_category_to_sgn(c.category)) AS amount, c.$category
546          FROM acc_trans ac
547          JOIN chart c ON (c.id = ac.chart_id)
548          JOIN ap a ON (a.id = ac.trans_id)
549          WHERE $where $dpt_where
550            AND ac.trans_id IN ( SELECT trans_id FROM acc_trans a WHERE (a.chart_link LIKE '%AP_paid%') $subwhere)
551            $project
552          GROUP BY c.$category
553
554          UNION
555
556          SELECT SUM(ac.amount * chart_category_to_sgn(c.category)) AS amount, c.$category
557          FROM acc_trans ac
558          JOIN chart c ON (c.id = ac.chart_id)
559          JOIN gl a ON (a.id = ac.trans_id)
560          WHERE $where $dpt_where $glwhere
561            AND NOT ((ac.chart_link = 'AR') OR (ac.chart_link = 'AP'))
562            $project
563          GROUP BY c.$category
564         |;
565
566     if ($form->{project_id}) {
567       $query .= qq|
568          UNION
569
570          SELECT SUM(ac.sellprice * ac.qty * chart_category_to_sgn(c.category)) AS amount, c.$category
571          FROM invoice ac
572          JOIN ar a ON (a.id = ac.trans_id)
573          JOIN parts p ON (ac.parts_id = p.id)
574          JOIN chart c on (p.income_accno_id = c.id)
575          WHERE (c.category = 'I') $prwhere $dpt_where
576            AND ac.trans_id IN ( SELECT trans_id FROM acc_trans a WHERE (a.chart_link LIKE '%AR_paid%') $subwhere)
577            $project
578          GROUP BY c.$category
579
580          UNION
581
582          SELECT SUM(ac.sellprice * chart_category_to_sgn(c.category)) AS amount, c.$category
583          FROM invoice ac
584          JOIN ap a ON (a.id = ac.trans_id)
585          JOIN parts p ON (ac.parts_id = p.id)
586          JOIN chart c on (p.expense_accno_id = c.id)
587          WHERE (c.category = 'E') $prwhere $dpt_where
588            AND ac.trans_id IN ( SELECT trans_id FROM acc_trans a WHERE (a.chart_link LIKE '%AP_paid%') $subwhere)
589          $project
590          GROUP BY c.$category
591          |;
592     }
593
594   } else {                      # if ($form->{method} eq 'cash')
595     if ($department_id) {
596       $dpt_where = qq| AND (a.department_id = | . conv_i($department_id, 'NULL') . qq|) |;
597       $dpt_where_without_arapgl = qq| AND COALESCE((SELECT department_id FROM ar WHERE ar.id=ac.trans_id),
598                                                    (SELECT department_id FROM gl WHERE gl.id=ac.trans_id),
599                                                    (SELECT department_id FROM ap WHERE ap.id=ac.trans_id)) = | . conv_i($department_id);
600     }
601
602     $query = qq|
603         SELECT sum(ac.amount * chart_category_to_sgn(c.category)) AS amount, c.$category
604         FROM acc_trans ac
605         JOIN chart c ON (c.id = ac.chart_id)
606         WHERE $where
607           $dpt_where_without_arapgl
608           $project
609         GROUP BY c.$category |;
610
611     if ($form->{project_id}) {
612       $query .= qq|
613         UNION
614
615         SELECT SUM(ac.sellprice * ac.qty * chart_category_to_sgn(c.category)) AS amount, c.$category
616         FROM invoice ac
617         JOIN ar a ON (a.id = ac.trans_id)
618         JOIN parts p ON (ac.parts_id = p.id)
619         JOIN chart c on (p.income_accno_id = c.id)
620         WHERE (c.category = 'I')
621           $prwhere
622           $dpt_where
623           $project
624         GROUP BY c.$category
625
626         UNION
627
628         SELECT SUM(ac.sellprice * ac.qty * chart_category_to_sgn(c.category)) AS amount, c.$category
629         FROM invoice ac
630         JOIN ap a ON (a.id = ac.trans_id)
631         JOIN parts p ON (ac.parts_id = p.id)
632         JOIN chart c on (p.expense_accno_id = c.id)
633         WHERE (c.category = 'E')
634           $prwhere
635           $dpt_where
636           $project
637         GROUP BY c.$category |;
638     }
639   }
640
641   my @accno;
642   my $accno;
643   my $ref;
644
645   foreach my $ref (selectall_hashref_query($form, $dbh, $query)) {
646     if ($category eq "pos_bwa") {
647       if ($last_period) {
648         $form->{ $ref->{$category} }{kumm} += $ref->{amount};
649       } else {
650         $form->{ $ref->{$category} }{jetzt} += $ref->{amount};
651       }
652     } else {
653       $form->{ $ref->{$category} } += $ref->{amount};
654     }
655   }
656
657   $main::lxdebug->leave_sub();
658 }
659
660 sub trial_balance {
661   $main::lxdebug->enter_sub();
662
663   my ($self, $myconfig, $form, %options) = @_;
664
665   my $dbh = $form->dbconnect($myconfig);
666
667   my ($query, $sth, $ref);
668   my %balance = ();
669   my %trb     = ();
670   my ($null, $department_id) = split /--/, $form->{department};
671   my @headingaccounts = ();
672   my $dpt_where;
673   my $dpt_where_without_arapgl;
674   my $project;
675
676   my $where    = "1 = 1";
677   my $invwhere = $where;
678
679   if ($department_id) {
680     $dpt_where = qq| AND (a.department_id = | . conv_i($department_id, 'NULL') . qq|) |;
681     $dpt_where_without_arapgl = qq| AND COALESCE((SELECT department_id FROM ar WHERE ar.id=ac.trans_id),
682                                                  (SELECT department_id FROM gl WHERE gl.id=ac.trans_id),
683                                                  (SELECT department_id FROM ap WHERE ap.id=ac.trans_id)) = | . conv_i($department_id);
684   }
685
686   # project_id only applies to getting transactions
687   # it has nothing to do with a trial balance
688   # but we use the same function to collect information
689
690   if ($form->{project_id}) {
691     $project = qq| AND (ac.project_id = | . conv_i($form->{project_id}, 'NULL') . qq|) |;
692   }
693
694   my $acc_cash_where = "";
695 #  my $ar_cash_where = "";
696 #  my $ap_cash_where = "";
697
698
699   if ($form->{method} eq "cash") {
700     $acc_cash_where =
701       qq| AND (ac.trans_id IN (
702             SELECT id
703             FROM ar
704             WHERE datepaid >= '$form->{fromdate}'
705               AND datepaid <= '$form->{todate}'
706
707             UNION
708
709             SELECT id
710             FROM ap
711             WHERE datepaid >= '$form->{fromdate}'
712               AND datepaid <= '$form->{todate}'
713
714             UNION
715
716             SELECT id
717             FROM gl
718             WHERE transdate >= '$form->{fromdate}'
719               AND transdate <= '$form->{todate}'
720           )) |;
721 #    $ar_ap_cash_where = qq| AND (a.datepaid>='$form->{fromdate}' AND a.datepaid<='$form->{todate}') |;
722   }
723
724   if ($options{beginning_balances}) {
725     foreach my $prefix (qw(from to)) {
726       next if ($form->{"${prefix}date"});
727
728       my $min_max = $prefix eq 'from' ? 'min' : 'max';
729       $query      = qq|SELECT ${min_max}(transdate)
730                        FROM acc_trans ac
731                        WHERE (1 = 1)
732                          $dpt_where_without_arapgl
733                          $project|;
734       ($form->{"${prefix}date"}) = selectfirst_array_query($form, $dbh, $query);
735     }
736
737     # get beginning balances
738     $query =
739       qq|SELECT c.accno, c.category, SUM(ac.amount) AS amount, c.description
740           FROM acc_trans ac
741           LEFT JOIN chart c ON (ac.chart_id = c.id)
742           WHERE ((select date_trunc('year', ac.transdate::date)) = (select date_trunc('year', ?::date))) AND ac.ob_transaction
743             $dpt_where_without_arapgl
744             $project
745           GROUP BY c.accno, c.category, c.description |;
746
747     $sth = prepare_execute_query($form, $dbh, $query, $form->{fromdate});
748
749     while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
750
751       if ($ref->{amount} != 0 || $form->{all_accounts}) {
752         $trb{ $ref->{accno} }{description} = $ref->{description};
753         $trb{ $ref->{accno} }{charttype}   = 'A';
754         $trb{ $ref->{accno} }{beginning_balance} = $ref->{amount};
755
756         if ($ref->{amount} > 0) {
757           $trb{ $ref->{accno} }{haben_eb}   = $ref->{amount};
758         } else {
759           $trb{ $ref->{accno} }{soll_eb}   = $ref->{amount} * -1;
760         }
761         $trb{ $ref->{accno} }{category}    = $ref->{category};
762       }
763
764     }
765     $sth->finish;
766   }
767
768   # get headings
769   $query =
770     qq|SELECT c.accno, c.description, c.category
771        FROM chart c
772        WHERE c.charttype = 'H'
773        ORDER by c.accno|;
774
775   $sth = prepare_execute_query($form, $dbh, $query);
776
777   while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
778     $trb{ $ref->{accno} }{description} = $ref->{description};
779     $trb{ $ref->{accno} }{charttype}   = 'H';
780     $trb{ $ref->{accno} }{category}    = $ref->{category};
781
782     push @headingaccounts, $ref->{accno};
783   }
784
785   $sth->finish;
786
787   $where = " 1 = 1 ";
788   my $saldowhere    = " 1 = 1 ";
789   my $sumwhere      = " 1 = 1 ";
790   my $subwhere      = '';
791   my $sumsubwhere   = '';
792   my $saldosubwhere = '';
793   my $glsaldowhere  = '';
794   my $glsubwhere    = '';
795   my $glwhere       = '';
796   my $glsumwhere    = '';
797   my $tofrom;
798   my ($fromdate, $todate);
799
800   if ($form->{fromdate} || $form->{todate}) {
801     if ($form->{fromdate}) {
802       $fromdate = conv_dateq($form->{fromdate});
803       $tofrom        .= " AND (ac.transdate >= $fromdate)";
804       $subwhere      .= " AND (ac.transdate >= $fromdate)";
805       $sumsubwhere   .= " AND (ac.transdate >= (select date_trunc('year', date $fromdate))) ";
806       $saldosubwhere .= " AND (ac,transdate>=(select date_trunc('year', date $fromdate)))  ";
807       $invwhere      .= " AND (a.transdate >= $fromdate)";
808       $glsaldowhere  .= " AND ac.transdate>=(select date_trunc('year', date $fromdate)) ";
809       $glwhere        = " AND (ac.transdate >= $fromdate)";
810       $glsumwhere     = " AND (ac.transdate >= (select date_trunc('year', date $fromdate))) ";
811     }
812     if ($form->{todate}) {
813       $todate = conv_dateq($form->{todate});
814       $tofrom        .= " AND (ac.transdate <= $todate)";
815       $invwhere      .= " AND (a.transdate <= $todate)";
816       $saldosubwhere .= " AND (ac.transdate <= $todate)";
817       $sumsubwhere   .= " AND (ac.transdate <= $todate)";
818       $subwhere      .= " AND (ac.transdate <= $todate)";
819       $glwhere       .= " AND (ac.transdate <= $todate)";
820       $glsumwhere    .= " AND (ac.transdate <= $todate) ";
821       $glsaldowhere  .= " AND (ac.transdate <= $todate) ";
822    }
823   }
824
825   if ($form->{method} eq "cash") {
826     $where .=
827       qq| AND(ac.trans_id IN (SELECT id FROM ar WHERE datepaid>= $fromdate AND datepaid<= $todate UNION SELECT id FROM ap WHERE datepaid>= $fromdate AND datepaid<= $todate UNION SELECT id FROM gl WHERE transdate>= $fromdate AND transdate<= $todate)) AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL) AND (NOT ac.cb_transaction OR ac.cb_transaction IS NULL) |;
828     $saldowhere .= qq| AND(ac.trans_id IN (SELECT id FROM ar WHERE datepaid>= $fromdate AND datepaid<= $todate UNION SELECT id FROM ap WHERE datepaid>= $fromdate AND datepaid<= $todate UNION SELECT id FROM gl WHERE transdate>= $fromdate AND transdate<= $todate))  AND (NOT ac.cb_transaction OR ac.cb_transaction IS NULL) |;
829
830     $sumwhere .= qq| AND(ac.trans_id IN (SELECT id FROM ar WHERE datepaid>= $fromdate AND datepaid<= $todate UNION SELECT id FROM ap WHERE datepaid>= $fromdate AND datepaid<= $todate UNION SELECT id FROM gl WHERE transdate>= $fromdate AND transdate<= $todate)) AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL) AND (NOT ac.cb_transaction OR ac.cb_transaction IS NULL) |;
831   } else {
832     $where .= $tofrom . " AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL) AND (NOT ac.cb_transaction OR ac.cb_transaction IS NULL)";
833     $saldowhere .= $glsaldowhere . " AND (NOT ac.cb_transaction OR ac.cb_transaction IS NULL)";
834     $sumwhere .= $glsumwhere . " AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL) AND (NOT ac.cb_transaction OR ac.cb_transaction IS NULL)";
835   }
836
837   $query = qq|
838        SELECT c.accno, c.description, c.category, SUM(ac.amount) AS amount
839        FROM acc_trans ac
840        JOIN chart c ON (c.id = ac.chart_id)
841        WHERE $where
842          $dpt_where_without_arapgl
843          $project
844        GROUP BY c.accno, c.description, c.category |;
845
846   if ($form->{project_id}) {
847     $query .= qq|
848       -- add project transactions from invoice
849
850       UNION ALL
851
852       SELECT c.accno, c.description, c.category, SUM(ac.sellprice * ac.qty) AS amount
853       FROM invoice ac
854       JOIN ar a ON (ac.trans_id = a.id)
855       JOIN parts p ON (ac.parts_id = p.id)
856       JOIN chart c ON (p.income_accno_id = c.id)
857       WHERE $invwhere
858         $dpt_where
859         $project
860       GROUP BY c.accno, c.description, c.category
861
862       UNION ALL
863
864       SELECT c.accno, c.description, c.category, SUM(ac.sellprice * ac.qty) * -1 AS amount
865       FROM invoice ac
866       JOIN ap a ON (ac.trans_id = a.id)
867       JOIN parts p ON (ac.parts_id = p.id)
868       JOIN chart c ON (p.expense_accno_id = c.id)
869       WHERE $invwhere
870         $dpt_where
871         $project
872       GROUP BY c.accno, c.description, c.category
873       |;
874     }
875
876   $query .= qq| ORDER BY accno|;
877
878   $sth = prepare_execute_query($form, $dbh, $query);
879
880   # calculate the debit and credit in the period
881   while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
882     $trb{ $ref->{accno} }{description} = $ref->{description};
883     $trb{ $ref->{accno} }{charttype}   = 'A';
884     $trb{ $ref->{accno} }{category}    = $ref->{category};
885     $trb{ $ref->{accno} }{amount} += $ref->{amount};
886   }
887   $sth->finish;
888
889   # prepare query for each account
890   my ($q_drcr, $drcr, $q_project_drcr, $project_drcr);
891
892   $q_drcr =
893     qq|SELECT
894          (SELECT SUM(ac.amount) * -1
895           FROM acc_trans ac
896           JOIN chart c ON (c.id = ac.chart_id)
897           WHERE $where
898             $dpt_where_without_arapgl
899             $project
900           AND (ac.amount < 0)
901           AND (c.accno = ?)) AS debit,
902
903          (SELECT SUM(ac.amount)
904           FROM acc_trans ac
905           JOIN chart c ON (c.id = ac.chart_id)
906           WHERE $where
907             $dpt_where_without_arapgl
908             $project
909           AND ac.amount > 0
910           AND c.accno = ?) AS credit,
911         (SELECT SUM(ac.amount)
912          FROM acc_trans ac
913          JOIN chart c ON (ac.chart_id = c.id)
914          WHERE $saldowhere
915            $dpt_where_without_arapgl
916            $project
917          AND c.accno = ? AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL)) AS saldo,
918
919         (SELECT SUM(ac.amount)
920          FROM acc_trans ac
921          JOIN chart c ON (ac.chart_id = c.id)
922          WHERE $sumwhere
923            $dpt_where_without_arapgl
924            $project
925          AND amount > 0
926          AND c.accno = ?) AS sum_credit,
927
928         (SELECT SUM(ac.amount)
929          FROM acc_trans ac
930          JOIN chart c ON (ac.chart_id = c.id)
931          WHERE $sumwhere
932            $dpt_where_without_arapgl
933            $project
934          AND amount < 0
935          AND c.accno = ?) AS sum_debit,
936
937         (SELECT max(ac.transdate) FROM acc_trans ac
938         JOIN chart c ON (ac.chart_id = c.id)
939         WHERE $where
940           $dpt_where_without_arapgl
941           $project
942         AND c.accno = ?) AS last_transaction
943
944
945  |;
946
947   $drcr = prepare_query($form, $dbh, $q_drcr);
948
949   if ($form->{project_id}) {
950     # prepare query for each account
951     $q_project_drcr =
952       qq|SELECT
953           (SELECT SUM(ac.sellprice * ac.qty) * -1
954            FROM invoice ac
955            JOIN parts p ON (ac.parts_id = p.id)
956            JOIN ap a ON (ac.trans_id = a.id)
957            JOIN chart c ON (p.expense_accno_id = c.id)
958            WHERE $invwhere
959              $dpt_where
960              $project
961            AND c.accno = ?) AS debit,
962
963           (SELECT SUM(ac.sellprice * ac.qty)
964            FROM invoice ac
965            JOIN parts p ON (ac.parts_id = p.id)
966            JOIN ar a ON (ac.trans_id = a.id)
967            JOIN chart c ON (p.income_accno_id = c.id)
968            WHERE $invwhere
969              $dpt_where
970              $project
971            AND c.accno = ?) AS credit,
972
973         (SELECT SUM(ac.amount)
974          FROM acc_trans ac
975          JOIN chart c ON (ac.chart_id = c.id)
976          WHERE $saldowhere
977            $dpt_where_without_arapgl
978            $project
979          AND c.accno = ? AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL)) AS saldo,
980
981         (SELECT SUM(ac.amount)
982          FROM acc_trans ac
983          JOIN chart c ON (ac.chart_id = c.id)
984          WHERE $sumwhere
985            $dpt_where_without_arapgl
986            $project
987          AND amount > 0
988          AND c.accno = ?) AS sum_credit,
989
990         (SELECT SUM(ac.amount)
991          FROM acc_trans ac
992          JOIN chart c ON (ac.chart_id = c.id)
993          WHERE $sumwhere
994            $dpt_where_without_arapgl
995            $project
996          AND amount < 0
997          AND c.accno = ?) AS sum_debit,
998
999
1000         (SELECT max(ac.transdate) FROM acc_trans ac
1001         JOIN chart c ON (ac.chart_id = c.id)
1002         WHERE $where
1003           $dpt_where_without_arapgl
1004           $project
1005         AND c.accno = ?) AS last_transaction
1006  |;
1007
1008     $project_drcr = prepare_query($form, $dbh, $q_project_drcr);
1009   }
1010
1011
1012   my ($debit, $credit, $saldo, $soll_saldo, $haben_saldo,$soll_kummuliert, $haben_kummuliert, $last_transaction);
1013
1014   foreach my $accno (sort keys %trb) {
1015     $ref = {};
1016
1017     $ref->{accno} = $accno;
1018     map { $ref->{$_} = $trb{$accno}{$_} }
1019       qw(description category charttype amount soll_eb haben_eb beginning_balance);
1020
1021     $ref->{balance} = $form->round_amount($balance{ $ref->{accno} }, 2);
1022
1023     if ($trb{$accno}{charttype} eq 'A') {
1024
1025       # get DR/CR
1026       do_statement($form, $drcr, $q_drcr, $ref->{accno}, $ref->{accno}, $ref->{accno}, $ref->{accno}, $ref->{accno}, $ref->{accno});
1027
1028       ($debit, $credit, $saldo, $haben_saldo, $soll_saldo) = (0, 0, 0, 0, 0);
1029       my ($soll_kumuliert, $haben_kumuliert) = (0, 0);
1030       $last_transaction = "";
1031       while (($debit, $credit, $saldo, $haben_kumuliert, $soll_kumuliert, $last_transaction) = $drcr->fetchrow_array) {
1032         $ref->{debit}  += $debit;
1033         $ref->{credit} += $credit;
1034         if ($saldo >= 0) {
1035           $ref->{haben_saldo} += $saldo;
1036         } else {
1037           $ref->{soll_saldo} += $saldo * -1;
1038         }
1039         $ref->{last_transaction} = $last_transaction;
1040         $ref->{soll_kumuliert} = $soll_kumuliert * -1;
1041         $ref->{haben_kumuliert} = $haben_kumuliert;
1042       }
1043       $drcr->finish;
1044
1045       if ($form->{project_id}) {
1046
1047         # get DR/CR
1048         do_statement($form, $project_drcr, $q_project_drcr, $ref->{accno}, $ref->{accno}, $ref->{accno}, $ref->{accno}, $ref->{accno}, $ref->{accno});
1049
1050         ($debit, $credit) = (0, 0);
1051         while (($debit, $credit, $saldo, $haben_kumuliert, $soll_kumuliert, $last_transaction) = $project_drcr->fetchrow_array) {
1052           $ref->{debit}  += $debit;
1053           $ref->{credit} += $credit;
1054           if ($saldo >= 0) {
1055             $ref->{haben_saldo} += $saldo;
1056           } else {
1057             $ref->{soll_saldo} += $saldo * -1;
1058           }
1059           $ref->{soll_kumuliert} += $soll_kumuliert * -1;
1060           $ref->{haben_kumuliert} += $haben_kumuliert;
1061         }
1062         $project_drcr->finish;
1063       }
1064
1065       $ref->{debit}  = $form->round_amount($ref->{debit},  2);
1066       $ref->{credit} = $form->round_amount($ref->{credit}, 2);
1067
1068       if ($ref->{haben_saldo} != 0) {
1069         $ref->{haben_saldo}  = $ref->{haben_saldo} + $ref->{beginning_balance};
1070         if ($ref->{haben_saldo} < 0) {
1071           $ref->{soll_saldo} = $form->round_amount(($ref->{haben_saldo} *- 1), 2);
1072           $ref->{haben_saldo} = 0;
1073         }
1074       } else {
1075         $ref->{soll_saldo} = $ref->{soll_saldo} - $ref->{beginning_balance};
1076         if ($ref->{soll_saldo} < 0) {
1077           $ref->{haben_saldo} = $form->round_amount(($ref->{soll_saldo} * -1), 2);
1078           $ref->{soll_saldo} = 0;
1079         }
1080      }
1081       $ref->{haben_saldo} = $form->round_amount($ref->{haben_saldo}, 2);
1082       $ref->{soll_saldo} = $form->round_amount($ref->{soll_saldo}, 2);
1083       $ref->{haben_kumuliert}  = $form->round_amount($ref->{haben_kumuliert},  2);
1084       $ref->{soll_kumuliert} = $form->round_amount($ref->{soll_kumuliert}, 2);
1085     }
1086
1087     # add subtotal
1088     my @accno;
1089     @accno = grep { $_ le "$ref->{accno}" } @headingaccounts;
1090     $accno = pop @accno;
1091     if ($accno) {
1092       $trb{$accno}{debit}  += $ref->{debit};
1093       $trb{$accno}{credit} += $ref->{credit};
1094       $trb{$accno}{soll_saldo}  += $ref->{soll_saldo};
1095       $trb{$accno}{haben_saldo} += $ref->{haben_saldo};
1096       $trb{$accno}{soll_kumuliert}  += $ref->{soll_kumuliert};
1097       $trb{$accno}{haben_kumuliert} += $ref->{haben_kumuliert};
1098     }
1099
1100     push @{ $form->{TB} }, $ref;
1101
1102   }
1103
1104   $dbh->disconnect;
1105
1106   # debits and credits for headings
1107   foreach my $accno (@headingaccounts) {
1108     foreach $ref (@{ $form->{TB} }) {
1109       if ($accno eq $ref->{accno}) {
1110         $ref->{debit}           = $trb{$accno}{debit};
1111         $ref->{credit}          = $trb{$accno}{credit};
1112         $ref->{soll_saldo}      = $trb{$accno}{soll_saldo};
1113         $ref->{haben_saldo}     = $trb{$accno}{haben_saldo};
1114         $ref->{soll_kumuliert}  = $trb{$accno}{soll_kumuliert};
1115         $ref->{haben_kumuliert} = $trb{$accno}{haben_kumuliert};
1116       }
1117     }
1118   }
1119
1120   $main::lxdebug->leave_sub();
1121 }
1122
1123 sub get_storno {
1124   $main::lxdebug->enter_sub();
1125   my ($self, $dbh, $form) = @_;
1126   my $arap = $form->{arap} eq "ar" ? "ar" : "ap";
1127   my $query = qq|SELECT invnumber FROM $arap WHERE invnumber LIKE "Storno zu "|;
1128   my $sth =  $dbh->prepare($query);
1129   while(my $ref = $sth->fetchrow_hashref()) {
1130     $ref->{invnumer} =~ s/Storno zu //g;
1131     $form->{storno}{$ref->{invnumber}} = 1;
1132   }
1133   $main::lxdebug->leave_sub();
1134 }
1135
1136 sub aging {
1137   $main::lxdebug->enter_sub();
1138
1139   my ($self, $myconfig, $form) = @_;
1140
1141   # connect to database
1142   my $dbh     = $form->dbconnect($myconfig);
1143
1144   my ($invoice, $arap, $buysell, $ct, $ct_id, $ml);
1145
1146   # falls customer ziehen wir die offene forderungsliste
1147   # anderfalls für die lieferanten die offenen verbindlichkeitne
1148   if ($form->{ct} eq "customer") {
1149     $invoice = "is";
1150     $arap = "ar";
1151     $buysell = "buy";
1152     $ct = "customer";
1153     $ml = -1;
1154   } else {
1155     $invoice = "ir";
1156     $arap = "ap";
1157     $buysell = "sell";
1158     $ct = "vendor";
1159     $ml = 1;
1160   }
1161   $ct_id = "${ct}_id";
1162
1163   # erweiterung um einen freien zeitraum oder einen stichtag
1164   # mit entsprechender altersstrukturliste (s.a. Bug 1842)
1165   # eine neue variable an der oberfläche eingeführt, somit ist
1166   # todate == freier zeitrau und fordate == stichtag
1167
1168   my ($review_of_aging_list, $todate, $fromdate, $fromwhere, $fordate);
1169
1170   if ($form->{reporttype} eq 'custom') {  # altersstrukturliste
1171
1172     # explizit rausschmeissen was man für diesen bericht nicht braucht
1173     delete $form->{fromdate};
1174     delete $form->{todate};
1175
1176     # an der oberfläche ist das tagesaktuelle datum vorausgewählt
1177     # falls es dennoch per Benutzereingabe gelöscht wird, lieber wieder vorbelegen
1178     # ferner muss für die spätere DB-Abfrage muss todate gesetzt sein.
1179     $form->{fordate}  = $form->current_date($myconfig) unless ($form->{fordate});
1180     $fordate          = conv_dateq($form->{fordate});
1181     $todate           = $fordate;
1182
1183     if ($form->{review_of_aging_list}) { # falls die liste leer ist, alles anzeigen
1184       if ($form->{review_of_aging_list} =~ m "-") {             # ..  periode von bis
1185         my @period = split(/-/, $form->{review_of_aging_list}); # ... von periode bis periode
1186         $review_of_aging_list = " AND $period[0] <  (date $fordate) - duedate
1187                                   AND (date $fordate) - duedate  < $period[1]";
1188       } else {
1189         $form->{review_of_aging_list} =~ s/[^0-9]//g;   # größer 120 das substitute ist nur für das '>' zeichen
1190         $review_of_aging_list = " AND $form->{review_of_aging_list} < (date $fordate) - duedate";
1191       }
1192     }
1193   } else {  # freier zeitraum OHNE review_of_aging_list
1194     $form->{todate}  = $form->current_date($myconfig) unless ($form->{todate});
1195     $todate = conv_dateq($form->{todate});
1196     $fromdate = conv_dateq($form->{fromdate});
1197     $fromwhere = ($form->{fromdate} ne "") ? " AND (transdate >= (date $fromdate)) " : "";
1198   }
1199   my $where = " 1 = 1 ";
1200   my ($name, $null);
1201
1202   if ($form->{$ct_id}) {
1203     $where .= qq| AND (ct.id = | . conv_i($form->{$ct_id}) . qq|)|;
1204   } elsif ($form->{ $form->{ct} }) {
1205     $where .= qq| AND (ct.name ILIKE | . $dbh->quote('%' . $form->{$ct} . '%') . qq|)|;
1206   }
1207
1208   my $dpt_join;
1209   my $where_dpt;
1210   if ($form->{department}) {
1211     my ($null, $department_id) = split /--/, $form->{department};
1212     $dpt_join = qq| JOIN department d ON (a.department_id = d.id) |;
1213     $where .= qq| AND (a.department_id = | . conv_i($department_id, 'NULL') . qq|)|;
1214     $where_dpt = qq| AND (${arap}.department_id = | . conv_i($department_id, 'NULL') . qq|)|;
1215   }
1216  my $q_details = qq|
1217
1218     SELECT ${ct}.id AS ctid, ${ct}.name,
1219       street, zipcode, city, country, contact, email,
1220       phone as customerphone, fax as customerfax, ${ct}number,
1221       "invnumber", "transdate",
1222       (amount - COALESCE((SELECT sum(amount)*$ml FROM acc_trans WHERE chart_link ilike '%paid%' AND acc_trans.trans_id=${arap}.id AND acc_trans.transdate <= (date $todate)),0)) as "open", "amount",
1223       "duedate", invoice, ${arap}.id, date_part('days', now() - duedate) as overduedays,
1224       (SELECT $buysell
1225        FROM exchangerate
1226        WHERE (${arap}.curr = exchangerate.curr)
1227          AND (exchangerate.transdate = ${arap}.transdate)) AS exchangerate
1228     FROM ${arap}, ${ct}
1229     WHERE ((paid != amount) OR (datepaid > (date $todate) AND datepaid is not null))
1230       AND NOT COALESCE (${arap}.storno, 'f')
1231       AND (${arap}.${ct}_id = ${ct}.id)
1232       $where_dpt
1233       AND (${ct}.id = ?)
1234       AND (transdate <= (date $todate) $fromwhere )
1235       $review_of_aging_list
1236     ORDER BY ctid, transdate, invnumber |;
1237
1238   my $sth_details = prepare_query($form, $dbh, $q_details);
1239
1240   # select outstanding vendors or customers, depends on $ct
1241   my $query =
1242     qq|SELECT DISTINCT ct.id, ct.name
1243        FROM $ct ct, $arap a
1244        $dpt_join
1245        WHERE $where
1246          AND (a.${ct_id} = ct.id)
1247          AND ((a.paid != a.amount) OR ((a.datepaid > $todate) AND (datepaid is NOT NULL)))
1248          AND (a.transdate <= $todate $fromwhere)
1249        ORDER BY ct.name|;
1250
1251   my $sth = prepare_execute_query($form, $dbh, $query);
1252
1253   $form->{AG} = [];
1254   # for each company that has some stuff outstanding
1255   while (my ($id) = $sth->fetchrow_array) {
1256     do_statement($form, $sth_details, $q_details, $id);
1257
1258     while (my $ref = $sth_details->fetchrow_hashref("NAME_lc")) {
1259       $ref->{module} = ($ref->{invoice}) ? $invoice : $arap;
1260       $ref->{exchangerate} = 1 unless $ref->{exchangerate};
1261       push @{ $form->{AG} }, $ref;
1262     }
1263
1264     $sth_details->finish;
1265
1266   }
1267
1268   $sth->finish;
1269
1270   # disconnect
1271   $dbh->disconnect;
1272
1273   $main::lxdebug->leave_sub();
1274 }
1275
1276 sub get_customer {
1277   $main::lxdebug->enter_sub();
1278
1279   my ($self, $myconfig, $form) = @_;
1280
1281   # connect to database
1282   my $dbh = $form->dbconnect($myconfig);
1283
1284   my $ct = $form->{ct} eq "customer" ? "customer" : "vendor";
1285
1286   my $query =
1287     qq|SELECT ct.name, ct.email, ct.cc, ct.bcc
1288        FROM $ct ct
1289        WHERE ct.id = ?|;
1290   ($form->{ $form->{ct} }, $form->{email}, $form->{cc}, $form->{bcc}) =
1291     selectrow_query($form, $dbh, $query, $form->{"${ct}_id"});
1292   $dbh->disconnect;
1293
1294   $main::lxdebug->leave_sub();
1295 }
1296
1297 sub tax_report {
1298   $main::lxdebug->enter_sub();
1299
1300   my ($self, $myconfig, $form) = @_;
1301
1302   # connect to database
1303   my $dbh = $form->dbconnect($myconfig);
1304
1305   my ($null, $department_id) = split /--/, $form->{department};
1306
1307   # build WHERE
1308   my $where = "1 = 1";
1309
1310   if ($department_id) {
1311     $where .= qq| AND (a.department_id = | . conv_i($department_id, 'NULL') . qq|) |;
1312   }
1313
1314   my ($accno, $rate);
1315
1316   if ($form->{accno}) {
1317     $accno = $form->{accno};
1318     $rate  = $form->{"$form->{accno}_rate"};
1319     $accno = qq| AND (ch.accno = | . $dbh->quote($accno) . qq|)|;
1320   }
1321   $rate *= 1;
1322
1323   my ($table, $ARAP);
1324
1325   if ($form->{db} eq 'ar') {
1326     $table = "customer";
1327     $ARAP  = "AR";
1328   } else {
1329     $table = "vendor";
1330     $ARAP  = "AP";
1331   }
1332
1333   my $arap = lc($ARAP);
1334
1335   my $transdate = "a.transdate";
1336
1337   if ($form->{method} eq 'cash') {
1338     $transdate = "a.datepaid";
1339
1340     my $todate = conv_dateq($form->{todate} ? $form->{todate} : $form->current_date($myconfig));
1341
1342     $where .= qq|
1343       AND ac.trans_id IN
1344         (
1345           SELECT trans_id
1346           FROM acc_trans a
1347           WHERE (a.chart_link LIKE '%${ARAP}_paid%')
1348           AND (transdate <= $todate)
1349         )
1350       |;
1351   }
1352
1353   # if there are any dates construct a where
1354   $where .= " AND ($transdate >= " . conv_dateq($form->{fromdate}) . ") " if ($form->{fromdate});
1355   $where .= " AND ($transdate <= " . conv_dateq($form->{todate}) . ") " if ($form->{todate});
1356
1357   my $ml = ($form->{db} eq 'ar') ? 1 : -1;
1358
1359   my $sortorder = join ', ', $form->sort_columns(qw(transdate invnumber name));
1360   $sortorder = $form->{sort} if ($form->{sort} && grep({ $_ eq $form->{sort} } qw(id transdate invnumber name netamount tax)));
1361
1362   my $query =
1363       qq|SELECT a.id, '0' AS invoice, $transdate AS transdate, a.invnumber, n.name, a.netamount,
1364           ac.amount * $ml AS tax
1365          FROM acc_trans ac
1366          JOIN ${arap} a ON (a.id = ac.trans_id)
1367          JOIN chart ch ON (ch.id = ac.chart_id)
1368          JOIN $table n ON (n.id = a.${table}_id)
1369          WHERE
1370            $where
1371            $accno
1372            AND (a.invoice = '0')
1373
1374          UNION
1375
1376          SELECT a.id, '1' AS invoice, $transdate AS transdate, a.invnumber, n.name, i.sellprice * i.qty AS netamount,
1377            i.sellprice * i.qty * $rate * $ml AS tax
1378          FROM acc_trans ac
1379          JOIN ${arap} a ON (a.id = ac.trans_id)
1380          JOIN chart ch ON (ch.id = ac.chart_id)
1381          JOIN $table n ON (n.id = a.${table}_id)
1382          JOIN ${table}tax t ON (t.${table}_id = n.id)
1383          JOIN invoice i ON (i.trans_id = a.id)
1384          WHERE
1385            $where
1386            $accno
1387            AND (a.invoice = '1')
1388          ORDER BY $sortorder|;
1389
1390   $form->{TR} = selectall_hashref_query($form, $dbh, $query);
1391
1392   $dbh->disconnect;
1393
1394   $main::lxdebug->leave_sub();
1395 }
1396
1397 sub paymentaccounts {
1398   $main::lxdebug->enter_sub();
1399
1400   my ($self, $myconfig, $form) = @_;
1401
1402   # connect to database, turn AutoCommit off
1403   my $dbh = $form->dbconnect_noauto($myconfig);
1404
1405   my $ARAP = $form->{db} eq "ar" ? "AR" : "AP";
1406
1407   # get A(R|P)_paid accounts
1408   my $query =
1409     qq|SELECT accno, description
1410        FROM chart
1411        WHERE link LIKE '%${ARAP}_paid%'|;
1412   $form->{PR} = selectall_hashref_query($form, $dbh, $query);
1413
1414   $dbh->disconnect;
1415
1416   $main::lxdebug->leave_sub();
1417 }
1418
1419 sub payments {
1420   $main::lxdebug->enter_sub();
1421
1422   my ($self, $myconfig, $form) = @_;
1423
1424   # connect to database, turn AutoCommit off
1425   my $dbh = $form->dbconnect_noauto($myconfig);
1426
1427   my $ml = 1;
1428   my $arap;
1429   my $table;
1430   if ($form->{db} eq 'ar') {
1431     $table = 'customer';
1432     $ml = -1;
1433     $arap = 'ar';
1434   } else {
1435     $table = 'vendor';
1436     $arap = 'ap';
1437   }
1438
1439   my ($query, $sth);
1440   my $where;
1441
1442   if ($form->{department_id}) {
1443     $where = qq| AND (a.department_id = | . conv_i($form->{department_id}, 'NULL') . qq|) |;
1444   }
1445
1446   if ($form->{fromdate}) {
1447     $where .= " AND (ac.transdate >= " . $dbh->quote($form->{fromdate}) . ") ";
1448   }
1449   if ($form->{todate}) {
1450     $where .= " AND (ac.transdate <= " . $dbh->quote($form->{todate}) . ") ";
1451   }
1452   if (!$form->{fx_transaction}) {
1453     $where .= " AND ac.fx_transaction = '0'";
1454   }
1455
1456   my $invnumber;
1457   my $reference;
1458   if ($form->{reference}) {
1459     $reference = $dbh->quote('%' . $form->{reference} . '%');
1460     $invnumber = " AND (a.invnumber LIKE $reference)";
1461     $reference = " AND (a.reference LIKE $reference)";
1462   }
1463   if ($form->{source}) {
1464     $where .= " AND (ac.source ILIKE " . $dbh->quote('%' . $form->{source} . '%') . ") ";
1465   }
1466   if ($form->{memo}) {
1467     $where .= " AND (ac.memo ILIKE " . $dbh->quote('%' . $form->{memo} . '%') . ") ";
1468   }
1469
1470   my %sort_columns =  (
1471     'transdate'    => [ qw(transdate lower_invnumber lower_name) ],
1472     'invnumber'    => [ qw(lower_invnumber lower_name transdate) ],
1473     'name'         => [ qw(lower_name transdate)                 ],
1474     'source'       => [ qw(lower_source)                         ],
1475     'memo'         => [ qw(lower_memo)                           ],
1476     );
1477   my %lowered_columns =  (
1478     'invnumber'       => { 'gl' => 'a.reference',   'arap' => 'a.invnumber', },
1479     'memo'            => { 'gl' => 'ac.memo',       'arap' => 'ac.memo',     },
1480     'source'          => { 'gl' => 'ac.source',     'arap' => 'ac.source',   },
1481     'name'            => { 'gl' => 'a.description', 'arap' => 'c.name',      },
1482     );
1483
1484   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
1485   my $sortkey   = $sort_columns{$form->{sort}} ? $form->{sort} : 'transdate';
1486   my $sortorder = join ', ', map { "$_ $sortdir" } @{ $sort_columns{$sortkey} };
1487
1488
1489   my %columns_for_sorting = ( 'gl' => '', 'arap' => '', );
1490   foreach my $spec (@{ $sort_columns{$sortkey} }) {
1491     next if ($spec !~ m/^lower_(.*)$/);
1492
1493     my $column = $1;
1494     map { $columns_for_sorting{$_} .= sprintf(', lower(%s) AS lower_%s', $lowered_columns{$column}->{$_}, $column) } qw(gl arap);
1495   }
1496
1497   $query = qq|SELECT id, accno, description FROM chart WHERE accno = ?|;
1498   $sth = prepare_query($form, $dbh, $query);
1499
1500   my $q_details =
1501       qq|SELECT c.name, a.invnumber, a.ordnumber,
1502            ac.transdate, ac.amount * $ml AS paid, ac.source,
1503            a.invoice, a.id, ac.memo, '${arap}' AS module
1504            $columns_for_sorting{arap}
1505          FROM acc_trans ac
1506          JOIN $arap a ON (ac.trans_id = a.id)
1507          JOIN $table c ON (c.id = a.${table}_id)
1508          WHERE (ac.chart_id = ?)
1509            $where
1510            $invnumber
1511
1512          UNION
1513
1514          SELECT a.description, a.reference, NULL AS ordnumber,
1515            ac.transdate, ac.amount * $ml AS paid, ac.source,
1516            '0' as invoice, a.id, ac.memo, 'gl' AS module
1517            $columns_for_sorting{gl}
1518          FROM acc_trans ac
1519          JOIN gl a ON (a.id = ac.trans_id)
1520          WHERE (ac.chart_id = ?)
1521            $where
1522            $reference
1523            AND (ac.amount * $ml) > 0
1524
1525          ORDER BY $sortorder|;
1526   my $sth_details = prepare_query($form, $dbh, $q_details);
1527
1528   $form->{PR} = [];
1529
1530   # cycle through each id
1531   foreach my $accno (split(/ /, $form->{paymentaccounts})) {
1532     do_statement($form, $sth, $query, $accno);
1533     my $ref = $sth->fetchrow_hashref();
1534     push(@{ $form->{PR} }, $ref);
1535     $sth->finish();
1536
1537     $form->{ $ref->{id} } = [] unless ($form->{ $ref->{id} });
1538
1539     do_statement($form, $sth_details, $q_details, $ref->{id}, $ref->{id});
1540     while (my $pr = $sth_details->fetchrow_hashref()) {
1541       push(@{ $form->{ $ref->{id} } }, $pr);
1542     }
1543     $sth_details->finish();
1544   }
1545
1546   $dbh->disconnect;
1547
1548   $main::lxdebug->leave_sub();
1549 }
1550
1551 sub bwa {
1552   $main::lxdebug->enter_sub();
1553
1554   my ($self, $myconfig, $form) = @_;
1555
1556   # connect to database
1557   my $dbh = $form->dbconnect($myconfig);
1558
1559   my $last_period = 0;
1560   my $category;
1561   my @categories  =
1562     qw(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40);
1563
1564   $form->{decimalplaces} *= 1;
1565
1566   &get_accounts_g($dbh, $last_period, $form->{fromdate}, $form->{todate}, $form, "pos_bwa");
1567
1568   # if there are any compare dates
1569   my $year;
1570   if ($form->{fromdate} || $form->{todate}) {
1571     $last_period = 1;
1572     if ($form->{fromdate}) {
1573       $form->{fromdate} =~ /[0-9]*\.[0-9]*\.([0-9]*)/;
1574       $year = $1;
1575     } else {
1576       $form->{todate} =~ /[0-9]*\.[0-9]*\.([0-9]*)/;
1577       $year = $1;
1578     }
1579     my $kummfromdate = $form->{comparefromdate};
1580     my $kummtodate   = $form->{comparetodate};
1581     &get_accounts_g($dbh, $last_period, $kummfromdate, $kummtodate, $form, "pos_bwa");
1582   }
1583
1584   my @periods        = qw(jetzt kumm);
1585   my @gesamtleistung = qw(1 3);
1586   my @gesamtkosten   = qw (10 11 12 13 14 15 16 17 18 20);
1587   my @ergebnisse     =
1588     qw (rohertrag betriebrohertrag betriebsergebnis neutraleraufwand neutralerertrag ergebnisvorsteuern ergebnis gesamtleistung gesamtkosten);
1589
1590   foreach my $key (@periods) {
1591     $form->{ "$key" . "gesamtleistung" } = 0;
1592     $form->{ "$key" . "gesamtkosten" }   = 0;
1593
1594     foreach $category (@categories) {
1595
1596       if (defined($form->{$category}{$key})) {
1597         $form->{"$key$category"} =
1598           $form->format_amount($myconfig,
1599                                $form->round_amount($form->{$category}{$key}, 2
1600                                ),
1601                                $form->{decimalplaces},
1602                                '0');
1603       }
1604     }
1605     foreach my $item (@gesamtleistung) {
1606       $form->{ "$key" . "gesamtleistung" } += $form->{$item}{$key};
1607     }
1608     $form->{ "$key" . "gesamtleistung" } -= $form->{2}{$key};
1609
1610     foreach my $item (@gesamtkosten) {
1611       $form->{ "$key" . "gesamtkosten" } += $form->{$item}{$key};
1612     }
1613     $form->{ "$key" . "rohertrag" } =
1614       $form->{ "$key" . "gesamtleistung" } - $form->{4}{$key};
1615     $form->{ "$key" . "betriebrohertrag" } =
1616       $form->{ "$key" . "rohertrag" } + $form->{5}{$key};
1617     $form->{ "$key" . "betriebsergebnis" } =
1618       $form->{ "$key" . "betriebrohertrag" } -
1619       $form->{ "$key" . "gesamtkosten" };
1620     $form->{ "$key" . "neutraleraufwand" } =
1621       $form->{19}{$key} + $form->{30}{$key} + $form->{31}{$key};
1622     $form->{ "$key" . "neutralerertrag" } =
1623       $form->{32}{$key} + $form->{33}{$key} + $form->{34}{$key};
1624     $form->{ "$key" . "ergebnisvorsteuern" } =
1625       $form->{ "$key" . "betriebsergebnis" } -
1626       $form->{ "$key" . "neutraleraufwand" } +
1627       $form->{ "$key" . "neutralerertrag" };
1628     $form->{ "$key" . "ergebnis" } =
1629       $form->{ "$key" . "ergebnisvorsteuern" } - $form->{35}{$key};
1630
1631     if ($form->{ "$key" . "gesamtleistung" } > 0) {
1632       foreach $category (@categories) {
1633         if (defined($form->{$category}{$key})) {
1634           $form->{ "$key" . "gl" . "$category" } =
1635             $form->format_amount(
1636                                $myconfig,
1637                                $form->round_amount(
1638                                  ($form->{$category}{$key} /
1639                                     $form->{ "$key" . "gesamtleistung" } * 100
1640                                  ),
1641                                  $form->{decimalplaces}
1642                                ),
1643                                $form->{decimalplaces},
1644                                '0');
1645         }
1646       }
1647       foreach my $item (@ergebnisse) {
1648         $form->{ "$key" . "gl" . "$item" } =
1649           $form->format_amount($myconfig,
1650                                $form->round_amount(
1651                                  ( $form->{ "$key" . "$item" } /
1652                                      $form->{ "$key" . "gesamtleistung" } * 100
1653                                  ),
1654                                  $form->{decimalplaces}
1655                                ),
1656                                $form->{decimalplaces},
1657                                '0');
1658       }
1659     }
1660
1661     if ($form->{ "$key" . "gesamtkosten" } > 0) {
1662       foreach $category (@categories) {
1663         if (defined($form->{$category}{$key})) {
1664           $form->{ "$key" . "gk" . "$category" } =
1665             $form->format_amount($myconfig,
1666                                  $form->round_amount(
1667                                    ($form->{$category}{$key} /
1668                                       $form->{ "$key" . "gesamtkosten" } * 100
1669                                    ),
1670                                    $form->{decimalplaces}
1671                                  ),
1672                                  $form->{decimalplaces},
1673                                  '0');
1674         }
1675       }
1676       foreach my $item (@ergebnisse) {
1677         $form->{ "$key" . "gk" . "$item" } =
1678           $form->format_amount($myconfig,
1679                                $form->round_amount(
1680                                    ($form->{ "$key" . "$item" } /
1681                                       $form->{ "$key" . "gesamtkosten" } * 100
1682                                    ),
1683                                    $form->{decimalplaces}
1684                                ),
1685                                $form->{decimalplaces},
1686                                '0');
1687       }
1688     }
1689
1690     if ($form->{10}{$key} > 0) {
1691       foreach $category (@categories) {
1692         if (defined($form->{$category}{$key})) {
1693           $form->{ "$key" . "pk" . "$category" } =
1694             $form->format_amount(
1695                         $myconfig,
1696                         $form->round_amount(
1697                           ($form->{$category}{$key} / $form->{10}{$key} * 100),
1698                           $form->{decimalplaces}
1699                         ),
1700                         $form->{decimalplaces},
1701                         '0');
1702         }
1703       }
1704       foreach my $item (@ergebnisse) {
1705         $form->{ "$key" . "pk" . "$item" } =
1706           $form->format_amount($myconfig,
1707                                $form->round_amount(
1708                                                 ($form->{ "$key" . "$item" } /
1709                                                    $form->{10}{$key} * 100
1710                                                 ),
1711                                                 $form->{decimalplaces}
1712                                ),
1713                                $form->{decimalplaces},
1714                                '0');
1715       }
1716     }
1717
1718     if ($form->{4}{$key} > 0) {
1719       foreach $category (@categories) {
1720         if (defined($form->{$category}{$key})) {
1721           $form->{ "$key" . "auf" . "$category" } =
1722             $form->format_amount(
1723                          $myconfig,
1724                          $form->round_amount(
1725                            ($form->{$category}{$key} / $form->{4}{$key} * 100),
1726                            $form->{decimalplaces}
1727                          ),
1728                          $form->{decimalplaces},
1729                          '0');
1730         }
1731       }
1732       foreach my $item (@ergebnisse) {
1733         $form->{ "$key" . "auf" . "$item" } =
1734           $form->format_amount($myconfig,
1735                                $form->round_amount(
1736                                                 ($form->{ "$key" . "$item" } /
1737                                                    $form->{4}{$key} * 100
1738                                                 ),
1739                                                 $form->{decimalplaces}
1740                                ),
1741                                $form->{decimalplaces},
1742                                '0');
1743       }
1744     }
1745
1746     foreach my $item (@ergebnisse) {
1747       $form->{ "$key" . "$item" } =
1748         $form->format_amount($myconfig,
1749                              $form->round_amount($form->{ "$key" . "$item" },
1750                                                  $form->{decimalplaces}
1751                              ),
1752                              $form->{decimalplaces},
1753                              '0');
1754     }
1755
1756   }
1757   $dbh->disconnect;
1758
1759   $main::lxdebug->leave_sub();
1760 }
1761
1762 sub income_statement {
1763   $main::lxdebug->enter_sub();
1764
1765   my ($self, $myconfig, $form) = @_;
1766
1767   # connect to database
1768   my $dbh = $form->dbconnect($myconfig);
1769
1770   my $last_period          = 0;
1771   my @categories_einnahmen = qw(1 2 3 4 5 6 7);
1772   my @categories_ausgaben  =
1773     qw(8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31);
1774
1775   my @ergebnisse = qw(sumeura sumeurb guvsumme);
1776
1777   $form->{decimalplaces} *= 1;
1778
1779
1780
1781   &get_accounts_g($dbh, $last_period, $form->{fromdate}, $form->{todate},
1782                   $form, "pos_eur");
1783
1784
1785   foreach my $item (@categories_einnahmen) {
1786     $form->{"eur${item}"} =
1787       $form->format_amount($myconfig, $form->round_amount($form->{$item}, 2),2);
1788     $form->{"sumeura"} += $form->{$item};
1789   }
1790   foreach my $item (@categories_ausgaben) {
1791     $form->{"eur${item}"} =
1792       $form->format_amount($myconfig, $form->round_amount($form->{$item}, 2),2);
1793     $form->{"sumeurb"} += $form->{$item};
1794   }
1795
1796   $form->{"guvsumme"} = $form->{"sumeura"} - $form->{"sumeurb"};
1797
1798   foreach my $item (@ergebnisse) {
1799     $form->{$item} =
1800       $form->format_amount($myconfig, $form->round_amount($form->{$item}, 2),2);
1801   }
1802   $main::lxdebug->leave_sub();
1803 }
1804 1;