75dc0264b0b14f4af529eba60d3756f889685a23
[kivitendo-erp.git] / SL / AR.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 #
31 # Accounts Receivable module backend routines
32 #
33 #======================================================================
34
35 package AR;
36
37 use Data::Dumper;
38
39 sub post_transaction {
40   $main::lxdebug->enter_sub();
41
42   my ($self, $myconfig, $form) = @_;
43
44   my ($null, $taxrate, $amount, $tax, $diff);
45   my $exchangerate = 0;
46   my $i;
47
48   my $dbh = $form->dbconnect_noauto($myconfig);
49
50   if ($form->{currency} eq $form->{defaultcurrency}) {
51     $form->{exchangerate} = 1;
52   } else {
53     $exchangerate =
54       $form->check_exchangerate($myconfig, $form->{currency},
55                                 $form->{transdate}, 'buy');
56   }
57   for $i (1 .. $form->{rowcount}) {
58     $form->{AR_amounts}{"amount_$i"} = (split(/--/, $form->{"AR_amount_$i"}))[0];
59   }
60   ($form->{AR_amounts}{receivables}) = split(/--/, $form->{ARselected});
61   ($form->{AR}{receivables}) = split(/--/, $form->{ARselected});
62
63   $form->{exchangerate} =
64     ($exchangerate)
65     ? $exchangerate
66     : $form->parse_amount($myconfig, $form->{exchangerate});
67
68   for $i (1 .. $form->{rowcount}) {
69
70     $form->{"amount_$i"} =
71       $form->round_amount($form->parse_amount($myconfig, $form->{"amount_$i"})
72                             * $form->{exchangerate},
73                           2);
74
75     $form->{netamount} += $form->{"amount_$i"};
76   }
77
78   # this is for ar
79
80   $form->{amount} = $form->{netamount};
81
82   $form->{tax} = 0;
83
84   # taxincluded doesn't make sense if there is no amount
85
86   $form->{taxincluded} = 0 if ($form->{amount} == 0);
87   for $i (1 .. $form->{rowcount}) {
88     ($form->{"taxkey_$i"},      $NULL)         = split /--/, $form->{"taxchart_$i"};
89
90     $query =
91       qq| SELECT c.accno, t.rate FROM chart c, tax t where c.id=t.chart_id AND t.taxkey=$form->{"taxkey_$i"}|;
92     $sth = $dbh->prepare($query);
93     $sth->execute || $form->dberror($query);
94     ($form->{AR_amounts}{"tax_$i"}, $form->{"taxrate_$i"}) = $sth->fetchrow_array;
95     $form->{AR_amounts}{"tax_$i"}{taxkey} = $form->{"taxkey_$i"};
96     $form->{AR_amounts}{"amount_$i"}{taxkey} = $form->{"taxkey_$i"};
97
98     $sth->finish;
99     if (!$form->{"korrektur_$i"}) {
100       if ($form->{taxincluded} *= 1) {
101         $tax = $form->{"amount_$i"} - ($form->{"amount_$i"} / ($form->{"taxrate_$i"} + 1));
102         $amount = $form->{"amount_$i"} - $tax;
103         $form->{"amount_$i"} = $form->round_amount($amount, 2);
104         $diff += $amount - $form->{"amount_$i"};
105         $form->{"tax_$i"} = $form->round_amount($tax, 2);
106         $form->{netamount} += $form->{"amount_$i"};
107       }
108       $form->{"tax_$i"} = $form->{"amount_$i"} * $form->{"taxrate_$i"};
109       $form->{"tax_$i"} = $form->round_amount($form->{"tax_$i"} * $form->{exchangerate}, 2);
110     }
111     $form->{total_tax} += $form->{"tax_$i"};
112   }
113
114   # adjust paidaccounts if there is no date in the last row
115   $form->{paidaccounts}-- unless ($form->{"datepaid_$form->{paidaccounts}"});
116   $form->{paid} = 0;
117
118   # add payments
119   for $i (1 .. $form->{paidaccounts}) {
120     $form->{"paid_$i"} =
121       $form->round_amount($form->parse_amount($myconfig, $form->{"paid_$i"}),
122                           2);
123
124     $form->{paid} += $form->{"paid_$i"};
125     $form->{datepaid} = $form->{"datepaid_$i"};
126
127   }
128
129   $form->{amount} = $form->{netamount} + $form->{total_tax};
130   $form->{paid}   =
131     $form->round_amount($form->{paid} * $form->{exchangerate}, 2);
132
133   my ($query, $sth, $null);
134
135   ($null, $form->{employee_id}) = split /--/, $form->{employee};
136   unless ($form->{employee_id}) {
137     $form->get_employee($dbh);
138   }
139
140   # if we have an id delete old records
141   if ($form->{id}) {
142
143     # delete detail records
144     $query = qq|DELETE FROM acc_trans WHERE trans_id = $form->{id}|;
145     $dbh->do($query) || $form->dberror($query);
146
147   } else {
148     my $uid = rand() . time;
149
150     $uid .= $form->{login};
151
152     $uid = substr($uid, 2, 75);
153
154     $query = qq|INSERT INTO ar (invnumber, employee_id)
155                 VALUES ('$uid', $form->{employee_id})|;
156     $dbh->do($query) || $form->dberror($query);
157
158     $query = qq|SELECT a.id FROM ar a
159                 WHERE a.invnumber = '$uid'|;
160     $sth = $dbh->prepare($query);
161     $sth->execute || $form->dberror($query);
162
163     ($form->{id}) = $sth->fetchrow_array;
164     $sth->finish;
165
166   }
167
168   # update department
169   ($null, $form->{department_id}) = split(/--/, $form->{department});
170   $form->{department_id} *= 1;
171
172   # escape '
173   map { $form->{$_} =~ s/\'/\'\'/g } qw(invnumber ordnumber notes);
174
175   # record last payment date in ar table
176   $form->{datepaid} = $form->{transdate} unless $form->{datepaid};
177   my $datepaid = ($form->{paid} != 0) ? qq|'$form->{datepaid}'| : 'NULL';
178
179   $query = qq|UPDATE ar set
180               invnumber = '$form->{invnumber}',
181               ordnumber = '$form->{ordnumber}',
182               transdate = '$form->{transdate}',
183               customer_id = $form->{customer_id},
184               taxincluded = '$form->{taxincluded}',
185               amount = $form->{amount},
186               duedate = '$form->{duedate}',
187               paid = $form->{paid},
188               datepaid = $datepaid,
189               netamount = $form->{netamount},
190               curr = '$form->{currency}',
191               notes = '$form->{notes}',
192               department_id = $form->{department_id},
193               employee_id = $form->{employee_id}
194               WHERE id = $form->{id}|;
195   $dbh->do($query) || $form->dberror($query);
196
197   # amount for AR account
198   $form->{receivables} = $form->round_amount($form->{amount}, 2) * -1;
199
200   # update exchangerate
201   if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
202     $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate},
203                                $form->{exchangerate}, 0);
204   }
205
206   # add individual transactions for AR, amount and taxes
207   for $i (1 .. $form->{rowcount}) {
208     if ($form->{"amount_$i"} != 0) {
209       $project_id = 'NULL';
210       if ("amount_$i" =~ /amount_/) {
211         if ($form->{"project_id_$i"} && $form->{"projectnumber_$i"}) {
212           $project_id = $form->{"project_id_$i"};
213         }
214       }
215       if ("amount_$i" =~ /amount/) {
216         $taxkey = $form->{AR_amounts}{"amount_$i"}{taxkey};
217       }
218
219       # insert detail records in acc_trans
220       $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
221                                          project_id, taxkey)
222                   VALUES ($form->{id}, (SELECT c.id FROM chart c
223                                         WHERE c.accno = '$form->{AR_amounts}{"amount_$i"}'),
224                   $form->{"amount_$i"}, '$form->{transdate}', $project_id, '$taxkey')|;
225       $dbh->do($query) || $form->dberror($query);
226       if ($form->{"tax_$i"} !=0) {
227         # insert detail records in acc_trans
228         $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
229                                           project_id, taxkey)
230                     VALUES ($form->{id}, (SELECT c.id FROM chart c
231                                           WHERE c.accno = '$form->{AR_amounts}{"tax_$i"}'),
232                     $form->{"tax_$i"}, '$form->{transdate}', $project_id, '$taxkey')|;
233         $dbh->do($query) || $form->dberror($query);
234       }
235     }
236   }
237   # add recievables
238   $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
239                                       project_id)
240               VALUES ($form->{id}, (SELECT c.id FROM chart c
241                                     WHERE c.accno = '$form->{AR_amounts}{receivables}'),
242               $form->{receivables}, '$form->{transdate}', $project_id)|;
243   $dbh->do($query) || $form->dberror($query);
244
245
246   # add paid transactions
247   for my $i (1 .. $form->{paidaccounts}) {
248     if ($form->{"paid_$i"} != 0) {
249
250       $form->{"AR_paid_$i"} =~ s/\"//g;
251       ($form->{AR}{"paid_$i"}) = split(/--/, $form->{"AR_paid_$i"});
252       $form->{"datepaid_$i"} = $form->{transdate}
253         unless ($form->{"datepaid_$i"});
254
255       $exchangerate = 0;
256       if ($form->{currency} eq $form->{defaultcurrency}) {
257         $form->{"exchangerate_$i"} = 1;
258       } else {
259         $exchangerate =
260           $form->check_exchangerate($myconfig, $form->{currency},
261                                     $form->{"datepaid_$i"}, 'buy');
262
263         $form->{"exchangerate_$i"} =
264           ($exchangerate)
265           ? $exchangerate
266           : $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
267       }
268
269       # if there is no amount and invtotal is zero there is no exchangerate
270       if ($form->{amount} == 0 && $form->{netamount} == 0) {
271         $form->{exchangerate} = $form->{"exchangerate_$i"};
272       }
273
274       # receivables amount
275       $amount =
276         $form->round_amount($form->{"paid_$i"} * $form->{exchangerate}, 2);
277
278       if ($form->{receivables} != 0) {
279
280         # add receivable
281         $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
282                     transdate)
283                     VALUES ($form->{id},
284                            (SELECT c.id FROM chart c
285                             WHERE c.accno = '$form->{AR}{receivables}'),
286                     $amount, '$form->{"datepaid_$i"}')|;
287         $dbh->do($query) || $form->dberror($query);
288       }
289       $form->{receivables} = $amount;
290
291       $form->{"memo_$i"} =~ s/\'/\'\'/g;
292
293       if ($form->{"paid_$i"} != 0) {
294
295         # add payment
296         $amount = $form->{"paid_$i"} * -1;
297         $query  = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
298                     transdate, source, memo)
299                     VALUES ($form->{id},
300                            (SELECT c.id FROM chart c
301                             WHERE c.accno = '$form->{AR}{"paid_$i"}'),
302                     $amount, '$form->{"datepaid_$i"}',
303                     '$form->{"source_$i"}', '$form->{"memo_$i"}')|;
304         $dbh->do($query) || $form->dberror($query);
305
306         # exchangerate difference for payment
307         $amount =
308           $form->round_amount(
309                     $form->{"paid_$i"} * ($form->{"exchangerate_$i"} - 1) * -1,
310                     2);
311
312         if ($amount != 0) {
313           $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
314                       transdate, fx_transaction, cleared)
315                       VALUES ($form->{id},
316                              (SELECT c.id FROM chart c
317                               WHERE c.accno = '$form->{AR}{"paid_$i"}'),
318                       $amount, '$form->{"datepaid_$i"}', '1', '0')|;
319           $dbh->do($query) || $form->dberror($query);
320         }
321
322         # exchangerate gain/loss
323         $amount =
324           $form->round_amount(
325                    $form->{"paid_$i"} *
326                      ($form->{exchangerate} - $form->{"exchangerate_$i"}) * -1,
327                    2);
328
329         if ($amount != 0) {
330           $accno =
331             ($amount > 0) ? $form->{fxgain_accno} : $form->{fxloss_accno};
332           $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
333                       transdate, fx_transaction, cleared)
334                       VALUES ($form->{id}, (SELECT c.id FROM chart c
335                                             WHERE c.accno = '$accno'),
336                       $amount, '$form->{"datepaid_$i"}', '1', '0')|;
337           $dbh->do($query) || $form->dberror($query);
338         }
339       }
340
341       # update exchangerate record
342       if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
343         $form->update_exchangerate($dbh, $form->{currency},
344                                    $form->{"datepaid_$i"},
345                                    $form->{"exchangerate_$i"}, 0);
346       }
347     }
348   }
349
350   my $rc = $dbh->commit;
351   $dbh->disconnect;
352
353   $main::lxdebug->leave_sub();
354
355   return $rc;
356 }
357
358 sub delete_transaction {
359   $main::lxdebug->enter_sub();
360
361   my ($self, $myconfig, $form) = @_;
362
363   # connect to database, turn AutoCommit off
364   my $dbh = $form->dbconnect_noauto($myconfig);
365
366   my $query = qq|DELETE FROM ar WHERE id = $form->{id}|;
367   $dbh->do($query) || $form->dberror($query);
368
369   $query = qq|DELETE FROM acc_trans WHERE trans_id = $form->{id}|;
370   $dbh->do($query) || $form->dberror($query);
371
372   # commit
373   my $rc = $dbh->commit;
374   $dbh->disconnect;
375
376   $main::lxdebug->leave_sub();
377
378   return $rc;
379 }
380
381 sub ar_transactions {
382   $main::lxdebug->enter_sub();
383
384   my ($self, $myconfig, $form) = @_;
385
386   # connect to database
387   my $dbh = $form->dbconnect($myconfig);
388
389   my $query = qq|SELECT a.id, a.invnumber, a.ordnumber, a.transdate,
390                  a.duedate, a.netamount, a.amount, a.paid, c.name,
391                  a.invoice, a.datepaid, a.terms, a.notes, a.shipvia,
392                  a.shippingpoint,
393                  e.name AS employee
394                  FROM ar a
395               JOIN customer c ON (a.customer_id = c.id)
396               LEFT JOIN employee e ON (a.employee_id = e.id)|;
397
398   my $where = "1 = 1";
399   if ($form->{customer_id}) {
400     $where .= " AND a.customer_id = $form->{customer_id}";
401   } else {
402     if ($form->{customer}) {
403       my $customer = $form->like(lc $form->{customer});
404       $where .= " AND lower(c.name) LIKE '$customer'";
405     }
406   }
407   if ($form->{department}) {
408     my ($null, $department_id) = split /--/, $form->{department};
409     $where .= " AND a.department_id = $department_id";
410   }
411   if ($form->{invnumber}) {
412     my $invnumber = $form->like(lc $form->{invnumber});
413     $where .= " AND lower(a.invnumber) LIKE '$invnumber'";
414   }
415   if ($form->{ordnumber}) {
416     my $ordnumber = $form->like(lc $form->{ordnumber});
417     $where .= " AND lower(a.ordnumber) LIKE '$ordnumber'";
418   }
419   if ($form->{notes}) {
420     my $notes = $form->like(lc $form->{notes});
421     $where .= " AND lower(a.notes) LIKE '$notes'";
422   }
423
424   $where .= " AND a.transdate >= '$form->{transdatefrom}'"
425     if $form->{transdatefrom};
426   $where .= " AND a.transdate <= '$form->{transdateto}'"
427     if $form->{transdateto};
428   if ($form->{open} || $form->{closed}) {
429     unless ($form->{open} && $form->{closed}) {
430       $where .= " AND a.amount <> a.paid" if ($form->{open});
431       $where .= " AND a.amount = a.paid"  if ($form->{closed});
432     }
433   }
434
435   my @a = (transdate, invnumber, name);
436   push @a, "employee" if $form->{l_employee};
437   my $sortorder = join ', ', $form->sort_columns(@a);
438   $sortorder = $form->{sort} unless $sortorder;
439
440   $query .= "WHERE $where
441              ORDER by $sortorder";
442
443   my $sth = $dbh->prepare($query);
444   $sth->execute || $form->dberror($query);
445
446   while (my $ar = $sth->fetchrow_hashref(NAME_lc)) {
447     push @{ $form->{AR} }, $ar;
448   }
449
450   $sth->finish;
451   $dbh->disconnect;
452
453   $main::lxdebug->leave_sub();
454 }
455
456 1;
457