Kosmetik: Perltidy-Lauf nach den Einstellungen in doc/programmierrichtlinien.txt...
[kivitendo-erp.git] / SL / IS.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:
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 # Inventory invoicing module
32 #
33 #======================================================================
34
35 package IS;
36
37 use Data::Dumper;
38
39 sub invoice_details {
40   $main::lxdebug->enter_sub();
41
42   my ($self, $myconfig, $form, $locale) = @_;
43
44   $form->{duedate} = $form->{invdate} unless ($form->{duedate});
45
46   # connect to database
47   my $dbh = $form->dbconnect($myconfig);
48
49   my $query = qq|SELECT date '$form->{duedate}' - date '$form->{invdate}'
50                  AS terms
51                  FROM defaults|;
52   my $sth = $dbh->prepare($query);
53   $sth->execute || $form->dberror($query);
54
55   ($form->{terms}) = $sth->fetchrow_array;
56   $sth->finish;
57
58   my $tax = 0;
59   my $item;
60   my $i;
61   my @partsgroup = ();
62   my $partsgroup;
63   my %oid = ('Pg'     => 'oid',
64              'Oracle' => 'rowid');
65
66   # sort items by partsgroup
67   for $i (1 .. $form->{rowcount}) {
68     $partsgroup = "";
69     if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
70       $form->format_string("partsgroup_$i");
71       $partsgroup = $form->{"partsgroup_$i"};
72     }
73     push @partsgroup, [$i, $partsgroup];
74   }
75
76   my $sameitem = "";
77   my @taxaccounts;
78   my %taxaccounts;
79   my %taxbase;
80   my $taxrate;
81   my $taxamount;
82   my $taxbase;
83   my $taxdiff;
84
85   foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
86     $i = $item->[0];
87
88     if ($item->[1] ne $sameitem) {
89       push(@{ $form->{description} }, qq|$item->[1]|);
90       $sameitem = $item->[1];
91
92       map { push(@{ $form->{$_} }, "") }
93         qw(runningnumber number serialnumber bin partnotes qty unit deliverydate sellprice listprice netprice discount linetotal);
94     }
95
96     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
97
98     if ($form->{"qty_$i"} != 0) {
99
100       # add number, description and qty to $form->{number}, ....
101       push(@{ $form->{runningnumber} }, $i);
102       push(@{ $form->{number} },        qq|$form->{"partnumber_$i"}|);
103       push(@{ $form->{serialnumber} },  qq|$form->{"serialnumber_$i"}|);
104       push(@{ $form->{bin} },           qq|$form->{"bin_$i"}|);
105       push(@{ $form->{"partnotes"} },   qq|$form->{"partnotes_$i"}|);
106       push(@{ $form->{description} },   qq|$form->{"description_$i"}|);
107       push(@{ $form->{qty} },
108            $form->format_amount($myconfig, $form->{"qty_$i"}));
109       push(@{ $form->{unit} },            qq|$form->{"unit_$i"}|);
110       push(@{ $form->{deliverydate_oe} }, qq|$form->{"deliverydate_$i"}|);
111
112       push(@{ $form->{sellprice} },    $form->{"sellprice_$i"});
113       push(@{ $form->{ordnumber_oe} }, qq|$form->{"ordnumber_$i"}|);
114       push(@{ $form->{transdate_oe} }, qq|$form->{"transdate_$i"}|);
115
116       if ($form->{lizenzen}) {
117         if ($form->{"licensenumber_$i"}) {
118           $query =
119             qq|SELECT l.licensenumber, l.validuntil FROM license l WHERE l.id = $form->{"licensenumber_$i"}|;
120           $sth = $dbh->prepare($query);
121           $sth->execute || $form->dberror($query);
122
123           ($licensenumber, $validuntil) = $sth->fetchrow_array;
124           push(@{ $form->{licensenumber} }, $licensenumber);
125           push(@{ $form->{validuntil} },
126                $locale->date($myconfig, $validuntil, 0));
127           $sth->finish;
128         } else {
129           push(@{ $form->{licensenumber} }, "");
130           push(@{ $form->{validuntil} },    "");
131         }
132       }
133
134       # listprice
135       push(@{ $form->{listprice} }, $form->{"listprice_$i"});
136
137       my $sellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
138       my ($dec) = ($sellprice =~ /\.(\d+)/);
139       $dec = length $dec;
140       my $decimalplaces = ($dec > 2) ? $dec : 2;
141
142       my $i_discount =
143         $form->round_amount(
144                             $sellprice * $form->parse_amount($myconfig,
145                                                  $form->{"discount_$i"}) / 100,
146                             $decimalplaces);
147
148       my $discount =
149         $form->round_amount($form->{"qty_$i"} * $i_discount, $decimalplaces);
150
151       # keep a netprice as well, (sellprice - discount)
152       $form->{"netprice_$i"} = $sellprice - $i_discount;
153
154       push(@{ $form->{netprice} },
155            ($form->{"netprice_$i"} != 0)
156            ? $form->format_amount(
157                                  $myconfig, $form->{"netprice_$i"},
158                                  $decimalplaces
159              )
160            : " ");
161
162       my $linetotal =
163         $form->round_amount($form->{"qty_$i"} * $form->{"netprice_$i"}, 2);
164
165       $discount =
166         ($discount != 0)
167         ? $form->format_amount($myconfig, $discount * -1, $decimalplaces)
168         : " ";
169       $linetotal = ($linetotal != 0) ? $linetotal : " ";
170
171       push(@{ $form->{discount} },   $discount);
172       push(@{ $form->{p_discount} }, $form->{"discount_$i"});
173
174       $form->{total} += $linetotal;
175
176       push(@{ $form->{linetotal} },
177            $form->format_amount($myconfig, $linetotal, 2));
178
179       @taxaccounts = split / /, $form->{"taxaccounts_$i"};
180       $taxrate     = 0;
181       $taxdiff     = 0;
182
183       map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
184
185       if ($form->{taxincluded}) {
186
187         # calculate tax
188         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
189         $taxbase = $linetotal - $taxamount;
190       } else {
191         $taxamount = $linetotal * $taxrate;
192         $taxbase   = $linetotal;
193       }
194
195       if ($form->round_amount($taxrate, 7) == 0) {
196         if ($form->{taxincluded}) {
197           foreach $item (@taxaccounts) {
198             $taxamount =
199               $form->round_amount($linetotal * $form->{"${item}_rate"} /
200                                     (1 + abs($form->{"${item}_rate"})),
201                                   2);
202
203             $taxaccounts{$item} += $taxamount;
204             $taxdiff            += $taxamount;
205
206             $taxbase{$item} += $taxbase;
207           }
208           $taxaccounts{ $taxaccounts[0] } += $taxdiff;
209         } else {
210           foreach $item (@taxaccounts) {
211             $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
212             $taxbase{$item}     += $taxbase;
213           }
214         }
215       } else {
216         foreach $item (@taxaccounts) {
217           $taxaccounts{$item} +=
218             $taxamount * $form->{"${item}_rate"} / $taxrate;
219           $taxbase{$item} += $taxbase;
220         }
221       }
222       $tax_rate = $taxrate * 100;
223       push(@{ $form->{tax_rate} }, qq|$tax_rate|);
224       if ($form->{"assembly_$i"}) {
225         $sameitem = "";
226
227         # get parts and push them onto the stack
228         my $sortorder = "";
229         if ($form->{groupitems}) {
230           $sortorder =
231             qq|ORDER BY pg.partsgroup, a.$oid{$myconfig->{dbdriver}}|;
232         } else {
233           $sortorder = qq|ORDER BY a.$oid{$myconfig->{dbdriver}}|;
234         }
235
236         $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty,
237                     pg.partsgroup
238                     FROM assembly a
239                     JOIN parts p ON (a.parts_id = p.id)
240                     LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
241                     WHERE a.bom = '1'
242                     AND a.id = '$form->{"id_$i"}'
243                     $sortorder|;
244         $sth = $dbh->prepare($query);
245         $sth->execute || $form->dberror($query);
246
247         while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
248           if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
249             map { push(@{ $form->{$_} }, "") }
250               qw(runningnumber number serialnumber unit qty bin sellprice listprice netprice discount linetotal);
251             $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
252             push(@{ $form->{description} }, $sameitem);
253           }
254
255           map { $form->{"a_$_"} = $ref->{$_} } qw(partnumber description);
256           $form->format_string("a_partnumber", "a_description");
257
258           push(@{ $form->{description} },
259                $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}
260                  )
261                  . qq| -- $form->{"a_partnumber"}, $form->{"a_description"}|);
262           map { push(@{ $form->{$_} }, "") }
263             qw(number unit qty runningnumber serialnumber bin sellprice listprice netprice discount linetotal);
264
265         }
266         $sth->finish;
267       }
268     }
269   }
270
271   foreach my $item (sort keys %taxaccounts) {
272     if ($form->round_amount($taxaccounts{$item}, 2) != 0) {
273       push(@{ $form->{taxbase} },
274            $form->format_amount($myconfig, $taxbase{$item}, 2));
275
276       $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
277
278       push(@{ $form->{tax} }, $form->format_amount($myconfig, $taxamount, 2));
279       push(@{ $form->{taxdescription} }, $form->{"${item}_description"});
280       push(@{ $form->{taxrate} },
281            $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
282       push(@{ $form->{taxnumber} }, $form->{"${item}_taxnumber"});
283     }
284   }
285
286   for my $i (1 .. $form->{paidaccounts}) {
287     if ($form->{"paid_$i"}) {
288       push(@{ $form->{payment} }, $form->{"paid_$i"});
289       my ($accno, $description) = split /--/, $form->{"AR_paid_$i"};
290       push(@{ $form->{paymentaccount} }, $description);
291       push(@{ $form->{paymentdate} },    $form->{"datepaid_$i"});
292       push(@{ $form->{paymentsource} },  $form->{"source_$i"});
293
294       $form->{paid} += $form->parse_amount($myconfig, $form->{"paid_$i"});
295     }
296   }
297
298   $form->{subtotal} = $form->format_amount($myconfig, $form->{total}, 2);
299   $form->{invtotal} =
300     ($form->{taxincluded}) ? $form->{total} : $form->{total} + $tax;
301   $form->{total} =
302     $form->format_amount($myconfig, $form->{invtotal} - $form->{paid}, 2);
303   $form->{invtotal} = $form->format_amount($myconfig, $form->{invtotal}, 2);
304
305   $form->{paid} = $form->format_amount($myconfig, $form->{paid}, 2);
306
307   # myconfig variables
308   map { $form->{$_} = $myconfig->{$_} }
309     (qw(company address tel fax signature businessnumber));
310   $form->{username} = $myconfig->{name};
311
312   $dbh->disconnect;
313
314   $main::lxdebug->leave_sub();
315 }
316
317 sub project_description {
318   $main::lxdebug->enter_sub();
319
320   my ($self, $dbh, $id) = @_;
321
322   my $query = qq|SELECT p.description
323                  FROM project p
324                  WHERE p.id = $id|;
325   my $sth = $dbh->prepare($query);
326   $sth->execute || $form->dberror($query);
327
328   ($_) = $sth->fetchrow_array;
329
330   $sth->finish;
331
332   $main::lxdebug->leave_sub();
333
334   return $_;
335 }
336
337 sub customer_details {
338   $main::lxdebug->enter_sub();
339
340   my ($self, $myconfig, $form) = @_;
341
342   # connect to database
343   my $dbh = $form->dbconnect($myconfig);
344
345   # get contact id, set it if nessessary
346   ($null, $form->{cp_id}) = split /--/, $form->{contact};
347
348   $contact = "";
349   if ($form->{cp_id}) {
350     $contact = "and cp.cp_id = $form->{cp_id}";
351   }
352
353   # get rest for the customer
354   my $query = qq|SELECT ct.*, cp.*, ct.notes as customernotes
355                  FROM customer ct
356                  LEFT JOIN contacts cp on ct.id = cp.cp_cv_id
357                  WHERE ct.id = $form->{customer_id} $contact order by cp.cp_id limit 1|;
358   my $sth = $dbh->prepare($query);
359   $sth->execute || $form->dberror($query);
360
361   $ref = $sth->fetchrow_hashref(NAME_lc);
362
363   # remove id and taxincluded before copy back
364   delete @$ref{qw(id taxincluded)};
365   map { $form->{$_} = $ref->{$_} } keys %$ref;
366
367   $sth->finish;
368   $dbh->disconnect;
369
370   $main::lxdebug->leave_sub();
371 }
372
373 sub post_invoice {
374   $main::lxdebug->enter_sub();
375
376   my ($self, $myconfig, $form) = @_;
377
378   # connect to database, turn off autocommit
379   my $dbh = $form->dbconnect_noauto($myconfig);
380
381   my ($query, $sth, $null, $project_id, $deliverydate);
382   my $exchangerate = 0;
383
384   ($null, $form->{employee_id}) = split /--/, $form->{employee};
385   unless ($form->{employee_id}) {
386     $form->get_employee($dbh);
387   }
388
389   ($null, $form->{contact_id}) = split /--/, $form->{contact};
390   $form->{contact_id} *= 1;
391
392   ($null, $form->{department_id}) = split(/--/, $form->{department});
393   $form->{department_id} *= 1;
394
395   if ($form->{id}) {
396
397     &reverse_invoice($dbh, $form);
398
399   } else {
400     my $uid = rand() . time;
401
402     $uid .= $form->{login};
403
404     $uid = substr($uid, 2, 75);
405
406     $query = qq|INSERT INTO ar (invnumber, employee_id)
407                 VALUES ('$uid', $form->{employee_id})|;
408     $dbh->do($query) || $form->dberror($query);
409
410     $query = qq|SELECT a.id FROM ar a
411                 WHERE a.invnumber = '$uid'|;
412     $sth = $dbh->prepare($query);
413     $sth->execute || $form->dberror($query);
414
415     ($form->{id}) = $sth->fetchrow_array;
416     $sth->finish;
417   }
418
419   map { $form->{$_} =~ s/\'/\'\'/g }
420     (qw(invnumber shippingpoint shipvia notes intnotes message));
421
422   my ($netamount, $invoicediff) = (0, 0);
423   my ($amount, $linetotal, $lastincomeaccno);
424
425   if ($form->{currency} eq $form->{defaultcurrency}) {
426     $form->{exchangerate} = 1;
427   } else {
428     $exchangerate =
429       $form->check_exchangerate($myconfig, $form->{currency},
430                                 $form->{transdate}, 'buy');
431   }
432
433   $form->{exchangerate} =
434     ($exchangerate)
435     ? $exchangerate
436     : $form->parse_amount($myconfig, $form->{exchangerate});
437
438   $form->{expense_inventory} = "";
439
440   foreach my $i (1 .. $form->{rowcount}) {
441     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
442
443     if ($form->{"qty_$i"} != 0) {
444
445       map { $form->{"${_}_$i"} =~ s/\'/\'\'/g }
446         (qw(partnumber description unit));
447
448       # undo discount formatting
449       $form->{"discount_$i"} =
450         $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
451
452       my ($allocated, $taxrate) = (0, 0);
453       my $taxamount;
454
455       # keep entered selling price
456       my $fxsellprice =
457         $form->parse_amount($myconfig, $form->{"sellprice_$i"});
458
459       my ($dec) = ($fxsellprice =~ /\.(\d+)/);
460       $dec = length $dec;
461       my $decimalplaces = ($dec > 2) ? $dec : 2;
462
463       # deduct discount
464       my $discount =
465         $form->round_amount($fxsellprice * $form->{"discount_$i"},
466                             $decimalplaces);
467       $form->{"sellprice_$i"} = $fxsellprice - $discount;
468
469       # add tax rates
470       map { $taxrate += $form->{"${_}_rate"} } split / /,
471         $form->{"taxaccounts_$i"};
472
473       # round linetotal to 2 decimal places
474       $linetotal =
475         $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"}, 2);
476
477       if ($form->{taxincluded}) {
478         $taxamount = $linetotal * ($taxrate / (1 + $taxrate));
479         $form->{"sellprice_$i"} =
480           $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
481       } else {
482         $taxamount = $linetotal * $taxrate;
483       }
484
485       $netamount += $linetotal;
486
487       if ($taxamount != 0) {
488         map {
489           $form->{amount}{ $form->{id} }{$_} +=
490             $taxamount * $form->{"${_}_rate"} / $taxrate
491         } split / /, $form->{"taxaccounts_$i"};
492       }
493
494       # add amount to income, $form->{amount}{trans_id}{accno}
495       $amount =
496         $form->{"sellprice_$i"} * $form->{"qty_$i"} * $form->{exchangerate};
497
498       $linetotal =
499         $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"}, 2) *
500         $form->{exchangerate};
501       $linetotal = $form->round_amount($linetotal, 2);
502
503       # this is the difference from the inventory
504       $invoicediff += ($amount - $linetotal);
505
506       $form->{amount}{ $form->{id} }{ $form->{"income_accno_$i"} } +=
507         $linetotal;
508
509       $lastincomeaccno = $form->{"income_accno_$i"};
510
511       # adjust and round sellprice
512       $form->{"sellprice_$i"} =
513         $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate},
514                             $decimalplaces);
515
516       if ($form->{"inventory_accno_$i"} || $form->{"assembly_$i"}) {
517
518         # adjust parts onhand quantity
519
520         if ($form->{"assembly_$i"}) {
521
522           # do not update if assembly consists of all services
523           $query = qq|SELECT sum(p.inventory_accno_id)
524                       FROM parts p
525                       JOIN assembly a ON (a.parts_id = p.id)
526                       WHERE a.id = $form->{"id_$i"}|;
527           $sth = $dbh->prepare($query);
528           $sth->execute || $form->dberror($query);
529
530           if ($sth->fetchrow_array) {
531             $form->update_balance($dbh, "parts", "onhand",
532                                   qq|id = $form->{"id_$i"}|,
533                                   $form->{"qty_$i"} * -1)
534               unless $form->{shipped};
535           }
536           $sth->finish;
537
538           # record assembly item as allocated
539           &process_assembly($dbh, $form, $form->{"id_$i"}, $form->{"qty_$i"});
540         } else {
541           $form->update_balance($dbh, "parts", "onhand",
542                                 qq|id = $form->{"id_$i"}|,
543                                 $form->{"qty_$i"} * -1)
544             unless $form->{shipped};
545
546           $allocated = &cogs($dbh, $form, $form->{"id_$i"}, $form->{"qty_$i"});
547         }
548       }
549
550       $project_id = 'NULL';
551       if ($form->{"projectnumber_$i"}) {
552         $project_id = $form->{"projectnumber_$i"};
553       }
554       $deliverydate =
555         ($form->{"deliverydate_$i"})
556         ? qq|'$form->{"deliverydate_$i"}'|
557         : "NULL";
558
559       # get pricegroup_id and save ist
560       ($null, my $pricegroup_id) = split /--/, $form->{"sellprice_drag_$i"};
561       $pricegroup_id *= 1;
562
563       # save detail record in invoice table
564       $query = qq|INSERT INTO invoice (trans_id, parts_id, description, qty,
565                   sellprice, fxsellprice, discount, allocated, assemblyitem,
566                   unit, deliverydate, project_id, serialnumber, pricegroup_id,
567                   ordnumber, transdate, cusordnumber)
568                   VALUES ($form->{id}, $form->{"id_$i"},
569                   '$form->{"description_$i"}', $form->{"qty_$i"},
570                   $form->{"sellprice_$i"}, $fxsellprice,
571                   $form->{"discount_$i"}, $allocated, 'f',
572                   '$form->{"unit_$i"}', $deliverydate, (SELECT id from project where projectnumber = '$project_id'),
573                   '$form->{"serialnumber_$i"}', '$pricegroup_id',
574                   '$form->{"ordnumber_$i"}', '$form->{"transdate_$i"}', '$form->{"cusordnumber_$i"}')|;
575       $dbh->do($query) || $form->dberror($query);
576
577       if ($form->{lizenzen}) {
578         if ($form->{"licensenumber_$i"}) {
579           $query =
580             qq|SELECT i.id FROM invoice i WHERE i.trans_id=$form->{id} ORDER BY i.oid DESC LIMIT 1|;
581           $sth = $dbh->prepare($query);
582           $sth->execute || $form->dberror($query);
583
584           ($invoice_row_id) = $sth->fetchrow_array;
585           $sth->finish;
586
587           $query =
588             qq|INSERT INTO licenseinvoice (trans_id, license_id) VALUES ($invoice_row_id, $form->{"licensenumber_$i"})|;
589           $dbh->do($query) || $form->dberror($query);
590         }
591       }
592
593     }
594   }
595
596   $form->{datepaid} = $form->{invdate};
597
598   # total payments, don't move we need it here
599   for my $i (1 .. $form->{paidaccounts}) {
600     $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
601     $form->{paid} += $form->{"paid_$i"};
602     $form->{datepaid} = $form->{"datepaid_$i"} if ($form->{"datepaid_$i"});
603   }
604
605   my ($tax, $diff) = (0, 0);
606
607   $netamount = $form->round_amount($netamount, 2);
608
609   # figure out rounding errors for total amount vs netamount + taxes
610   if ($form->{taxincluded}) {
611
612     $amount = $form->round_amount($netamount * $form->{exchangerate}, 2);
613     $diff += $amount - $netamount * $form->{exchangerate};
614     $netamount = $amount;
615
616     foreach my $item (split / /, $form->{taxaccounts}) {
617       $amount = $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate};
618       $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount, 2);
619       $tax += $form->{amount}{ $form->{id} }{$item};
620       $netamount -= $form->{amount}{ $form->{id} }{$item};
621     }
622
623     $invoicediff += $diff;
624     ######## this only applies to tax included
625     if ($lastincomeaccno) {
626       $form->{amount}{ $form->{id} }{$lastincomeaccno} += $invoicediff;
627     }
628
629   } else {
630     $amount    = $form->round_amount($netamount * $form->{exchangerate}, 2);
631     $diff      = $amount - $netamount * $form->{exchangerate};
632     $netamount = $amount;
633     foreach my $item (split / /, $form->{taxaccounts}) {
634       $form->{amount}{ $form->{id} }{$item} =
635         $form->round_amount($form->{amount}{ $form->{id} }{$item}, 2);
636       $amount =
637         $form->round_amount(
638                  $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate},
639                  2);
640       $diff +=
641         $amount - $form->{amount}{ $form->{id} }{$item} *
642         $form->{exchangerate};
643       $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount, 2);
644       $tax += $form->{amount}{ $form->{id} }{$item};
645     }
646   }
647
648   $form->{amount}{ $form->{id} }{ $form->{AR} } = $netamount + $tax;
649   $form->{paid} =
650     $form->round_amount($form->{paid} * $form->{exchangerate} + $diff, 2);
651
652   # reverse AR
653   $form->{amount}{ $form->{id} }{ $form->{AR} } *= -1;
654
655   # update exchangerate
656   if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
657     $form->update_exchangerate($dbh, $form->{currency}, $form->{invdate},
658                                $form->{exchangerate}, 0);
659   }
660
661   foreach my $trans_id (keys %{ $form->{amount} }) {
662     foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
663       next unless ($form->{expense_inventory} =~ /$accno/);
664       if (
665           ($form->{amount}{$trans_id}{$accno} =
666            $form->round_amount($form->{amount}{$trans_id}{$accno}, 2)
667           ) != 0
668         ) {
669         $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
670                     transdate, taxkey)
671                     VALUES ($trans_id, (SELECT c.id FROM chart c
672                                         WHERE c.accno = '$accno'),
673                     $form->{amount}{$trans_id}{$accno}, '$form->{invdate}',
674                     (SELECT taxkey_id  FROM chart WHERE accno = '$accno'))|;
675         $dbh->do($query) || $form->dberror($query);
676         $form->{amount}{$trans_id}{$accno} = 0;
677       }
678     }
679
680     foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
681       if (
682           ($form->{amount}{$trans_id}{$accno} =
683            $form->round_amount($form->{amount}{$trans_id}{$accno}, 2)
684           ) != 0
685         ) {
686         $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
687                     transdate, taxkey)
688                     VALUES ($trans_id, (SELECT id FROM chart
689                                         WHERE accno = '$accno'),
690                     $form->{amount}{$trans_id}{$accno}, '$form->{invdate}',
691                     (SELECT taxkey_id  FROM chart WHERE accno = '$accno'))|;
692         $dbh->do($query) || $form->dberror($query);
693       }
694     }
695   }
696
697   # deduct payment differences from diff
698   for my $i (1 .. $form->{paidaccounts}) {
699     if ($form->{"paid_$i"} != 0) {
700       $amount =
701         $form->round_amount($form->{"paid_$i"} * $form->{exchangerate}, 2);
702       $diff -= $amount - $form->{"paid_$i"} * $form->{exchangerate};
703     }
704   }
705
706   # force AR entry if 0
707   #  $form->{amount}{$form->{id}}{$form->{AR}} = 1 if ($form->{amount}{$form->{id}}{$form->{AR}} == 0);
708
709   # record payments and offsetting AR
710   for my $i (1 .. $form->{paidaccounts}) {
711
712     if ($form->{"paid_$i"} != 0) {
713       my ($accno) = split /--/, $form->{"AR_paid_$i"};
714       $form->{"datepaid_$i"} = $form->{invdate}
715         unless ($form->{"datepaid_$i"});
716       $form->{datepaid} = $form->{"datepaid_$i"};
717
718       $exchangerate = 0;
719
720       if ($form->{currency} eq $form->{defaultcurrency}) {
721         $form->{"exchangerate_$i"} = 1;
722       } else {
723         $exchangerate =
724           $form->check_exchangerate($myconfig, $form->{currency},
725                                     $form->{"datepaid_$i"}, 'buy');
726
727         $form->{"exchangerate_$i"} =
728           ($exchangerate)
729           ? $exchangerate
730           : $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
731       }
732
733       # record AR
734       $amount =
735         $form->round_amount($form->{"paid_$i"} * $form->{exchangerate} + $diff,
736                             2);
737
738       if ($form->{amount}{ $form->{id} }{ $form->{AR} } != 0) {
739         $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
740                     transdate)
741                     VALUES ($form->{id}, (SELECT c.id FROM chart c
742                                         WHERE c.accno = '$form->{AR}'),
743                     $amount, '$form->{"datepaid_$i"}')|;
744         $dbh->do($query) || $form->dberror($query);
745       }
746
747       # record payment
748       $form->{"paid_$i"} *= -1;
749
750       $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
751                   source, memo)
752                   VALUES ($form->{id}, (SELECT c.id FROM chart c
753                                       WHERE c.accno = '$accno'),
754                   $form->{"paid_$i"}, '$form->{"datepaid_$i"}',
755                   '$form->{"source_$i"}', '$form->{"memo_$i"}')|;
756       $dbh->do($query) || $form->dberror($query);
757
758       # exchangerate difference
759       $form->{fx}{$accno}{ $form->{"datepaid_$i"} } +=
760         $form->{"paid_$i"} * ($form->{"exchangerate_$i"} - 1) + $diff;
761
762       # gain/loss
763       $amount =
764         $form->{"paid_$i"} * $form->{exchangerate} - $form->{"paid_$i"} *
765         $form->{"exchangerate_$i"};
766       if ($amount > 0) {
767         $form->{fx}{ $form->{fxgain_accno} }{ $form->{"datepaid_$i"} } +=
768           $amount;
769       } else {
770         $form->{fx}{ $form->{fxloss_accno} }{ $form->{"datepaid_$i"} } +=
771           $amount;
772       }
773
774       $diff = 0;
775
776       # update exchange rate
777       if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
778         $form->update_exchangerate($dbh, $form->{currency},
779                                    $form->{"datepaid_$i"},
780                                    $form->{"exchangerate_$i"}, 0);
781       }
782     }
783   }
784
785   # record exchange rate differences and gains/losses
786   foreach my $accno (keys %{ $form->{fx} }) {
787     foreach my $transdate (keys %{ $form->{fx}{$accno} }) {
788       if (
789           ($form->{fx}{$accno}{$transdate} =
790            $form->round_amount($form->{fx}{$accno}{$transdate}, 2)
791           ) != 0
792         ) {
793
794         $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
795                     transdate, cleared, fx_transaction)
796                     VALUES ($form->{id},
797                            (SELECT c.id FROM chart c
798                             WHERE c.accno = '$accno'),
799                     $form->{fx}{$accno}{$transdate}, '$transdate', '0', '1')|;
800         $dbh->do($query) || $form->dberror($query);
801       }
802     }
803   }
804
805   $amount = $netamount + $tax;
806
807   # set values which could be empty to 0
808   $form->{terms}       *= 1;
809   $form->{taxincluded} *= 1;
810   my $datepaid = ($form->{paid})    ? qq|'$form->{datepaid}'| : "NULL";
811   my $duedate  = ($form->{duedate}) ? qq|'$form->{duedate}'|  : "NULL";
812   my $deliverydate =
813     ($form->{deliverydate}) ? qq|'$form->{deliverydate}'| : "NULL";
814
815   # fill in subject if there is none
816   $form->{subject} = qq|$form->{label} $form->{invnumber}|
817     unless $form->{subject};
818
819   # if there is a message stuff it into the intnotes
820   my $cc  = "Cc: $form->{cc}\\r\n"   if $form->{cc};
821   my $bcc = "Bcc: $form->{bcc}\\r\n" if $form->{bcc};
822   my $now = scalar localtime;
823   $form->{intnotes} .= qq|\r
824 \r| if $form->{intnotes};
825
826   $form->{intnotes} .= qq|[email]\r
827 Date: $now
828 To: $form->{email}\r
829 $cc${bcc}Subject: $form->{subject}\r
830 \r
831 Message: $form->{message}\r| if $form->{message};
832
833   # save AR record
834   $query = qq|UPDATE ar set
835               invnumber = '$form->{invnumber}',
836               ordnumber = '$form->{ordnumber}',
837               quonumber = '$form->{quonumber}',
838               cusordnumber = '$form->{cusordnumber}',
839               transdate = '$form->{invdate}',
840               customer_id = $form->{customer_id},
841               amount = $amount,
842               netamount = $netamount,
843               paid = $form->{paid},
844               datepaid = $datepaid,
845               duedate = $duedate,
846               deliverydate = $deliverydate,
847               invoice = '1',
848               shippingpoint = '$form->{shippingpoint}',
849               shipvia = '$form->{shipvia}',
850               terms = $form->{terms},
851               notes = '$form->{notes}',
852               intnotes = '$form->{intnotes}',
853               taxincluded = '$form->{taxincluded}',
854               curr = '$form->{currency}',
855               department_id = $form->{department_id},
856               employee_id = $form->{employee_id},
857               cp_id = $form->{contact_id}
858               WHERE id = $form->{id}
859              |;
860   $dbh->do($query) || $form->dberror($query);
861
862   $form->{pago_total} = $amount;
863
864   # add shipto
865   $form->{name} = $form->{customer};
866   $form->{name} =~ s/--$form->{customer_id}//;
867   $form->add_shipto($dbh, $form->{id});
868
869   # save printed, emailed and queued
870   $form->save_status($dbh);
871
872   if ($form->{webdav}) {
873     &webdav_folder($myconfig, $form);
874   }
875
876   my $rc = $dbh->commit;
877   $dbh->disconnect;
878
879   $main::lxdebug->leave_sub();
880
881   return $rc;
882 }
883
884 sub process_assembly {
885   $main::lxdebug->enter_sub();
886
887   my ($dbh, $form, $id, $totalqty) = @_;
888
889   my $query = qq|SELECT a.parts_id, a.qty, p.assembly,
890                  p.partnumber, p.description, p.unit,
891                  p.inventory_accno_id, p.income_accno_id,
892                  p.expense_accno_id
893                  FROM assembly a
894                  JOIN parts p ON (a.parts_id = p.id)
895                  WHERE a.id = $id|;
896   my $sth = $dbh->prepare($query);
897   $sth->execute || $form->dberror($query);
898
899   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
900
901     my $allocated = 0;
902
903     $ref->{inventory_accno_id} *= 1;
904     $ref->{expense_accno_id}   *= 1;
905
906     map { $ref->{$_} =~ s/\'/\'\'/g } (qw(partnumber description unit));
907
908     # multiply by number of assemblies
909     $ref->{qty} *= $totalqty;
910
911     if ($ref->{assembly}) {
912       &process_assembly($dbh, $form, $ref->{parts_id}, $ref->{qty});
913       next;
914     } else {
915       if ($ref->{inventory_accno_id}) {
916         $allocated = &cogs($dbh, $form, $ref->{parts_id}, $ref->{qty});
917       }
918     }
919
920     # save detail record for individual assembly item in invoice table
921     $query = qq|INSERT INTO invoice (trans_id, description, parts_id, qty,
922                 sellprice, fxsellprice, allocated, assemblyitem, unit)
923                 VALUES
924                 ($form->{id}, '$ref->{description}',
925                 $ref->{parts_id}, $ref->{qty}, 0, 0, $allocated, 't',
926                 '$ref->{unit}')|;
927     $dbh->do($query) || $form->dberror($query);
928
929   }
930
931   $sth->finish;
932
933   $main::lxdebug->leave_sub();
934 }
935
936 sub cogs {
937   $main::lxdebug->enter_sub();
938
939   my ($dbh, $form, $id, $totalqty) = @_;
940
941   my $query = qq|SELECT i.id, i.trans_id, i.qty, i.allocated, i.sellprice,
942                    (SELECT c.accno FROM chart c
943                     WHERE p.inventory_accno_id = c.id) AS inventory_accno,
944                    (SELECT c.accno FROM chart c
945                     WHERE p.expense_accno_id = c.id) AS expense_accno
946                   FROM invoice i, parts p
947                   WHERE i.parts_id = p.id
948                   AND i.parts_id = $id
949                   AND (i.qty + i.allocated) < 0
950                   ORDER BY trans_id|;
951   my $sth = $dbh->prepare($query);
952   $sth->execute || $form->dberror($query);
953
954   my $allocated = 0;
955   my $qty;
956
957   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
958     if (($qty = (($ref->{qty} * -1) - $ref->{allocated})) > $totalqty) {
959       $qty = $totalqty;
960     }
961
962     $form->update_balance($dbh, "invoice", "allocated", qq|id = $ref->{id}|,
963                           $qty);
964
965     # total expenses and inventory
966     # sellprice is the cost of the item
967     $linetotal = $form->round_amount($ref->{sellprice} * $qty, 2);
968
969     if (!$eur) {
970
971       # add to expense
972       $form->{amount}{ $form->{id} }{ $ref->{expense_accno} } += -$linetotal;
973       $form->{expense_inventory} .= " " . $ref->{expense_accno};
974
975       # deduct inventory
976       $form->{amount}{ $form->{id} }{ $ref->{inventory_accno} } -= -$linetotal;
977       $form->{expense_inventory} .= " " . $ref->{inventory_accno};
978     }
979
980     # add allocated
981     $allocated += -$qty;
982
983     last if (($totalqty -= $qty) <= 0);
984   }
985
986   $sth->finish;
987
988   $main::lxdebug->leave_sub();
989
990   return $allocated;
991 }
992
993 sub reverse_invoice {
994   $main::lxdebug->enter_sub();
995
996   my ($dbh, $form) = @_;
997
998   # reverse inventory items
999   my $query = qq|SELECT i.id, i.parts_id, i.qty, i.assemblyitem, p.assembly,
1000                  p.inventory_accno_id
1001                  FROM invoice i
1002                  JOIN parts p ON (i.parts_id = p.id)
1003                  WHERE i.trans_id = $form->{id}|;
1004   my $sth = $dbh->prepare($query);
1005   $sth->execute || $form->dberror($query);
1006
1007   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1008
1009     if ($ref->{inventory_accno_id} || $ref->{assembly}) {
1010
1011       # if the invoice item is not an assemblyitem adjust parts onhand
1012       if (!$ref->{assemblyitem}) {
1013
1014         # adjust onhand in parts table
1015         $form->update_balance($dbh, "parts", "onhand",
1016                               qq|id = $ref->{parts_id}|,
1017                               $ref->{qty});
1018       }
1019
1020       # loop if it is an assembly
1021       next if ($ref->{assembly});
1022
1023       # de-allocated purchases
1024       $query = qq|SELECT i.id, i.trans_id, i.allocated
1025                   FROM invoice i
1026                   WHERE i.parts_id = $ref->{parts_id}
1027                   AND i.allocated > 0
1028                   ORDER BY i.trans_id DESC|;
1029       my $sth = $dbh->prepare($query);
1030       $sth->execute || $form->dberror($query);
1031
1032       while (my $inhref = $sth->fetchrow_hashref(NAME_lc)) {
1033         $qty = $ref->{qty};
1034         if (($ref->{qty} - $inhref->{allocated}) > 0) {
1035           $qty = $inhref->{allocated};
1036         }
1037
1038         # update invoice
1039         $form->update_balance($dbh, "invoice", "allocated",
1040                               qq|id = $inhref->{id}|,
1041                               $qty * -1);
1042
1043         last if (($ref->{qty} -= $qty) <= 0);
1044       }
1045       $sth->finish;
1046     }
1047   }
1048
1049   $sth->finish;
1050
1051   # delete acc_trans
1052   $query = qq|DELETE FROM acc_trans
1053               WHERE trans_id = $form->{id}|;
1054   $dbh->do($query) || $form->dberror($query);
1055
1056   # delete invoice entries
1057   $query = qq|DELETE FROM invoice
1058               WHERE trans_id = $form->{id}|;
1059   $dbh->do($query) || $form->dberror($query);
1060
1061   if ($form->{lizenzen}) {
1062     $query = qq|DELETE FROM licenseinvoice
1063               WHERE trans_id in (SELECT id FROM invoice WHERE trans_id = $form->{id})|;
1064     $dbh->do($query) || $form->dberror($query);
1065   }
1066
1067   $query = qq|DELETE FROM shipto
1068               WHERE trans_id = $form->{id}|;
1069   $dbh->do($query) || $form->dberror($query);
1070
1071   $main::lxdebug->leave_sub();
1072 }
1073
1074 sub delete_invoice {
1075   $main::lxdebug->enter_sub();
1076
1077   my ($self, $myconfig, $form, $spool) = @_;
1078
1079   # connect to database
1080   my $dbh = $form->dbconnect_noauto($myconfig);
1081
1082   &reverse_invoice($dbh, $form);
1083
1084   # delete AR record
1085   my $query = qq|DELETE FROM ar
1086                  WHERE id = $form->{id}|;
1087   $dbh->do($query) || $form->dberror($query);
1088
1089   # delete spool files
1090   $query = qq|SELECT s.spoolfile FROM status s
1091               WHERE s.trans_id = $form->{id}|;
1092   my $sth = $dbh->prepare($query);
1093   $sth->execute || $self->dberror($query);
1094
1095   my $spoolfile;
1096   my @spoolfiles = ();
1097
1098   while (($spoolfile) = $sth->fetchrow_array) {
1099     push @spoolfiles, $spoolfile;
1100   }
1101   $sth->finish;
1102
1103   # delete status entries
1104   $query = qq|DELETE FROM status
1105               WHERE trans_id = $form->{id}|;
1106   $dbh->do($query) || $form->dberror($query);
1107
1108   my $rc = $dbh->commit;
1109   $dbh->disconnect;
1110
1111   if ($rc) {
1112     foreach $spoolfile (@spoolfiles) {
1113       unlink "$spool/$spoolfile" if $spoolfile;
1114     }
1115   }
1116
1117   $main::lxdebug->leave_sub();
1118
1119   return $rc;
1120 }
1121
1122 sub retrieve_invoice {
1123   $main::lxdebug->enter_sub();
1124
1125   my ($self, $myconfig, $form) = @_;
1126
1127   # connect to database
1128   my $dbh = $form->dbconnect_noauto($myconfig);
1129
1130   my $query;
1131
1132   if ($form->{id}) {
1133
1134     # get default accounts and last invoice number
1135     $query = qq|SELECT (SELECT c.accno FROM chart c
1136                         WHERE d.inventory_accno_id = c.id) AS inventory_accno,
1137                        (SELECT c.accno FROM chart c
1138                         WHERE d.income_accno_id = c.id) AS income_accno,
1139                        (SELECT c.accno FROM chart c
1140                         WHERE d.expense_accno_id = c.id) AS expense_accno,
1141                        (SELECT c.accno FROM chart c
1142                         WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
1143                        (SELECT c.accno FROM chart c
1144                         WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
1145                 d.curr AS currencies
1146                 FROM defaults d|;
1147   } else {
1148     $query = qq|SELECT (SELECT c.accno FROM chart c
1149                         WHERE d.inventory_accno_id = c.id) AS inventory_accno,
1150                        (SELECT c.accno FROM chart c
1151                         WHERE d.income_accno_id = c.id) AS income_accno,
1152                        (SELECT c.accno FROM chart c
1153                         WHERE d.expense_accno_id = c.id) AS expense_accno,
1154                        (SELECT c.accno FROM chart c
1155                         WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
1156                        (SELECT c.accno FROM chart c
1157                         WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
1158                 d.curr AS currencies, current_date AS invdate
1159                 FROM defaults d|;
1160   }
1161   my $sth = $dbh->prepare($query);
1162   $sth->execute || $form->dberror($query);
1163
1164   my $ref = $sth->fetchrow_hashref(NAME_lc);
1165   map { $form->{$_} = $ref->{$_} } keys %$ref;
1166   $sth->finish;
1167
1168   if ($form->{id}) {
1169
1170     # retrieve invoice
1171     $query = qq|SELECT a.invnumber, a.ordnumber, a.quonumber, a.cusordnumber,
1172                 a.transdate AS invdate, a.deliverydate, a.paid,
1173                 a.shippingpoint, a.shipvia, a.terms, a.notes, a.intnotes,
1174                 a.duedate, a.taxincluded, a.curr AS currency,
1175                 a.employee_id, e.name AS employee
1176                 FROM ar a
1177                 LEFT JOIN employee e ON (e.id = a.employee_id)
1178                 WHERE a.id = $form->{id}|;
1179     $sth = $dbh->prepare($query);
1180     $sth->execute || $form->dberror($query);
1181
1182     $ref = $sth->fetchrow_hashref(NAME_lc);
1183     map { $form->{$_} = $ref->{$_} } keys %$ref;
1184     $sth->finish;
1185
1186     $form->{exchangerate} =
1187       $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate},
1188                               "buy");
1189
1190     # get shipto
1191     $query = qq|SELECT s.* FROM shipto s
1192                 WHERE s.trans_id = $form->{id}|;
1193     $sth = $dbh->prepare($query);
1194     $sth->execute || $form->dberror($query);
1195
1196     $ref = $sth->fetchrow_hashref(NAME_lc);
1197     map { $form->{$_} = $ref->{$_} } keys %$ref;
1198     $sth->finish;
1199
1200     # get printed, emailed
1201     $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname
1202                 FROM status s
1203                 WHERE s.trans_id = $form->{id}|;
1204     $sth = $dbh->prepare($query);
1205     $sth->execute || $form->dberror($query);
1206
1207     while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
1208       $form->{printed} .= "$ref->{formname} " if $ref->{printed};
1209       $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
1210       $form->{queued} .= "$ref->{formname} $ref->{spoolfile} "
1211         if $ref->{spoolfile};
1212     }
1213     $sth->finish;
1214     map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
1215
1216     # retrieve individual items
1217     $query = qq|SELECT (SELECT c.accno FROM chart c
1218                        WHERE p.inventory_accno_id = c.id)
1219                        AS inventory_accno,
1220                        (SELECT c.accno FROM chart c
1221                        WHERE p.income_accno_id = c.id)
1222                        AS income_accno,
1223                        (SELECT c.accno FROM chart c
1224                        WHERE p.expense_accno_id = c.id)
1225                        AS expense_accno,
1226                 i.description, i.qty, i.fxsellprice AS sellprice,
1227                 i.discount, i.parts_id AS id, i.unit, i.deliverydate,
1228                 i.project_id, pr.projectnumber, i.serialnumber,
1229                 p.partnumber, p.assembly, p.bin, p.notes AS partnotes, i.id AS invoice_pos,
1230                 pg.partsgroup, i.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=i.pricegroup_id) as pricegroup,
1231                 i.ordnumber, i.transdate, i.cusordnumber
1232                 FROM invoice i
1233                 JOIN parts p ON (i.parts_id = p.id)
1234                 LEFT JOIN project pr ON (i.project_id = pr.id)
1235                 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1236                 WHERE i.trans_id = $form->{id}
1237                 AND NOT i.assemblyitem = '1'
1238                 ORDER BY i.id|;
1239     $sth = $dbh->prepare($query);
1240     $sth->execute || $form->dberror($query);
1241     while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1242
1243       #set expense_accno=inventory_accno if they are different => bilanz
1244       $vendor_accno =
1245         ($ref->{expense_accno} != $ref->{inventory_accno})
1246         ? $ref->{inventory_accno}
1247         : $ref->{expense_accno};
1248
1249       # get tax rates and description
1250       $accno_id =
1251         ($form->{vc} eq "customer") ? $ref->{income_accno} : $vendor_accno;
1252       $query = qq|SELECT c.accno, c.description, t.rate, t.taxnumber
1253                  FROM chart c, tax t
1254                  WHERE c.id=t.chart_id AND t.taxkey in (SELECT taxkey_id from chart where accno = '$accno_id')
1255                  ORDER BY accno|;
1256       $stw = $dbh->prepare($query);
1257       $stw->execute || $form->dberror($query);
1258       $ref->{taxaccounts} = "";
1259       while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
1260
1261         #    if ($customertax{$ref->{accno}}) {
1262         $ref->{taxaccounts} .= "$ptr->{accno} ";
1263         if (!($form->{taxaccounts} =~ /$ptr->{accno}/)) {
1264           $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
1265           $form->{"$ptr->{accno}_description"} = $ptr->{description};
1266           $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
1267           $form->{taxaccounts} .= "$ptr->{accno} ";
1268         }
1269
1270       }
1271
1272       if ($form->{lizenzen}) {
1273         $query = qq|SELECT l.licensenumber, l.id AS licenseid
1274                  FROM license l, licenseinvoice li
1275                  WHERE l.id = li.license_id AND li.trans_id = $ref->{invoice_pos}|;
1276         $stg = $dbh->prepare($query);
1277         $stg->execute || $form->dberror($query);
1278         ($licensenumber, $licenseid) = $stg->fetchrow_array();
1279         $ref->{lizenzen} =
1280           "<option value=\"$licenseid\">$licensenumber</option>";
1281         $stg->finish();
1282       }
1283
1284       chop $ref->{taxaccounts};
1285       push @{ $form->{invoice_details} }, $ref;
1286       $stw->finish;
1287     }
1288     $sth->finish;
1289
1290     if ($form->{webdav}) {
1291       &webdav_folder($myconfig, $form);
1292     }
1293   }
1294
1295   my $rc = $dbh->commit;
1296   $dbh->disconnect;
1297
1298   $main::lxdebug->leave_sub();
1299
1300   return $rc;
1301 }
1302
1303 sub get_customer {
1304   $main::lxdebug->enter_sub();
1305
1306   my ($self, $myconfig, $form) = @_;
1307
1308   # connect to database
1309   my $dbh = $form->dbconnect($myconfig);
1310
1311   my $dateformat = $myconfig->{dateformat};
1312   $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
1313
1314   my $duedate =
1315     ($form->{invdate})
1316     ? "to_date('$form->{invdate}', '$dateformat')"
1317     : "current_date";
1318
1319   $form->{customer_id} *= 1;
1320
1321   # get customer
1322   my $query = qq|SELECT c.name AS customer, c.discount, c.creditlimit, c.terms,
1323                  c.email, c.cc, c.bcc, c.language,
1324                  c.street, c.zipcode, c.city, c.country,
1325                  $duedate + c.terms AS duedate, c.notes AS intnotes,
1326                  b.discount AS tradediscount, b.description AS business, c.klass as customer_klass
1327                  FROM customer c
1328                  LEFT JOIN business b ON (b.id = c.business_id)
1329                  WHERE c.id = $form->{customer_id}|;
1330   my $sth = $dbh->prepare($query);
1331   $sth->execute || $form->dberror($query);
1332
1333   $ref = $sth->fetchrow_hashref(NAME_lc);
1334
1335   map { $form->{$_} = $ref->{$_} } keys %$ref;
1336   $sth->finish;
1337
1338   $form->{creditremaining} = $form->{creditlimit};
1339   $query = qq|SELECT SUM(a.amount - a.paid)
1340               FROM ar a
1341               WHERE a.customer_id = $form->{customer_id}|;
1342   $sth = $dbh->prepare($query);
1343   $sth->execute || $form->dberror($query);
1344
1345   ($form->{creditremaining}) -= $sth->fetchrow_array;
1346
1347   $sth->finish;
1348
1349   $query = qq|SELECT o.amount,
1350                 (SELECT e.buy FROM exchangerate e
1351                  WHERE e.curr = o.curr
1352                  AND e.transdate = o.transdate)
1353               FROM oe o
1354               WHERE o.customer_id = $form->{customer_id}
1355               AND o.quotation = '0'
1356               AND o.closed = '0'|;
1357   $sth = $dbh->prepare($query);
1358   $sth->execute || $form->dberror($query);
1359
1360   while (my ($amount, $exch) = $sth->fetchrow_array) {
1361     $exch = 1 unless $exch;
1362     $form->{creditremaining} -= $amount * $exch;
1363   }
1364   $sth->finish;
1365
1366   $form->get_contacts($dbh, $form->{customer_id});
1367   ($null, $form->{cp_id}) = split /--/, $form->{contact};
1368
1369   # get contact if selected
1370   if ($form->{contact} ne "--" && $form->{contact} ne "") {
1371     $form->get_contact($dbh, $form->{cp_id});
1372   }
1373
1374   # get shipto if we did not converted an order or invoice
1375   if (!$form->{shipto}) {
1376     map { delete $form->{$_} }
1377       qw(shiptoname shiptodepartment_1 shiptodepartment_2 shiptostreet shiptozipcode shiptocity shiptocountry shiptocontact shiptophone shiptofax shiptoemail);
1378
1379     $query = qq|SELECT s.* FROM shipto s
1380                 WHERE s.trans_id = $form->{customer_id}|;
1381     $sth = $dbh->prepare($query);
1382     $sth->execute || $form->dberror($query);
1383
1384     $ref = $sth->fetchrow_hashref(NAME_lc);
1385     map { $form->{$_} = $ref->{$_} } keys %$ref;
1386     $sth->finish;
1387   }
1388
1389   # get taxes we charge for this customer
1390   $query = qq|SELECT c.accno
1391               FROM chart c
1392               JOIN customertax ct ON (ct.chart_id = c.id)
1393               WHERE ct.customer_id = $form->{customer_id}|;
1394   $sth = $dbh->prepare($query);
1395   $sth->execute || $form->dberror($query);
1396
1397   my $customertax = ();
1398   while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
1399     $customertax{ $ref->{accno} } = 1;
1400   }
1401   $sth->finish;
1402
1403   # setup last accounts used for this customer
1404   if (!$form->{id} && $form->{type} !~ /_(order|quotation)/) {
1405     $query = qq|SELECT c.accno, c.description, c.link, c.category
1406                 FROM chart c
1407                 JOIN acc_trans ac ON (ac.chart_id = c.id)
1408                 JOIN ar a ON (a.id = ac.trans_id)
1409                 WHERE a.customer_id = $form->{customer_id}
1410                 AND NOT (c.link LIKE '%_tax%' OR c.link LIKE '%_paid%')
1411                 AND a.id IN (SELECT max(a2.id) FROM ar a2
1412                              WHERE a2.customer_id = $form->{customer_id})|;
1413     $sth = $dbh->prepare($query);
1414     $sth->execute || $form->dberror($query);
1415
1416     my $i = 0;
1417     while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
1418       if ($ref->{category} eq 'I') {
1419         $i++;
1420         $form->{"AR_amount_$i"} = "$ref->{accno}--$ref->{description}";
1421       }
1422       if ($ref->{category} eq 'A') {
1423         $form->{ARselected} = $form->{AR_1} =
1424           "$ref->{accno}--$ref->{description}";
1425       }
1426     }
1427     $sth->finish;
1428     $form->{rowcount} = $i if ($i && !$form->{type});
1429   }
1430
1431   $dbh->disconnect;
1432
1433   $main::lxdebug->leave_sub();
1434 }
1435
1436 sub retrieve_item {
1437   $main::lxdebug->enter_sub();
1438
1439   my ($self, $myconfig, $form) = @_;
1440
1441   my $i = $form->{rowcount};
1442
1443   my $where = "NOT p.obsolete = '1'";
1444
1445   if ($form->{"partnumber_$i"}) {
1446     my $partnumber = $form->like(lc $form->{"partnumber_$i"});
1447     $where .= " AND lower(p.partnumber) LIKE '$partnumber'";
1448   }
1449   if ($form->{"description_$i"}) {
1450     my $description = $form->like(lc $form->{"description_$i"});
1451     $where .= " AND lower(p.description) LIKE '$description'";
1452   }
1453
1454   if ($form->{"partsgroup_$i"}) {
1455     my $partsgroup = $form->like(lc $form->{"partsgroup_$i"});
1456     $where .= " AND lower(pg.partsgroup) LIKE '$partsgroup'";
1457   }
1458
1459   if ($form->{"description_$i"}) {
1460     $where .= " ORDER BY p.description";
1461   } else {
1462     $where .= " ORDER BY p.partnumber";
1463   }
1464
1465   # connect to database
1466   my $dbh = $form->dbconnect($myconfig);
1467
1468   my $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
1469                         p.listprice,
1470                         c1.accno AS inventory_accno,
1471                         c2.accno AS income_accno,
1472                         c3.accno AS expense_accno,
1473                  p.unit, p.assembly, p.bin, p.onhand, p.notes AS partnotes,
1474                  pg.partsgroup
1475                  FROM parts p
1476                  LEFT JOIN chart c1 ON (p.inventory_accno_id = c1.id)
1477                  LEFT JOIN chart c2 ON (p.income_accno_id = c2.id)
1478                  LEFT JOIN chart c3 ON (p.expense_accno_id = c3.id)
1479                  LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
1480                  WHERE $where|;
1481   my $sth = $dbh->prepare($query);
1482   $sth->execute || $form->dberror($query);
1483
1484   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1485
1486     #set expense_accno=inventory_accno if they are different => bilanz
1487     $vendor_accno =
1488       ($ref->{expense_accno} != $ref->{inventory_accno})
1489       ? $ref->{inventory_accno}
1490       : $ref->{expense_accno};
1491
1492     # get tax rates and description
1493     $accno_id =
1494       ($form->{vc} eq "customer") ? $ref->{income_accno} : $vendor_accno;
1495     $query = qq|SELECT c.accno, c.description, t.rate, t.taxnumber
1496               FROM chart c, tax t
1497               WHERE c.id=t.chart_id AND t.taxkey in (SELECT c2.taxkey_id from chart c2 where c2.accno = '$accno_id')
1498               ORDER BY c.accno|;
1499     $stw = $dbh->prepare($query);
1500     $stw->execute || $form->dberror($query);
1501
1502     $ref->{taxaccounts} = "";
1503     while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
1504
1505       #    if ($customertax{$ref->{accno}}) {
1506       $ref->{taxaccounts} .= "$ptr->{accno} ";
1507       if (!($form->{taxaccounts} =~ /$ptr->{accno}/)) {
1508         $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
1509         $form->{"$ptr->{accno}_description"} = $ptr->{description};
1510         $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
1511         $form->{taxaccounts} .= "$ptr->{accno} ";
1512       }
1513
1514     }
1515
1516     $stw->finish;
1517     chop $ref->{taxaccounts};
1518
1519     push @{ $form->{item_list} }, $ref;
1520
1521     if ($form->{lizenzen}) {
1522       if ($ref->{inventory_accno} > 0) {
1523         $query =
1524           qq| SELECT l.* FROM license l WHERE l.parts_id = $ref->{id} AND NOT l.id IN (SELECT li.license_id FROM licenseinvoice li)|;
1525         $stw = $dbh->prepare($query);
1526         $stw->execute || $form->dberror($query);
1527         while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
1528           push @{ $form->{LIZENZEN}{ $ref->{id} } }, $ptr;
1529         }
1530         $stw->finish;
1531       }
1532     }
1533   }
1534   $sth->finish;
1535   $dbh->disconnect;
1536
1537   $main::lxdebug->leave_sub();
1538 }
1539
1540 ##########################
1541 # get pricegroups from database
1542 # build up selected pricegroup
1543 # if an exchange rate - change price
1544 # for each part
1545 #
1546 sub get_pricegroups_for_parts {
1547
1548   $main::lxdebug->enter_sub();
1549
1550   my ($self, $myconfig, $form) = @_;
1551
1552   my $dbh = $form->dbconnect($myconfig);
1553
1554   my $i  = 1;
1555   my $id = 0;
1556
1557   while (($form->{"id_$i"}) or ($form->{"new_id_$i"})) {
1558
1559     $id = $form->{"id_$i"};
1560
1561     if (!($form->{"id_$i"}) and $form->{"new_id_$i"}) {
1562
1563       $id = $form->{"new_id_$i"};
1564     }
1565
1566     ($price, $selectedpricegroup_id) = split /--/,
1567       $form->{"sellprice_drag_$i"};
1568
1569     $pricegroup_old = $form->{"pricegroup_old_$i"};
1570
1571     $price_new = $form->{"price_new_$i"};
1572
1573     $price_old = $form->{"price_old_$i"};
1574
1575     $query =
1576       qq|SELECT pricegroup_id, (SELECT p.sellprice from parts p where p.id = $id) as default_sellprice,(SELECT pg.pricegroup FROM pricegroup pg WHERE id=pricegroup_id) AS pricegroup, price, '' AS selected FROM prices WHERE parts_id = $id UNION SELECT 0 as pricegroup_id,(SELECT sellprice FROM parts WHERE id=$id) as default_sellprice,'' as pricegroup, (SELECT DISTINCT sellprice from parts where id=$id) as price, 'selected' AS selected from prices ORDER BY pricegroup|;
1577
1578     $pkq = $dbh->prepare($query);
1579     $pkq->execute || $form->dberror($query);
1580     while ($pkr = $pkq->fetchrow_hashref(NAME_lc)) {
1581
1582       #       push @{ $form->{PRICES}{$id} }, $pkr;
1583       push @{ $form->{PRICES}{$i} }, $pkr;
1584       $pkr->{id}       = $id;
1585       $pkr->{selected} = '';
1586
1587       # if there is an exchange rate change price
1588       if (($form->{exchangerate} * 1) != 0) {
1589
1590         $pkr->{price} /= $form->{exchangerate};
1591       }
1592       $pkr->{price} = $form->format_amount($myconfig, $pkr->{price}, 5);
1593
1594       if ($selectedpricegroup_id eq undef) {
1595         if ($pkr->{pricegroup_id} eq $form->{customer_klass}) {
1596
1597           $pkr->{selected}  = ' selected';
1598           $last->{selected} = '';
1599
1600           # no customer pricesgroup set
1601           if ($pkr->{price} == $pkr->{default_sellprice}) {
1602
1603             $pkr->{price} = $form->{"sellprice_$i"};
1604
1605           } else {
1606
1607             $form->{"sellprice_$i"} = $pkr->{price};
1608           }
1609
1610         } else {
1611           if ($pkr->{price} == $pkr->{default_sellprice}) {
1612
1613             $pkr->{price}    = $form->{"sellprice_$i"};
1614             $pkr->{selected} = ' selected';
1615           }
1616         }
1617       }
1618       if ($selectedpricegroup_id or $selectedpricegroup_id == 0) {
1619         if ($selectedpricegroup_id ne $pricegroup_old) {
1620           if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
1621             if ($price_new != $form->{"sellprice_$i"}) {
1622             } else {
1623               $pkr->{selected}  = ' selected';
1624               $last->{selected} = '';
1625             }
1626           }
1627         } else {
1628           if (($price_new != $form->{"sellprice_$i"}) and ($price_new ne 0)) {
1629             if ($pkr->{pricegroup_id} == 0) {
1630               $pkr->{price}     = $form->{"sellprice_$i"};
1631               $pkr->{selected}  = ' selected';
1632               $last->{selected} = '';
1633             }
1634           } else {
1635             if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
1636               $pkr->{selected}  = ' selected';
1637               $last->{selected} = '';
1638               if (    ($pkr->{pricegroup_id} == 0)
1639                   and ($pkr->{price} == $form->{"sellprice_$i"})) {
1640
1641                 # $pkr->{price}                         = $form->{"sellprice_$i"};
1642                   } else {
1643                 $pkr->{price} = $form->{"sellprice_$i"};
1644               }
1645             }
1646           }
1647         }
1648       }
1649     }
1650     $i++;
1651
1652     $pkq->finish;
1653   }
1654
1655   $dbh->disconnect;
1656
1657   $main::lxdebug->leave_sub();
1658 }
1659
1660 sub webdav_folder {
1661   $main::lxdebug->enter_sub();
1662
1663   my ($myconfig, $form) = @_;
1664
1665 SWITCH: {
1666     $path = "webdav/rechnungen/" . $form->{invnumber}, last SWITCH
1667       if ($form->{vc} eq "customer");
1668     $path = "webdav/einkaufsrechnungen/" . $form->{invnumber}, last SWITCH
1669       if ($form->{vc} eq "vendor");
1670   }
1671
1672   if (!-d $path) {
1673     mkdir($path, 0770) or die "can't make directory $!\n";
1674   } else {
1675     if ($form->{id}) {
1676       @files = <$path/*>;
1677       foreach $file (@files) {
1678         $file =~ /\/([^\/]*)$/;
1679         $fname = $1;
1680         $ENV{'SCRIPT_NAME'} =~ /\/([^\/]*)\//;
1681         $lxerp = $1;
1682         $link  = "http://" . $ENV{'SERVER_NAME'} . "/" . $lxerp . "/" . $file;
1683         $form->{WEBDAV}{$fname} = $link;
1684       }
1685     }
1686   }
1687
1688   $main::lxdebug->leave_sub();
1689 }
1690
1691 1;
1692