Ein Query in SL/CA auf DBUTils umgestellt.
[kivitendo-erp.git] / SL / CA.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) 2001
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 #  Contributors:
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 # chart of accounts
31 #
32 # CHANGE LOG:
33 #   DS. 2000-07-04  Created
34 #
35 #======================================================================
36
37 package CA;
38 use Data::Dumper;
39 use SL::DBUtils;
40
41 sub all_accounts {
42   $main::lxdebug->enter_sub();
43
44   my ($self, $myconfig, $form, $chart_id) = @_;
45
46   my %amount;
47
48   # connect to database
49   my $dbh = $form->dbconnect($myconfig);
50
51   if ($form->{method} eq "cash") {
52     $acc_cash_where = qq| AND (a.trans_id IN (SELECT id FROM ar WHERE datepaid>=(select date_trunc('year', current_date)) UNION SELECT id FROM ap WHERE datepaid>=(select date_trunc('year', current_date)) UNION SELECT id FROM gl WHERE transdate>=(select date_trunc('year', current_date)))) |;
53   } else {
54     $acc_cash_where = " AND ((select date_trunc('year', a.transdate::date)) >= (select date_trunc('year', current_date)))";
55   }
56
57   my $query =
58     qq|SELECT c.accno, SUM(a.amount) AS amount | .
59     qq|FROM chart c, acc_trans a | .
60     qq|WHERE c.id = a.chart_id | .
61     qq|$acc_cash_where| .
62     qq|GROUP BY c.accno|;
63
64   foreach my $ref (selectall_hashref_query($form, $dbh, $query)) {
65     $amount{ $ref->{accno} } = $ref->{amount};
66   }
67
68   my $where = "AND c.id = $chart_id" if ($chart_id ne '');
69
70   $query = qq{
71     SELECT
72       c.accno,
73       c.id,
74       c.description,
75       c.charttype,
76       c.category,
77       c.link,
78       c.pos_bwa,
79       c.pos_bilanz,
80       c.pos_eur,
81       c.valid_from,
82       c.datevautomatik,
83       comma(tk.startdate::text) AS startdate,
84       comma(tk.taxkey_id::text) AS taxkey,
85       comma(tx.taxdescription || to_char (tx.rate, '99V99' ) || '%') AS taxdescription,
86       comma(tx.taxnumber::text) AS taxaccount,
87       comma(tk.pos_ustva::text) AS tk_ustva,
88       ( SELECT accno
89       FROM chart c2
90       WHERE c2.id = c.id
91       ) AS new_account
92     FROM chart c
93     LEFT JOIN taxkeys tk ON (c.id = tk.chart_id)
94     LEFT JOIN tax tx ON (tk.tax_id = tx.id)
95     WHERE 1=1
96     $where
97     GROUP BY c.accno, c.id, c.description, c.charttype, c.gifi_accno,
98       c.category, c.link, c.pos_bwa, c.pos_bilanz, c.pos_eur, c.valid_from,
99       c.datevautomatik
100     ORDER BY c.accno
101   };
102
103   my $sth = prepare_execute_query($form, $dbh, $query);
104
105   $form->{CA} = [];
106
107   while (my $ca = $sth->fetchrow_hashref(NAME_lc)) {
108     $ca->{amount} = $amount{ $ca->{accno} };
109     if ($ca->{amount} < 0) {
110       $ca->{debit} = $ca->{amount} * -1;
111     } else {
112       $ca->{credit} = $ca->{amount};
113     }
114     push(@{ $form->{CA} }, $ca);
115   }
116
117   $sth->finish;
118   $dbh->disconnect;
119
120   $main::lxdebug->leave_sub();
121 }
122
123 sub all_transactions {
124   $main::lxdebug->enter_sub();
125
126   my ($self, $myconfig, $form) = @_;
127
128   # connect to database
129   my $dbh = $form->dbconnect($myconfig);
130
131   # get chart_id
132   my $query = qq|SELECT id FROM chart WHERE accno = ?|;
133   my @id = selectall_array_query($form, $dbh, $query, $form->{accno});
134
135   my $fromdate_where;
136   my $todate_where;
137
138   my $where = qq|1 = 1|;
139
140   # build WHERE clause from dates if any
141   #  if ($form->{fromdate}) {
142   #    $where .= " AND ac.transdate >= '$form->{fromdate}'";
143   #  }
144   #  if ($form->{todate}) {
145   #    $where .= " AND ac.transdate <= '$form->{todate}'";
146   #  }
147
148   my (@values, @where_values, @subwhere_values);
149   if ($form->{fromdate}) {
150     $where .= qq| AND ac.transdate >= ?|;
151     $subwhere .= qq| AND transdate >= ?|;
152     push(@where_values, conv_date($form->{fromdate}));
153     push(@subwhere_values, conv_date($form->{fromdate}));
154   }
155
156   if ($form->{todate}) {
157     $where .= qq| AND ac.transdate <= ?|;
158     $subwhere .= qq| AND transdate <= ?|;
159     push(@where_values, conv_date($form->{todate}));
160     push(@subwhere_values, conv_date($form->{todate}));
161   }
162
163
164   my $sortorder = join ', ',
165     $form->sort_columns(qw(transdate reference description));
166   my $false = ($myconfig->{dbdriver} eq 'Pg') ? FALSE: q|'0'|;
167
168   # Oracle workaround, use ordinal positions
169   my %ordinal = (transdate   => 4,
170                  reference   => 2,
171                  description => 3);
172   map { $sortorder =~ s/$_/$ordinal{$_}/ } keys %ordinal;
173
174   my ($null, $department_id) = split(/--/, $form->{department});
175   my ($dpt_where, $dpt_join, @department_values);
176   if ($department_id) {
177     $dpt_join = qq| JOIN department t ON (t.id = a.department_id) |;
178     $dpt_where = qq| AND t.id = ? |;
179     @department_values = ($department_id);
180   }
181
182   my ($project, @project_values);
183   if ($form->{project_id}) {
184     $project = qq| AND ac.project_id = ? |;
185     @project_values = (conv_i($form->{project_id}));
186   }
187   my $acc_cash_where = "";
188   my $ar_cash_where = "";
189   my $ap_cash_where = "";
190
191
192   if ($form->{method} eq "cash") {
193     $where = qq| (ac.trans_id IN (SELECT id FROM ar WHERE datepaid>= ? AND datepaid<= ? UNION SELECT id FROM ap WHERE datepaid>= ? AND datepaid<= ? UNION SELECT id FROM gl WHERE transdate>= ? AND transdate<= ?)) |;
194     @where_values = ();
195     push(@where_values, conv_date($form->{fromdate}));
196     push(@where_values, conv_date($form->{todate}));
197     push(@where_values, conv_date($form->{fromdate}));
198     push(@where_values, conv_date($form->{todate}));
199     push(@where_values, conv_date($form->{fromdate}));
200     push(@where_values, conv_date($form->{todate}));
201  }
202
203
204   if ($form->{accno}) {
205
206     # get category for account
207     $query = qq|SELECT category FROM chart WHERE accno = ?|;
208     ($form->{category}) = selectrow_query($form, $dbh, $query, $form->{accno});
209
210     if ($form->{fromdate}) {
211       # get beginning balances
212       $query =
213         qq|SELECT SUM(ac.amount) AS amount
214             FROM acc_trans ac
215             JOIN chart c ON (ac.chart_id = c.id)
216             WHERE ((select date_trunc('year', ac.transdate::date)) = (select date_trunc('year', ?::date))) AND ac.ob_transaction
217               $project
218             AND c.accno = ?|;
219
220       ($form->{beginning_balance}) = selectrow_query($form, $dbh, $query, $form->{fromdate}, $form->{accno});
221
222       # get last transaction date
223       my $todate = ($form->{todate}) ? " AND ac.transdate <= '$form->{todate}' " : "";
224       $query = qq|SELECT max(ac.transdate) FROM acc_trans ac LEFT JOIN chart c ON (ac.chart_id = c.id) WHERE ((select date_trunc('year', ac.transdate::date)) >= (select date_trunc('year', ?::date))) $todate AND c.accno = ?|;
225       ($form->{last_transaction}) = selectrow_query($form, $dbh, $query, $form->{fromdate}, $form->{accno});
226
227       # get old saldo
228       $query = qq|SELECT sum(ac.amount) FROM acc_trans ac LEFT JOIN chart c ON (ac.chart_id = c.id) WHERE ((select date_trunc('year', ac.transdate::date)) >= (select date_trunc('year', ?::date))) AND ac.transdate < ? AND c.accno = ?  AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL)|;
229       ($form->{saldo_old}) = selectrow_query($form, $dbh, $query, $form->{fromdate}, $form->{fromdate}, $form->{accno});
230
231       #get old balance
232       $query = qq|SELECT sum(ac.amount) FROM acc_trans ac LEFT JOIN chart c ON (ac.chart_id = c.id) WHERE ((select date_trunc('year', ac.transdate::date)) >= (select date_trunc('year', ?::date))) AND ac.transdate < ? AND c.accno = ? AND ac.amount < 0 AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL)|;
233       ($form->{old_balance_debit}) = selectrow_query($form, $dbh, $query, $form->{fromdate}, $form->{fromdate}, $form->{accno});
234
235       $query = qq|SELECT sum(ac.amount) FROM acc_trans ac LEFT JOIN chart c ON (ac.chart_id = c.id) WHERE ((select date_trunc('year', ac.transdate::date)) >= (select date_trunc('year', ?::date))) AND ac.transdate < ? AND c.accno = ? AND ac.amount > 0 AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL)|;
236       ($form->{old_balance_credit}) = selectrow_query($form, $dbh, $query, $form->{fromdate}, $form->{fromdate}, $form->{accno});
237
238       # get current saldo
239       my $todate = ($form->{todate} ne "") ? " AND ac.transdate <= '$form->{todate}' " : "";
240       $query = qq|SELECT sum(ac.amount) FROM acc_trans ac LEFT JOIN chart c ON (ac.chart_id = c.id) WHERE ((select date_trunc('year', ac.transdate::date)) >= (select date_trunc('year', ?::date))) $todate AND c.accno = ? AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL)|;
241       ($form->{saldo_new}) = selectrow_query($form, $dbh, $query, $form->{fromdate}, $form->{accno});
242
243       #get current balance
244       my $todate = ($form->{todate} ne "") ? " AND ac.transdate <= '$form->{todate}' " : "";
245       $query = qq|SELECT sum(ac.amount) FROM acc_trans ac LEFT JOIN chart c ON (ac.chart_id = c.id) WHERE ((select date_trunc('year', ac.transdate::date)) >= (select date_trunc('year', ?::date))) $todate AND c.accno = ? AND ac.amount < 0 AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL)|;
246       ($form->{current_balance_debit}) = selectrow_query($form, $dbh, $query, $form->{fromdate}, $form->{accno});
247
248       my $todate = ($form->{todate} ne "") ? " AND ac.transdate <= '$form->{todate}' " : "";
249       $query = qq|SELECT sum(ac.amount) FROM acc_trans ac LEFT JOIN chart c ON (ac.chart_id = c.id)WHERE ((select date_trunc('year', ac.transdate::date)) >= (select date_trunc('year', ?::date))) $todate AND c.accno = ? AND ac.amount > 0 AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL)|;
250       ($form->{current_balance_credit}) = selectrow_query($form, $dbh, $query, $form->{fromdate}, $form->{accno});
251     }
252   }
253
254   $query = "";
255   my $union = "";
256   @values = ();
257
258   foreach my $id (@id) {
259
260     # NOTE: Postgres is really picky about the order of implicit CROSS
261     #  JOINs with ',' if you alias the tables and want to use the
262     #  alias later in another JOIN.  the alias you want to use has to
263     #  be the most recent in the list, otherwise Postgres will
264     #  overwrite the alias internally and complain.  For this reason,
265     #  in the next 3 SELECTs, the 'a' alias is last in the list.
266     #  Don't change this, and if you do, substitute the ',' with CROSS
267     #  JOIN ... that also works.
268
269     # get all transactions
270     $query =
271       qq|SELECT a.id, a.reference, a.description, ac.transdate, ac.chart_id, | .
272       qq|  $false AS invoice, ac.amount, 'gl' as module, | .
273       qq§(SELECT accno||'--'||rate FROM tax LEFT JOIN chart ON (tax.chart_id=chart.id) WHERE tax.id = (SELECT tax_id FROM taxkeys WHERE taxkey_id = ac.taxkey AND taxkeys.startdate <= ac.transdate ORDER BY taxkeys.startdate DESC LIMIT 1)) AS taxinfo, ac.source || ' ' || ac.memo AS memo § .
274       qq|FROM acc_trans ac, gl a | .
275       $dpt_join .
276       qq|WHERE | . $where . $dpt_where . $project .
277       qq|  AND ac.chart_id = ? | .
278       qq| AND ac.trans_id = a.id | .
279       qq| AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL) | .
280
281       qq|UNION ALL | .
282
283       qq|SELECT a.id, a.invnumber, c.name, ac.transdate, ac.chart_id, | .
284       qq|  a.invoice, ac.amount, 'ar' as module, | .
285       qq§(SELECT accno||'--'||rate FROM tax LEFT JOIN chart ON (tax.chart_id=chart.id) WHERE tax.id = (SELECT tax_id FROM taxkeys WHERE taxkey_id = ac.taxkey AND taxkeys.startdate <= ac.transdate ORDER BY taxkeys.startdate DESC LIMIT 1)) AS taxinfo, ac.source || ' ' || ac.memo AS memo  § .
286       qq|FROM acc_trans ac, customer c, ar a | .
287       $dpt_join .
288       qq|WHERE | . $where . $dpt_where . $project .
289       qq| AND ac.chart_id = ? | .
290       qq| AND ac.trans_id = a.id | .
291       qq| AND a.customer_id = c.id | .
292       qq| AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL)| .
293
294       qq|UNION ALL | .
295
296       qq|SELECT a.id, a.invnumber, v.name, ac.transdate, ac.chart_id, | .
297       qq|  a.invoice, ac.amount, 'ap' as module, | .
298       qq§(SELECT accno||'--'||rate FROM tax LEFT JOIN chart ON (tax.chart_id=chart.id) WHERE tax.id = (SELECT tax_id FROM taxkeys WHERE taxkey_id = ac.taxkey AND taxkeys.startdate <= ac.transdate ORDER BY taxkeys.startdate DESC LIMIT 1)) AS taxinfo, ac.source || ' ' || ac.memo AS memo  § .
299       qq|FROM acc_trans ac, vendor v, ap a | .
300       $dpt_join .
301       qq|WHERE | . $where . $dpt_where . $project .
302       qq| AND ac.chart_id = ? | .
303       qq| AND ac.trans_id = a.id | .
304       qq| AND a.vendor_id = v.id |;
305       qq| AND (NOT ac.ob_transaction OR ac.ob_transaction IS NULL)| .
306     push(@values,
307          @where_values, @department_values, @project_values, $id,
308          @where_values, @department_values, @project_values, $id,
309          @where_values, @department_values, @project_values, $id);
310
311     $union = qq|UNION ALL |;
312
313     if ($form->{project_id}) {
314
315       $fromdate_where =~ s/ac\./a\./;
316       $todate_where   =~ s/ac\./a\./;
317
318       $query .=
319         qq|UNION ALL | .
320
321         qq|SELECT a.id, a.invnumber, c.name, a.transdate, | .
322         qq|  a.invoice, ac.qty * ac.sellprice AS sellprice, 'ar' as module, | .
323         qq§(SELECT accno||'--'||rate FROM tax LEFT JOIN chart ON (tax.chart_id=chart.id) WHERE tax.id = (SELECT tax_id FROM taxkeys WHERE taxkey_id = ac.taxkey AND taxkeys.startdate <= ac.transdate ORDER BY taxkeys.startdate DESC LIMIT 1)) AS taxinfo § .
324         qq|FROM ar a | .
325         qq|JOIN invoice ac ON (ac.trans_id = a.id) | .
326         qq|JOIN parts p ON (ac.parts_id = p.id) | .
327         qq|JOIN customer c ON (a.customer_id = c.id) | .
328         $dpt_join .
329         qq|WHERE p.income_accno_id = ? | .
330         $fromdate_where .
331         $todate_where .
332         $dpt_where .
333         $project .
334         $ar_ap_cash_where .
335         qq|UNION ALL | .
336
337         qq|SELECT a.id, a.invnumber, v.name, a.transdate, | .
338         qq|  a.invoice, ac.qty * ac.sellprice AS sellprice, 'ap' as module, | .
339         qq§(SELECT accno||'--'||rate FROM tax LEFT JOIN chart ON (tax.chart_id=chart.id) WHERE tax.id = (SELECT tax_id FROM taxkeys WHERE taxkey_id = ac.taxkey AND taxkeys.startdate <= ac.transdate ORDER BY taxkeys.startdate DESC LIMIT 1)) AS taxinfo § .
340         qq|FROM ap a | .
341         qq|JOIN invoice ac ON (ac.trans_id = a.id) | .
342         qq|JOIN parts p ON (ac.parts_id = p.id) | .
343         qq|JOIN vendor v ON (a.vendor_id = v.id) | .
344         $dpt_join .
345         qq|WHERE p.expense_accno_id = ? | .
346         $fromdate_where .
347         $todate_where .
348         $dpt_where .
349         $project .
350         $ar_ap_cash_where;
351       push(@values,
352            $id, @department_values, @project_values,
353            $id, @department_values, @project_values);
354
355       $fromdate_where =~ s/a\./ac\./;
356       $todate_where   =~ s/a\./ac\./;
357
358     }
359
360     $union = qq|UNION ALL|;
361   }
362
363   my $sort = grep({ $form->{sort} eq $_ } qw(transdate reference description)) ? $form->{sort} : 'transdate';
364
365   $query .= qq|ORDER BY $sort|;
366   $sth = prepare_execute_query($form, $dbh, $query, @values);
367
368   #get detail information for each transaction
369   $trans_query =
370         qq|SELECT accno, | .
371         qq|amount, transdate FROM acc_trans LEFT JOIN chart ON (chart_id=chart.id) WHERE | .
372         qq|trans_id = ? AND sign(amount) <> sign(?) AND chart_id <> ? AND transdate = ?|;
373   my $trans_sth = $dbh->prepare($trans_query);
374
375   $form->{CA} = [];
376   while (my $ca = $sth->fetchrow_hashref(NAME_lc)) {
377     # ap
378     if ($ca->{module} eq "ap") {
379       $ca->{module} = ($ca->{invoice}) ? 'ir' : 'ap';
380     }
381
382     # ar
383     if ($ca->{module} eq "ar") {
384       $ca->{module} = ($ca->{invoice}) ? 'is' : 'ar';
385     }
386
387     if ($ca->{amount} < 0) {
388       $ca->{debit}  = $ca->{amount} * -1;
389       $ca->{credit} = 0;
390     } else {
391       $ca->{credit} = $ca->{amount};
392       $ca->{debit}  = 0;
393     }
394
395     ($ca->{ustkonto},$ca->{ustrate}) = split /--/, $ca->{taxinfo};
396
397     #get detail information for this transaction
398     $trans_sth->execute($ca->{id}, $ca->{amount}, $ca->{chart_id}, $ca->{transdate}) ||
399     $form->dberror($trans_query . " (" . join(", ", $ca->{id}) . ")");
400     while (my $trans = $trans_sth->fetchrow_hashref(NAME_lc)) {
401       if (($ca->{transdate} eq $trans->{transdate}) && ($ca->{amount} * $trans->{amount} < 0)) {
402         if ($trans->{amount} < 0) {
403           $trans->{debit}  = $trans->{amount} * -1;
404           $trans->{credit} = 0;
405         } else {
406           $trans->{credit} = $trans->{amount};
407           $trans->{debit}  = 0;
408         }
409         push(@{ $ca->{GEGENKONTO} }, $trans);
410       } else {
411         next;
412       }
413     }
414
415     $ca->{index} = join "--", map { $ca->{$_} } qw(id reference description transdate);
416 #     $ca->{index} = $ca->{$form->{sort}};
417     push(@{ $form->{CA} }, $ca);
418
419   }
420
421   $sth->finish;
422   $dbh->disconnect;
423
424   $main::lxdebug->leave_sub();
425 }
426
427 1;