Beim Verschicken von Emails überprüfen, ob es im Vorlagenverzeichnis eine Vorlage...
[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 List::Util qw(max);
38
39 use SL::AM;
40 use SL::ARAP;
41 use SL::CVar;
42 use SL::Common;
43 use SL::DBUtils;
44 use SL::DO;
45 use SL::MoreCommon;
46 use Data::Dumper;
47
48 sub invoice_details {
49   $main::lxdebug->enter_sub();
50
51   my ($self, $myconfig, $form, $locale) = @_;
52
53   $form->{duedate} ||= $form->{invdate};
54
55   # connect to database
56   my $dbh = $form->dbconnect($myconfig);
57   my $sth;
58
59   my $query = qq|SELECT date | . conv_dateq($form->{duedate}) . qq| - date | . conv_dateq($form->{invdate}) . qq| AS terms|;
60   ($form->{terms}) = selectrow_query($form, $dbh, $query);
61
62   my (@project_ids, %projectnumbers);
63
64   push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
65
66   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
67   my %price_factors;
68
69   foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
70     $price_factors{$pfac->{id}}  = $pfac;
71     $pfac->{factor}             *= 1;
72     $pfac->{formatted_factor}    = $form->format_amount($myconfig, $pfac->{factor});
73   }
74
75   # sort items by partsgroup
76   for $i (1 .. $form->{rowcount}) {
77     $partsgroup = "";
78     if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
79       $partsgroup = $form->{"partsgroup_$i"};
80     }
81     push @partsgroup, [$i, $partsgroup];
82     push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
83   }
84
85   if (@project_ids) {
86     $query = "SELECT id, projectnumber FROM project WHERE id IN (" .
87       join(", ", map({ "?" } @project_ids)) . ")";
88     $sth = $dbh->prepare($query);
89     $sth->execute(@project_ids) ||
90       $form->dberror($query . " (" . join(", ", @project_ids) . ")");
91     while (my $ref = $sth->fetchrow_hashref()) {
92       $projectnumbers{$ref->{id}} = $ref->{projectnumber};
93     }
94     $sth->finish();
95   }
96
97   $form->{"globalprojectnumber"} =
98     $projectnumbers{$form->{"globalproject_id"}};
99
100   my $tax = 0;
101   my $item;
102   my $i;
103   my @partsgroup = ();
104   my $partsgroup;
105   my %oid = ('Pg'     => 'oid',
106              'Oracle' => 'rowid');
107
108   # sort items by partsgroup
109   for $i (1 .. $form->{rowcount}) {
110     $partsgroup = "";
111     if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
112       $partsgroup = $form->{"partsgroup_$i"};
113     }
114     push @partsgroup, [$i, $partsgroup];
115   }
116
117   my $sameitem = "";
118   my @taxaccounts;
119   my %taxaccounts;
120   my %taxbase;
121   my $taxrate;
122   my $taxamount;
123   my $taxbase;
124   my $taxdiff;
125   my $nodiscount;
126   my $yesdiscount;
127   my $nodiscount_subtotal = 0;
128   my $discount_subtotal = 0;
129   my $position = 0;
130   my $subtotal_header = 0;
131   my $subposition = 0;
132
133   $form->{discount} = [];
134
135   my @arrays =
136     qw(runningnumber number description longdescription qty ship unit bin
137        deliverydate_oe ordnumber_oe transdate_oe licensenumber validuntil
138        partnotes serialnumber reqdate sellprice listprice netprice
139        discount p_discount discount_sub nodiscount_sub
140        linetotal  nodiscount_linetotal tax_rate projectnumber
141        price_factor price_factor_name);
142
143   my @tax_arrays =
144     qw(taxbase tax taxdescription taxrate taxnumber);
145
146   foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
147     $i = $item->[0];
148
149     if ($item->[1] ne $sameitem) {
150       push(@{ $form->{description} }, qq|$item->[1]|);
151       $sameitem = $item->[1];
152
153       map({ push(@{ $form->{$_} }, "") } grep({ $_ ne "description" } @arrays));
154     }
155
156     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
157
158     if ($form->{"id_$i"} != 0) {
159
160       # add number, description and qty to $form->{number},
161       if ($form->{"subtotal_$i"} && !$subtotal_header) {
162         $subtotal_header = $i;
163         $position = int($position);
164         $subposition = 0;
165         $position++;
166       } elsif ($subtotal_header) {
167         $subposition += 1;
168         $position = int($position);
169         $position = $position.".".$subposition;
170       } else {
171         $position = int($position);
172         $position++;
173       }
174
175       my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
176
177       push @{ $form->{runningnumber} },     $position;
178       push @{ $form->{number} },            $form->{"partnumber_$i"};
179       push @{ $form->{serialnumber} },      $form->{"serialnumber_$i"};
180       push @{ $form->{bin} },               $form->{"bin_$i"};
181       push @{ $form->{"partnotes"} },       $form->{"partnotes_$i"};
182       push @{ $form->{description} },       $form->{"description_$i"};
183       push @{ $form->{longdescription} },   $form->{"longdescription_$i"};
184       push @{ $form->{qty} },               $form->format_amount($myconfig, $form->{"qty_$i"});
185       push @{ $form->{unit} },              $form->{"unit_$i"};
186       push @{ $form->{deliverydate_oe} },   $form->{"deliverydate_$i"};
187       push @{ $form->{sellprice} },         $form->{"sellprice_$i"};
188       push @{ $form->{ordnumber_oe} },      $form->{"ordnumber_$i"};
189       push @{ $form->{transdate_oe} },      $form->{"transdate_$i"};
190       push @{ $form->{invnumber} },         $form->{"invnumber"};
191       push @{ $form->{invdate} },           $form->{"invdate"};
192       push @{ $form->{price_factor} },      $price_factor->{formatted_factor};
193       push @{ $form->{price_factor_name} }, $price_factor->{description};
194
195       if ($form->{lizenzen}) {
196         if ($form->{"licensenumber_$i"}) {
197           $query = qq|SELECT licensenumber, validuntil FROM license WHERE id = ?|;
198           ($licensenumber, $validuntil) = selectrow_query($form, $dbh, $query, conv_i($form->{"licensenumber_$i"}));
199           push(@{ $form->{licensenumber} }, $licensenumber);
200           push(@{ $form->{validuntil} }, $locale->date($myconfig, $validuntil, 0));
201
202         } else {
203           push(@{ $form->{licensenumber} }, "");
204           push(@{ $form->{validuntil} },    "");
205         }
206       }
207
208       # listprice
209       push(@{ $form->{listprice} }, $form->{"listprice_$i"});
210
211       my $sellprice     = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
212       my ($dec)         = ($sellprice =~ /\.(\d+)/);
213       my $decimalplaces = max 2, length($dec);
214
215       my $parsed_discount      = $form->parse_amount($myconfig, $form->{"discount_$i"});
216       my $discount             = $form->round_amount($form->{"qty_$i"} * $sellprice * $parsed_discount / 100 / $price_factor->{factor}, $decimalplaces);
217       my $linetotal            = $form->round_amount($form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor}, 2);
218       my $nodiscount_linetotal = $form->round_amount($form->{"qty_$i"} * $sellprice / $price_factor->{factor}, 2);
219       $form->{"netprice_$i"}   = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, 2);
220
221       push @{ $form->{netprice} }, ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
222
223       $linetotal = ($linetotal != 0) ? $linetotal : '';
224
225       push @{ $form->{discount} },   ($discount  != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
226       push @{ $form->{p_discount} }, $form->{"discount_$i"};
227
228       $form->{total}            += $linetotal;
229       $form->{nodiscount_total} += $nodiscount_linetotal;
230       $form->{discount_total}   += $discount;
231
232       if ($subtotal_header) {
233         $discount_subtotal   += $linetotal;
234         $nodiscount_subtotal += $nodiscount_linetotal;
235       }
236
237       if ($form->{"subtotal_$i"} && $subtotal_header && ($subtotal_header != $i)) {
238         push @{ $form->{discount_sub} },   $form->format_amount($myconfig, $discount_subtotal,   2);
239         push @{ $form->{nodiscount_sub} }, $form->format_amount($myconfig, $nodiscount_subtotal, 2);
240
241         $discount_subtotal   = 0;
242         $nodiscount_subtotal = 0;
243         $subtotal_header     = 0;
244
245       } else {
246         push @{ $form->{discount_sub} },   "";
247         push @{ $form->{nodiscount_sub} }, "";
248       }
249
250       if (!$form->{"discount_$i"}) {
251         $nodiscount += $linetotal;
252       }
253
254       push @{ $form->{linetotal} }, $form->format_amount($myconfig, $linetotal, 2);
255       push @{ $form->{nodiscount_linetotal} }, $form->format_amount($myconfig, $nodiscount_linetotal, 2);
256
257       push(@{ $form->{projectnumber} }, $projectnumbers{$form->{"project_id_$i"}});
258
259       @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
260       $taxrate     = 0;
261       $taxdiff     = 0;
262
263       map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
264
265       if ($form->{taxincluded}) {
266
267         # calculate tax
268         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
269         $taxbase = $linetotal - $taxamount;
270       } else {
271         $taxamount = $linetotal * $taxrate;
272         $taxbase   = $linetotal;
273       }
274
275       if ($form->round_amount($taxrate, 7) == 0) {
276         if ($form->{taxincluded}) {
277           foreach $item (@taxaccounts) {
278             $taxamount =
279               $form->round_amount($linetotal * $form->{"${item}_rate"} /
280                                     (1 + abs($form->{"${item}_rate"})),
281                                   2);
282
283             $taxaccounts{$item} += $taxamount;
284             $taxdiff            += $taxamount;
285
286             $taxbase{$item} += $taxbase;
287           }
288           $taxaccounts{ $taxaccounts[0] } += $taxdiff;
289         } else {
290           foreach $item (@taxaccounts) {
291             $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
292             $taxbase{$item}     += $taxbase;
293           }
294         }
295       } else {
296         foreach $item (@taxaccounts) {
297           $taxaccounts{$item} +=
298             $taxamount * $form->{"${item}_rate"} / $taxrate;
299           $taxbase{$item} += $taxbase;
300         }
301       }
302       $tax_rate = $taxrate * 100;
303       push(@{ $form->{tax_rate} }, qq|$tax_rate|);
304       if ($form->{"assembly_$i"}) {
305         $sameitem = "";
306
307         # get parts and push them onto the stack
308         my $sortorder = "";
309         if ($form->{groupitems}) {
310           $sortorder =
311             qq|ORDER BY pg.partsgroup, a.$oid{$myconfig->{dbdriver}}|;
312         } else {
313           $sortorder = qq|ORDER BY a.$oid{$myconfig->{dbdriver}}|;
314         }
315
316         $query =
317           qq|SELECT p.partnumber, p.description, p.unit, a.qty, pg.partsgroup
318              FROM assembly a
319              JOIN parts p ON (a.parts_id = p.id)
320              LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
321              WHERE (a.bom = '1') AND (a.id = ?) $sortorder|;
322         $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{"id_$i"}));
323
324         while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
325           if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
326             map({ push(@{ $form->{$_} }, "") } grep({ $_ ne "description" } @arrays));
327             $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
328             push(@{ $form->{description} }, $sameitem);
329           }
330
331           map { $form->{"a_$_"} = $ref->{$_} } qw(partnumber description);
332
333           push(@{ $form->{description} },
334                $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}
335                  )
336                  . qq| -- $form->{"a_partnumber"}, $form->{"a_description"}|);
337           map({ push(@{ $form->{$_} }, "") } grep({ $_ ne "description" } @arrays));
338
339         }
340         $sth->finish;
341       }
342     }
343   }
344
345   foreach my $item (sort keys %taxaccounts) {
346     push(@{ $form->{taxbase} },
347           $form->format_amount($myconfig, $taxbase{$item}, 2));
348
349     $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
350
351     push(@{ $form->{tax} }, $form->format_amount($myconfig, $taxamount, 2));
352     push(@{ $form->{taxdescription} }, $form->{"${item}_description"}  . q{ } . 100 * $form->{"${item}_rate"} . q{%});
353     push(@{ $form->{taxrate} },
354           $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
355     push(@{ $form->{taxnumber} }, $form->{"${item}_taxnumber"});
356   }
357
358   for my $i (1 .. $form->{paidaccounts}) {
359     if ($form->{"paid_$i"}) {
360       push(@{ $form->{payment} }, $form->{"paid_$i"});
361       my ($accno, $description) = split(/--/, $form->{"AR_paid_$i"});
362       push(@{ $form->{paymentaccount} }, $description);
363       push(@{ $form->{paymentdate} },    $form->{"datepaid_$i"});
364       push(@{ $form->{paymentsource} },  $form->{"source_$i"});
365
366       $form->{paid} += $form->parse_amount($myconfig, $form->{"paid_$i"});
367     }
368   }
369   if($form->{taxincluded}) {
370     $form->{subtotal} = $form->format_amount($myconfig, $form->{total} - $tax, 2);
371   }
372   else {
373     $form->{subtotal} = $form->format_amount($myconfig, $form->{total}, 2);
374   }
375
376   $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
377   $form->{discount_total}      = $form->format_amount($myconfig, $form->{discount_total}, 2);
378   $form->{nodiscount}          = $form->format_amount($myconfig, $nodiscount, 2);
379   $form->{yesdiscount}         = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
380
381   $form->{invtotal} = ($form->{taxincluded}) ? $form->{total} : $form->{total} + $tax;
382   $form->{total}    = $form->format_amount($myconfig, $form->{invtotal} - $form->{paid}, 2);
383
384   $form->{invtotal} = $form->format_amount($myconfig, $form->{invtotal}, 2);
385   $form->{paid}     = $form->format_amount($myconfig, $form->{paid}, 2);
386
387   $form->set_payment_options($myconfig, $form->{invdate});
388
389   $form->{username} = $myconfig->{name};
390
391   $dbh->disconnect;
392
393   $main::lxdebug->leave_sub();
394 }
395
396 sub project_description {
397   $main::lxdebug->enter_sub();
398
399   my ($self, $dbh, $id) = @_;
400
401   my $query = qq|SELECT description FROM project WHERE id = ?|;
402   my ($description) = selectrow_query($form, $dbh, $query, conv_i($id));
403
404   $main::lxdebug->leave_sub();
405
406   return $_;
407 }
408
409 sub customer_details {
410   $main::lxdebug->enter_sub();
411
412   my ($self, $myconfig, $form, @wanted_vars) = @_;
413
414   # connect to database
415   my $dbh = $form->dbconnect($myconfig);
416
417   # get contact id, set it if nessessary
418   $form->{cp_id} *= 1;
419
420   my @values =  (conv_i($form->{customer_id}));
421
422   my $where = "";
423   if ($form->{cp_id}) {
424     $where = qq| AND (cp.cp_id = ?) |;
425     push(@values, conv_i($form->{cp_id}));
426   }
427
428   # get rest for the customer
429   my $query =
430     qq|SELECT ct.*, cp.*, ct.notes as customernotes,
431          ct.phone AS customerphone, ct.fax AS customerfax, ct.email AS customeremail
432        FROM customer ct
433        LEFT JOIN contacts cp on ct.id = cp.cp_cv_id
434        WHERE (ct.id = ?) $where
435        ORDER BY cp.cp_id
436        LIMIT 1|;
437   my $ref = selectfirst_hashref_query($form, $dbh, $query, @values);
438
439   # remove id and taxincluded before copy back
440   delete @$ref{qw(id taxincluded)};
441
442   @wanted_vars = grep({ $_ } @wanted_vars);
443   if (scalar(@wanted_vars) > 0) {
444     my %h_wanted_vars;
445     map({ $h_wanted_vars{$_} = 1; } @wanted_vars);
446     map({ delete($ref->{$_}) unless ($h_wanted_vars{$_}); } keys(%{$ref}));
447   }
448
449   map { $form->{$_} = $ref->{$_} } keys %$ref;
450
451   if ($form->{delivery_customer_id}) {
452     $query =
453       qq|SELECT *, notes as customernotes
454          FROM customer
455          WHERE id = ?
456          LIMIT 1|;
457     $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{delivery_customer_id}));
458
459     map { $form->{"dc_$_"} = $ref->{$_} } keys %$ref;
460   }
461
462   if ($form->{delivery_vendor_id}) {
463     $query =
464       qq|SELECT *, notes as customernotes
465          FROM customer
466          WHERE id = ?
467          LIMIT 1|;
468     $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{delivery_vendor_id}));
469
470     map { $form->{"dv_$_"} = $ref->{$_} } keys %$ref;
471   }
472
473   my $custom_variables = CVar->get_custom_variables('dbh'      => $dbh,
474                                                     'module'   => 'CT',
475                                                     'trans_id' => $form->{customer_id});
476   map { $form->{"vc_cvar_$_->{name}"} = $_->{value} } @{ $custom_variables };
477
478   $dbh->disconnect;
479
480   $main::lxdebug->leave_sub();
481 }
482
483 sub post_invoice {
484   $main::lxdebug->enter_sub();
485
486   my ($self, $myconfig, $form, $provided_dbh, $payments_only) = @_;
487
488   # connect to database, turn off autocommit
489   my $dbh = $provided_dbh ? $provided_dbh : $form->dbconnect_noauto($myconfig);
490
491   my ($query, $sth, $null, $project_id, @values);
492   my $exchangerate = 0;
493
494   if (!$form->{employee_id}) {
495     $form->get_employee($dbh);
496   }
497   
498   $form->{defaultcurrency} = $form->get_default_currency($myconfig);
499
500   ($null, $form->{department_id}) = split(/--/, $form->{department});
501
502   my $all_units = AM->retrieve_units($myconfig, $form);
503
504   if (!$payments_only) {
505     if ($form->{id}) {
506       &reverse_invoice($dbh, $form);
507
508     } else {
509       $query = qq|SELECT nextval('glid')|;
510       ($form->{"id"}) = selectrow_query($form, $dbh, $query);
511
512       $query = qq|INSERT INTO ar (id, invnumber) VALUES (?, ?)|;
513       do_query($form, $dbh, $query, $form->{"id"}, $form->{"id"});
514
515       if (!$form->{invnumber}) {
516         $form->{invnumber} =
517           $form->update_defaults($myconfig, $form->{type} eq "credit_note" ?
518                                  "cnnumber" : "invnumber", $dbh);
519       }
520     }
521   }
522
523   my ($netamount, $invoicediff) = (0, 0);
524   my ($amount, $linetotal, $lastincomeaccno);
525
526   my ($currencies)    = selectfirst_array_query($form, $dbh, qq|SELECT curr FROM defaults|);
527   my $defaultcurrency = (split m/:/, $currencies)[0];
528
529   if ($form->{currency} eq $defaultcurrency) {
530     $form->{exchangerate} = 1;
531   } else {
532     $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, 'buy');
533   }
534
535   $form->{exchangerate} =
536     ($exchangerate)
537     ? $exchangerate
538     : $form->parse_amount($myconfig, $form->{exchangerate});
539
540   $form->{expense_inventory} = "";
541
542   my %baseunits;
543
544   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
545   my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
546   my $price_factor;
547
548   foreach my $i (1 .. $form->{rowcount}) {
549     if ($form->{type} eq "credit_note") {
550       $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"}) * -1;
551       $form->{shipped} = 1;
552     } else {
553       $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
554     }
555     my $basefactor;
556     my $basqty;
557
558     $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
559     $form->{"marge_total_$i"} = $form->parse_amount($myconfig, $form->{"marge_total_$i"}) * 1;
560     $form->{"lastcost_$i"} = $form->{"lastcost_$i"} * 1;
561
562     if ($form->{storno}) {
563       $form->{"qty_$i"} *= -1;
564     }
565
566     if ($form->{"id_$i"}) {
567       my $item_unit;
568
569       if (defined($baseunits{$form->{"id_$i"}})) {
570         $item_unit = $baseunits{$form->{"id_$i"}};
571       } else {
572         # get item baseunit
573         $query = qq|SELECT unit FROM parts WHERE id = ?|;
574         ($item_unit) = selectrow_query($form, $dbh, $query, conv_i($form->{"id_$i"}));
575         $baseunits{$form->{"id_$i"}} = $item_unit;
576       }
577
578       if (defined($all_units->{$item_unit}->{factor})
579           && ($all_units->{$item_unit}->{factor} ne '')
580           && ($all_units->{$item_unit}->{factor} != 0)) {
581         $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
582       } else {
583         $basefactor = 1;
584       }
585       $baseqty = $form->{"qty_$i"} * $basefactor;
586
587       my ($allocated, $taxrate) = (0, 0);
588       my $taxamount;
589
590       # add tax rates
591       map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
592
593       # keep entered selling price
594       my $fxsellprice =
595         $form->parse_amount($myconfig, $form->{"sellprice_$i"});
596
597       my ($dec) = ($fxsellprice =~ /\.(\d+)/);
598       $dec = length $dec;
599       my $decimalplaces = ($dec > 2) ? $dec : 2;
600
601       # undo discount formatting
602       $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
603
604       # deduct discount
605       $form->{"sellprice_$i"} = $fxsellprice * (1 - $form->{"discount_$i"});
606
607       # round linetotal to 2 decimal places
608       $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
609       $linetotal    = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
610
611       if ($form->{taxincluded}) {
612         $taxamount = $linetotal * ($taxrate / (1 + $taxrate));
613         $form->{"sellprice_$i"} =
614           $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
615       } else {
616         $taxamount = $linetotal * $taxrate;
617       }
618
619       $netamount += $linetotal;
620
621       if ($taxamount != 0) {
622         map {
623           $form->{amount}{ $form->{id} }{$_} +=
624             $taxamount * $form->{"${_}_rate"} / $taxrate
625         } split(/ /, $form->{"taxaccounts_$i"});
626       }
627
628       # add amount to income, $form->{amount}{trans_id}{accno}
629       $amount = $form->{"sellprice_$i"} * $form->{"qty_$i"} * $form->{exchangerate} / $price_factor;
630
631       $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2) * $form->{exchangerate};
632       $linetotal = $form->round_amount($linetotal, 2);
633
634       # this is the difference from the inventory
635       $invoicediff += ($amount - $linetotal);
636
637       $form->{amount}{ $form->{id} }{ $form->{"income_accno_$i"} } +=
638         $linetotal;
639
640       $lastincomeaccno = $form->{"income_accno_$i"};
641
642       # adjust and round sellprice
643       $form->{"sellprice_$i"} =
644         $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate},
645                             $decimalplaces);
646
647       next if $payments_only;
648
649       if ($form->{"inventory_accno_$i"} || $form->{"assembly_$i"}) {
650
651         if ($form->{"assembly_$i"}) {
652           # record assembly item as allocated
653           &process_assembly($dbh, $form, $form->{"id_$i"}, $baseqty);
654
655         } else {
656           $allocated = &cogs($dbh, $form, $form->{"id_$i"}, $baseqty, $basefactor, $i);
657         }
658       }
659
660       # get pricegroup_id and save it
661       ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
662       $pricegroup_id *= 1;
663
664       # save detail record in invoice table
665       $query =
666         qq|INSERT INTO invoice (trans_id, parts_id, description, longdescription, qty,
667                                 sellprice, fxsellprice, discount, allocated, assemblyitem,
668                                 unit, deliverydate, project_id, serialnumber, pricegroup_id,
669                                 ordnumber, transdate, cusordnumber, base_qty, subtotal,
670                                 marge_percent, marge_total, lastcost,
671                                 price_factor_id, price_factor, marge_price_factor)
672            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
673                    (SELECT factor FROM price_factors WHERE id = ?), ?)|;
674
675       @values = (conv_i($form->{id}), conv_i($form->{"id_$i"}),
676                  $form->{"description_$i"}, $form->{"longdescription_$i"}, $form->{"qty_$i"},
677                  $form->{"sellprice_$i"}, $fxsellprice,
678                  $form->{"discount_$i"}, $allocated, 'f',
679                  $form->{"unit_$i"}, conv_date($form->{"reqdate_$i"}), conv_i($form->{"project_id_$i"}),
680                  $form->{"serialnumber_$i"}, conv_i($pricegroup_id),
681                  $form->{"ordnumber_$i"}, conv_date($form->{"transdate_$i"}),
682                  $form->{"cusordnumber_$i"}, $baseqty, $form->{"subtotal_$i"} ? 't' : 'f',
683                  $form->{"marge_percent_$i"}, $form->{"marge_total_$i"},
684                  $form->{"lastcost_$i"},
685                  conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
686                  conv_i($form->{"marge_price_factor_$i"}));
687       do_query($form, $dbh, $query, @values);
688
689       if ($form->{lizenzen} && $form->{"licensenumber_$i"}) {
690         $query =
691           qq|INSERT INTO licenseinvoice (trans_id, license_id)
692              VALUES ((SELECT id FROM invoice WHERE trans_id = ? ORDER BY oid DESC LIMIT 1), ?)|;
693         @values = (conv_i($form->{"id"}), conv_i($form->{"licensenumber_$i"}));
694         do_query($form, $dbh, $query, @values);
695       }
696     }
697   }
698
699   $form->{datepaid} = $form->{invdate};
700
701   # total payments, don't move we need it here
702   for my $i (1 .. $form->{paidaccounts}) {
703     if ($form->{type} eq "credit_note") {
704       $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"}) * -1;
705     } else {
706       $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
707     }
708     $form->{paid} += $form->{"paid_$i"};
709     $form->{datepaid} = $form->{"datepaid_$i"} if ($form->{"datepaid_$i"});
710   }
711
712   my ($tax, $diff) = (0, 0);
713
714   $netamount = $form->round_amount($netamount, 2);
715
716   # figure out rounding errors for total amount vs netamount + taxes
717   if ($form->{taxincluded}) {
718
719     $amount = $form->round_amount($netamount * $form->{exchangerate}, 2);
720     $diff += $amount - $netamount * $form->{exchangerate};
721     $netamount = $amount;
722
723     foreach my $item (split(/ /, $form->{taxaccounts})) {
724       $amount = $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate};
725       $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount, 2);
726       $tax += $form->{amount}{ $form->{id} }{$item};
727       $netamount -= $form->{amount}{ $form->{id} }{$item};
728     }
729
730     $invoicediff += $diff;
731     ######## this only applies to tax included
732     if ($lastincomeaccno) {
733       $form->{amount}{ $form->{id} }{$lastincomeaccno} += $invoicediff;
734     }
735
736   } else {
737     $amount    = $form->round_amount($netamount * $form->{exchangerate}, 2);
738     $diff      = $amount - $netamount * $form->{exchangerate};
739     $netamount = $amount;
740     foreach my $item (split(/ /, $form->{taxaccounts})) {
741       $form->{amount}{ $form->{id} }{$item} =
742         $form->round_amount($form->{amount}{ $form->{id} }{$item}, 2);
743       $amount =
744         $form->round_amount(
745                  $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate},
746                  2);
747       $diff +=
748         $amount - $form->{amount}{ $form->{id} }{$item} *
749         $form->{exchangerate};
750       $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount, 2);
751       $tax += $form->{amount}{ $form->{id} }{$item};
752     }
753   }
754
755   $form->{amount}{ $form->{id} }{ $form->{AR} } = $netamount + $tax;
756   $form->{paid} =
757     $form->round_amount($form->{paid} * $form->{exchangerate} + $diff, 2);
758
759   # reverse AR
760   $form->{amount}{ $form->{id} }{ $form->{AR} } *= -1;
761
762   # update exchangerate
763   if (($form->{currency} ne $defaultcurrency) && !$exchangerate) {
764     $form->update_exchangerate($dbh, $form->{currency}, $form->{invdate},
765                                $form->{exchangerate}, 0);
766   }
767
768   $project_id = conv_i($form->{"globalproject_id"});
769
770   foreach my $trans_id (keys %{ $form->{amount} }) {
771     foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
772       next unless ($form->{expense_inventory} =~ /\Q$accno\E/);
773
774       $form->{amount}{$trans_id}{$accno} = $form->round_amount($form->{amount}{$trans_id}{$accno}, 2);
775
776       if (!$payments_only && ($form->{amount}{$trans_id}{$accno} != 0)) {
777         $query =
778           qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id)
779              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
780                      (SELECT taxkey_id  FROM chart WHERE accno = ?), ?)|;
781         @values = (conv_i($trans_id), $accno, $form->{amount}{$trans_id}{$accno}, conv_date($form->{invdate}), $accno, conv_i($project_id));
782         do_query($form, $dbh, $query, @values);
783         $form->{amount}{$trans_id}{$accno} = 0;
784       }
785     }
786
787     foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
788       $form->{amount}{$trans_id}{$accno} = $form->round_amount($form->{amount}{$trans_id}{$accno}, 2);
789
790       if (!$payments_only && ($form->{amount}{$trans_id}{$accno} != 0)) {
791         $query =
792           qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id)
793              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
794                      (SELECT taxkey_id FROM chart WHERE accno = ?), ?)|;
795         @values = (conv_i($trans_id), $accno, $form->{amount}{$trans_id}{$accno}, conv_date($form->{invdate}), $accno, conv_i($project_id));
796         do_query($form, $dbh, $query, @values);
797       }
798     }
799   }
800
801   # deduct payment differences from diff
802   for my $i (1 .. $form->{paidaccounts}) {
803     if ($form->{"paid_$i"} != 0) {
804       $amount =
805         $form->round_amount($form->{"paid_$i"} * $form->{exchangerate}, 2);
806       $diff -= $amount - $form->{"paid_$i"} * $form->{exchangerate};
807     }
808   }
809
810   # record payments and offsetting AR
811   if (!$form->{storno}) {
812     for my $i (1 .. $form->{paidaccounts}) {
813
814       next if ($form->{"paid_$i"} == 0);
815
816       my ($accno) = split(/--/, $form->{"AR_paid_$i"});
817       $form->{"datepaid_$i"} = $form->{invdate}
818       unless ($form->{"datepaid_$i"});
819       $form->{datepaid} = $form->{"datepaid_$i"};
820
821       $exchangerate = 0;
822
823       if ($form->{currency} eq $defaultcurrency) {
824         $form->{"exchangerate_$i"} = 1;
825       } else {
826         $exchangerate              = $form->check_exchangerate($myconfig, $form->{currency}, $form->{"datepaid_$i"}, 'buy');
827         $form->{"exchangerate_$i"} = $exchangerate || $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
828       }
829
830       # record AR
831       $amount = $form->round_amount($form->{"paid_$i"} * $form->{exchangerate} + $diff, 2);
832
833       if ($form->{amount}{ $form->{id} }{ $form->{AR} } != 0) {
834         $query =
835         qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id)
836            VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
837                    (SELECT taxkey_id FROM chart WHERE accno = ?), ?)|;
838         @values = (conv_i($form->{"id"}), $form->{AR}, $amount, $form->{"datepaid_$i"}, $form->{AR}, $project_id);
839         do_query($form, $dbh, $query, @values);
840       }
841
842       # record payment
843       $form->{"paid_$i"} *= -1;
844
845       $query =
846       qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, source, memo, taxkey, project_id)
847          VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, ?, ?,
848                  (SELECT taxkey_id FROM chart WHERE accno = ?), ?)|;
849       @values = (conv_i($form->{"id"}), $accno, $form->{"paid_$i"}, $form->{"datepaid_$i"},
850                  $form->{"source_$i"}, $form->{"memo_$i"}, $accno, $project_id);
851       do_query($form, $dbh, $query, @values);
852
853       # exchangerate difference
854       $form->{fx}{$accno}{ $form->{"datepaid_$i"} } +=
855       $form->{"paid_$i"} * ($form->{"exchangerate_$i"} - 1) + $diff;
856
857       # gain/loss
858       $amount =
859       $form->{"paid_$i"} * $form->{exchangerate} - $form->{"paid_$i"} *
860       $form->{"exchangerate_$i"};
861       if ($amount > 0) {
862         $form->{fx}{ $form->{fxgain_accno} }{ $form->{"datepaid_$i"} } +=
863         $amount;
864       } else {
865         $form->{fx}{ $form->{fxloss_accno} }{ $form->{"datepaid_$i"} } +=
866         $amount;
867       }
868
869       $diff = 0;
870
871       # update exchange rate
872       if (($form->{currency} ne $defaultcurrency) && !$exchangerate) {
873         $form->update_exchangerate($dbh, $form->{currency},
874                                    $form->{"datepaid_$i"},
875                                    $form->{"exchangerate_$i"}, 0);
876       }
877     }
878
879   } else {                      # if (!$form->{storno})
880     $form->{marge_total} *= -1;
881   }
882
883   if ($payments_only) {
884     $query = qq|UPDATE ar SET paid = ?, datepaid = ? WHERE id = ?|;
885     do_query($form, $dbh, $query,  $form->{paid}, $form->{paid} ? conv_date($form->{datepaid}) : undef, conv_i($form->{id}));
886
887     if (!$provided_dbh) {
888       $dbh->commit();
889       $dbh->disconnect();
890     }
891
892     $main::lxdebug->leave_sub();
893     return;
894   }
895
896   # record exchange rate differences and gains/losses
897   foreach my $accno (keys %{ $form->{fx} }) {
898     foreach my $transdate (keys %{ $form->{fx}{$accno} }) {
899       if (
900           ($form->{fx}{$accno}{$transdate} =
901            $form->round_amount($form->{fx}{$accno}{$transdate}, 2)
902           ) != 0
903         ) {
904
905         $query =
906           qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, cleared, fx_transaction, taxkey, project_id)
907              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, '0', '1',
908              (SELECT taxkey_id FROM chart WHERE accno = ?), ?)|;
909         @values = (conv_i($form->{"id"}), $accno, $form->{fx}{$accno}{$transdate}, conv_date($transdate), $accno, $project_id);
910         do_query($form, $dbh, $query, @values);
911       }
912     }
913   }
914
915   $amount = $netamount + $tax;
916
917   # save AR record
918   $query = qq|UPDATE ar set
919                 invnumber   = ?, ordnumber     = ?, quonumber     = ?, cusordnumber  = ?,
920                 transdate   = ?, orddate       = ?, quodate       = ?, customer_id   = ?,
921                 amount      = ?, netamount     = ?, paid          = ?, datepaid      = ?,
922                 duedate     = ?, deliverydate  = ?, invoice       = ?, shippingpoint = ?,
923                 shipvia     = ?, terms         = ?, notes         = ?, intnotes      = ?,
924                 curr        = ?, department_id = ?, payment_id    = ?, taxincluded   = ?,
925                 type        = ?, language_id   = ?, taxzone_id    = ?, shipto_id     = ?,
926                 employee_id = ?, salesman_id   = ?, storno_id     = ?, storno        = ?,
927                 cp_id       = ?, marge_total   = ?, marge_percent = ?, 
928                 globalproject_id               = ?, delivery_customer_id             = ?,
929                 transaction_description        = ?, delivery_vendor_id               = ?
930               WHERE id = ?|;
931   @values = (          $form->{"invnumber"},           $form->{"ordnumber"},             $form->{"quonumber"},          $form->{"cusordnumber"},
932              conv_date($form->{"invdate"}),  conv_date($form->{"orddate"}),    conv_date($form->{"quodate"}),    conv_i($form->{"customer_id"}), 
933                        $amount,                        $netamount,                       $form->{"paid"},     conv_date($form->{"datepaid"}), 
934              conv_date($form->{"duedate"}),  conv_date($form->{"deliverydate"}),    '1',                                $form->{"shippingpoint"},
935                        $form->{"shipvia"},      conv_i($form->{"terms"}),                $form->{"notes"},              $form->{"intnotes"},
936                        $form->{"currency"},     conv_i($form->{"department_id"}), conv_i($form->{"payment_id"}),        $form->{"taxincluded"} ? 't' : 'f',
937                        $form->{"type"},         conv_i($form->{"language_id"}),   conv_i($form->{"taxzone_id"}), conv_i($form->{"shipto_id"}),
938                 conv_i($form->{"employee_id"}), conv_i($form->{"salesman_id"}),   conv_i($form->{storno_id}),           $form->{"storno"} ? 't' : 'f', 
939                 conv_i($form->{"cp_id"}),            1 * $form->{marge_total} ,      1 * $form->{marge_percent},
940                 conv_i($form->{"globalproject_id"}),                              conv_i($form->{"delivery_customer_id"}), 
941                        $form->{transaction_description},                          conv_i($form->{"delivery_vendor_id"}),
942                 conv_i($form->{"id"}));
943   do_query($form, $dbh, $query, @values);
944   
945   if($form->{"formname"} eq "credit_note") {
946     for my $i (1 .. $form->{rowcount}) {
947       $query = qq|UPDATE parts SET onhand = onhand - ? WHERE id = ?|;
948       @values = (conv_i($form->{"qty_$i"}), conv_i($form->{"id_$i"}));
949       do_query($form, $dbh, $query, @values);
950     }
951   }
952   
953   if ($form->{storno}) {
954     $query =
955       qq!UPDATE ar SET
956            paid = paid + amount,
957            storno = 't',
958            intnotes = ? || intnotes
959          WHERE id = ?!;
960     do_query($form, $dbh, $query, "Rechnung storniert am $form->{invdate} ", conv_i($form->{"storno_id"}));
961     do_query($form, $dbh, qq|UPDATE ar SET paid = amount WHERE id = ?|, conv_i($form->{"id"}));
962   }
963
964   # add shipto
965   $form->{name} = $form->{customer};
966   $form->{name} =~ s/--\Q$form->{customer_id}\E//;
967
968   if (!$form->{shipto_id}) {
969     $form->add_shipto($dbh, $form->{id}, "AR");
970   }
971
972   # save printed, emailed and queued
973   $form->save_status($dbh);
974
975   Common::webdav_folder($form) if ($main::webdav);
976
977   # Link this record to the records it was created from.
978   RecordLinks->create_links('dbh'        => $dbh,
979                             'mode'       => 'ids',
980                             'from_table' => 'oe',
981                             'from_ids'   => $form->{convert_from_oe_ids},
982                             'to_table'   => 'ar',
983                             'to_id'      => $form->{id},
984     );
985   delete $form->{convert_from_oe_ids};
986
987   my @convert_from_do_ids = map { $_ * 1 } grep { $_ } split m/\s+/, $form->{convert_from_do_ids};
988
989   if (scalar @convert_from_do_ids) {
990     DO->close_orders('dbh' => $dbh,
991                      'ids' => \@convert_from_do_ids);
992
993     RecordLinks->create_links('dbh'        => $dbh,
994                               'mode'       => 'ids',
995                               'from_table' => 'delivery_orders',
996                               'from_ids'   => \@convert_from_do_ids,
997                               'to_table'   => 'ar',
998                               'to_id'      => $form->{id},
999       );
1000   }
1001   delete $form->{convert_from_do_ids};
1002
1003   ARAP->close_orders_if_billed('dbh'     => $dbh,
1004                                'arap_id' => $form->{id},
1005                                'table'   => 'ar',);
1006
1007   my $rc = 1;
1008   if (!$provided_dbh) {
1009     $dbh->commit();
1010     $dbh->disconnect();
1011   }
1012
1013   $main::lxdebug->leave_sub();
1014
1015   return $rc;
1016 }
1017
1018 sub _delete_payments {
1019   $main::lxdebug->enter_sub();
1020
1021   my ($self, $form, $dbh) = @_;
1022
1023   my @delete_oids;
1024
1025   # Delete old payment entries from acc_trans.
1026   my $query =
1027     qq|SELECT oid
1028        FROM acc_trans
1029        WHERE (trans_id = ?) AND fx_transaction
1030
1031        UNION
1032
1033        SELECT at.oid
1034        FROM acc_trans at
1035        LEFT JOIN chart c ON (at.chart_id = c.id)
1036        WHERE (trans_id = ?) AND (c.link LIKE '%AR_paid%')|;
1037   push @delete_oids, selectall_array_query($form, $dbh, $query, conv_i($form->{id}), conv_i($form->{id}));
1038
1039   $query =
1040     qq|SELECT at.oid
1041        FROM acc_trans at
1042        LEFT JOIN chart c ON (at.chart_id = c.id)
1043        WHERE (trans_id = ?)
1044          AND ((c.link = 'AR') OR (c.link LIKE '%:AR') OR (c.link LIKE 'AR:%'))
1045        ORDER BY at.oid
1046        OFFSET 1|;
1047   push @delete_oids, selectall_array_query($form, $dbh, $query, conv_i($form->{id}));
1048
1049   if (@delete_oids) {
1050     $query = qq|DELETE FROM acc_trans WHERE oid IN (| . join(", ", @delete_oids) . qq|)|;
1051     do_query($form, $dbh, $query);
1052   }
1053
1054   $main::lxdebug->leave_sub();
1055 }
1056
1057 sub post_payment {
1058   $main::lxdebug->enter_sub();
1059
1060   my ($self, $myconfig, $form, $locale) = @_;
1061
1062   # connect to database, turn off autocommit
1063   my $dbh = $form->dbconnect_noauto($myconfig);
1064
1065   my (%payments, $old_form, $row, $item, $query, %keep_vars);
1066
1067   $old_form = save_form();
1068
1069   # Delete all entries in acc_trans from prior payments.
1070   $self->_delete_payments($form, $dbh);
1071
1072   # Save the new payments the user made before cleaning up $form.
1073   map { $payments{$_} = $form->{$_} } grep m/^datepaid_\d+$|^memo_\d+$|^source_\d+$|^exchangerate_\d+$|^paid_\d+$|^AR_paid_\d+$|^paidaccounts$/, keys %{ $form };
1074
1075   # Clean up $form so that old content won't tamper the results.
1076   %keep_vars = map { $_, 1 } qw(login password id);
1077   map { delete $form->{$_} unless $keep_vars{$_} } keys %{ $form };
1078
1079   # Retrieve the invoice from the database.
1080   $self->retrieve_invoice($myconfig, $form);
1081
1082   # Set up the content of $form in the way that IS::post_invoice() expects.
1083   $form->{exchangerate} = $form->format_amount($myconfig, $form->{exchangerate});
1084
1085   for $row (1 .. scalar @{ $form->{invoice_details} }) {
1086     $item = $form->{invoice_details}->[$row - 1];
1087
1088     map { $item->{$_} = $form->format_amount($myconfig, $item->{$_}) } qw(qty sellprice discount);
1089
1090     map { $form->{"${_}_${row}"} = $item->{$_} } keys %{ $item };
1091   }
1092
1093   $form->{rowcount} = scalar @{ $form->{invoice_details} };
1094
1095   delete @{$form}{qw(invoice_details paidaccounts storno paid)};
1096
1097   # Restore the payment options from the user input.
1098   map { $form->{$_} = $payments{$_} } keys %payments;
1099
1100   # Get the AR accno (which is normally done by Form::create_links()).
1101   $query =
1102     qq|SELECT c.accno
1103        FROM acc_trans at
1104        LEFT JOIN chart c ON (at.chart_id = c.id)
1105        WHERE (trans_id = ?)
1106          AND ((c.link = 'AR') OR (c.link LIKE '%:AR') OR (c.link LIKE 'AR:%'))
1107        ORDER BY at.oid
1108        LIMIT 1|;
1109
1110   ($form->{AR}) = selectfirst_array_query($form, $dbh, $query, conv_i($form->{id}));
1111
1112   # Post the new payments.
1113   $self->post_invoice($myconfig, $form, $dbh, 1);
1114
1115   restore_form($old_form);
1116
1117   my $rc = $dbh->commit();
1118   $dbh->disconnect();
1119
1120   $main::lxdebug->leave_sub();
1121
1122   return $rc;
1123 }
1124
1125 sub process_assembly {
1126   $main::lxdebug->enter_sub();
1127
1128   my ($dbh, $form, $id, $totalqty) = @_;
1129
1130   my $query =
1131     qq|SELECT a.parts_id, a.qty, p.assembly, p.partnumber, p.description, p.unit,
1132          p.inventory_accno_id, p.income_accno_id, p.expense_accno_id
1133        FROM assembly a
1134        JOIN parts p ON (a.parts_id = p.id)
1135        WHERE (a.id = ?)|;
1136   my $sth = prepare_execute_query($form, $dbh, $query, conv_i($id));
1137
1138   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1139
1140     my $allocated = 0;
1141
1142     $ref->{inventory_accno_id} *= 1;
1143     $ref->{expense_accno_id}   *= 1;
1144
1145     # multiply by number of assemblies
1146     $ref->{qty} *= $totalqty;
1147
1148     if ($ref->{assembly}) {
1149       &process_assembly($dbh, $form, $ref->{parts_id}, $ref->{qty});
1150       next;
1151     } else {
1152       if ($ref->{inventory_accno_id}) {
1153         $allocated = &cogs($dbh, $form, $ref->{parts_id}, $ref->{qty});
1154       }
1155     }
1156
1157     # save detail record for individual assembly item in invoice table
1158     $query =
1159       qq|INSERT INTO invoice (trans_id, description, parts_id, qty, sellprice, fxsellprice, allocated, assemblyitem, unit)
1160          VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)|;
1161     @values = (conv_i($form->{id}), $ref->{description}, conv_i($ref->{parts_id}), $ref->{qty}, 0, 0, $allocated, 't', $ref->{unit});
1162     do_query($form, $dbh, $query, @values);
1163
1164   }
1165
1166   $sth->finish;
1167
1168   $main::lxdebug->leave_sub();
1169 }
1170
1171 sub cogs {
1172   $main::lxdebug->enter_sub();
1173
1174   my ($dbh, $form, $id, $totalqty, $basefactor, $row) = @_;
1175   $form->{taxzone_id} *=1;
1176   my $transdate  = $form->{invdate} ? $dbh->quote($form->{invdate}) : "current_date";
1177   my $taxzone_id = $form->{"taxzone_id"} * 1;
1178   my $query =
1179     qq|SELECT i.id, i.trans_id, i.base_qty, i.allocated, i.sellprice,
1180          c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from AS inventory_valid,
1181          c2.accno AS    income_accno, c2.new_chart_id AS    income_new_chart, date($transdate) - c2.valid_from AS    income_valid,
1182          c3.accno AS   expense_accno, c3.new_chart_id AS   expense_new_chart, date($transdate) - c3.valid_from AS   expense_valid
1183        FROM invoice i, parts p
1184        LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id)
1185        LEFT JOIN chart c2 ON ((SELECT income_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c2.id)
1186        LEFT JOIN chart c3 ON ((select expense_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c3.id)
1187        WHERE (i.parts_id = p.id)
1188          AND (i.parts_id = ?)
1189          AND ((i.base_qty + i.allocated) < 0)
1190        ORDER BY trans_id|;
1191   my $sth = prepare_execute_query($form, $dbh, $query, conv_i($id));
1192
1193   my $allocated = 0;
1194   my $qty;
1195
1196   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1197     if (($qty = (($ref->{base_qty} * -1) - $ref->{allocated})) > $totalqty) {
1198       $qty = $totalqty;
1199     }
1200
1201     $form->update_balance($dbh, "invoice", "allocated", qq|id = $ref->{id}|, $qty);
1202
1203     # total expenses and inventory
1204     # sellprice is the cost of the item
1205     $linetotal = $form->round_amount(($ref->{sellprice} * $qty) / $basefactor, 2);
1206
1207     if (!$main::eur) {
1208       $ref->{expense_accno} = ($form->{"expense_accno_$row"}) ? $form->{"expense_accno_$row"} : $ref->{expense_accno};
1209       # add to expense
1210       $form->{amount}{ $form->{id} }{ $ref->{expense_accno} } += -$linetotal;
1211       $form->{expense_inventory} .= " " . $ref->{expense_accno};
1212       $ref->{inventory_accno} = ($form->{"inventory_accno_$row"}) ? $form->{"inventory_accno_$row"} : $ref->{inventory_accno};
1213       # deduct inventory
1214       $form->{amount}{ $form->{id} }{ $ref->{inventory_accno} } -= -$linetotal;
1215       $form->{expense_inventory} .= " " . $ref->{inventory_accno};
1216     }
1217
1218     # add allocated
1219     $allocated -= $qty;
1220
1221     last if (($totalqty -= $qty) <= 0);
1222   }
1223
1224   $sth->finish;
1225
1226   $main::lxdebug->leave_sub();
1227
1228   return $allocated;
1229 }
1230
1231 sub reverse_invoice {
1232   $main::lxdebug->enter_sub();
1233
1234   my ($dbh, $form) = @_;
1235
1236   # reverse inventory items
1237   my $query =
1238     qq|SELECT i.id, i.parts_id, i.qty, i.assemblyitem, p.assembly, p.inventory_accno_id
1239        FROM invoice i
1240        JOIN parts p ON (i.parts_id = p.id)
1241        WHERE i.trans_id = ?|;
1242   my $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{"id"}));
1243
1244   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1245
1246     if ($ref->{inventory_accno_id}) {
1247       # de-allocated purchases
1248       $query =
1249         qq|SELECT i.id, i.trans_id, i.allocated
1250            FROM invoice i
1251            WHERE (i.parts_id = ?) AND (i.allocated > 0)
1252            ORDER BY i.trans_id DESC|;
1253       my $sth2 = prepare_execute_query($form, $dbh, $query, conv_i($ref->{"parts_id"}));
1254
1255       while (my $inhref = $sth2->fetchrow_hashref(NAME_lc)) {
1256         $qty = $ref->{qty};
1257         if (($ref->{qty} - $inhref->{allocated}) > 0) {
1258           $qty = $inhref->{allocated};
1259         }
1260
1261         # update invoice
1262         $form->update_balance($dbh, "invoice", "allocated", qq|id = $inhref->{id}|, $qty * -1);
1263
1264         last if (($ref->{qty} -= $qty) <= 0);
1265       }
1266       $sth2->finish;
1267     }
1268   }
1269
1270   $sth->finish;
1271
1272   # delete acc_trans
1273   @values = (conv_i($form->{id}));
1274   do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE trans_id = ?|, @values);
1275   do_query($form, $dbh, qq|DELETE FROM invoice WHERE trans_id = ?|, @values);
1276
1277   if ($form->{lizenzen}) {
1278     $query =
1279       qq|DELETE FROM licenseinvoice
1280          WHERE trans_id in (SELECT id FROM invoice WHERE trans_id = ?)|;
1281     do_query($form, $dbh, $query, @values);
1282   }
1283
1284   do_query($form, $dbh, qq|DELETE FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|, @values);
1285
1286   $main::lxdebug->leave_sub();
1287 }
1288
1289 sub delete_invoice {
1290   $main::lxdebug->enter_sub();
1291
1292   my ($self, $myconfig, $form, $spool) = @_;
1293
1294   # connect to database
1295   my $dbh = $form->dbconnect_noauto($myconfig);
1296
1297   &reverse_invoice($dbh, $form);
1298
1299   my @values = (conv_i($form->{id}));
1300
1301   # delete AR record
1302   do_query($form, $dbh, qq|DELETE FROM ar WHERE id = ?|, @values);
1303
1304   # delete spool files
1305   my @spoolfiles = selectall_array_query($form, $dbh, qq|SELECT spoolfile FROM status WHERE trans_id = ?|, @values);
1306
1307   # delete status entries
1308   do_query($form, $dbh, qq|DELETE FROM status WHERE trans_id = ?|, @values);
1309
1310   my $rc = $dbh->commit;
1311   $dbh->disconnect;
1312
1313   if ($rc) {
1314     map { unlink "$spool/$_" if -f "$spool/$_"; } @{ $spoolfiles };
1315   }
1316
1317   $main::lxdebug->leave_sub();
1318
1319   return $rc;
1320 }
1321
1322 sub retrieve_invoice {
1323   $main::lxdebug->enter_sub();
1324
1325   my ($self, $myconfig, $form) = @_;
1326
1327   # connect to database
1328   my $dbh = $form->dbconnect_noauto($myconfig);
1329
1330   my ($sth, $ref, $query);
1331
1332   my $query_transdate = ", current_date AS invdate" if !$form->{id};
1333
1334   $query =
1335     qq|SELECT
1336          (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
1337          (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id)    AS income_accno,
1338          (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id)   AS expense_accno,
1339          (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id)    AS fxgain_accno,
1340          (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id)    AS fxloss_accno,
1341          d.curr AS currencies
1342          ${query_transdate}
1343        FROM defaults d|;
1344
1345   $ref = selectfirst_hashref_query($form, $dbh, $query);
1346   map { $form->{$_} = $ref->{$_} } keys %{ $ref };
1347
1348   if ($form->{id}) {
1349     my $id = conv_i($form->{id});
1350
1351     # retrieve invoice
1352     $query =
1353       qq|SELECT
1354            a.invnumber, a.ordnumber, a.quonumber, a.cusordnumber,
1355            a.orddate, a.quodate, a.globalproject_id,
1356            a.transdate AS invdate, a.deliverydate, a.paid, a.storno, a.gldate,
1357            a.shippingpoint, a.shipvia, a.terms, a.notes, a.intnotes, a.taxzone_id,
1358            a.duedate, a.taxincluded, a.curr AS currency, a.shipto_id, a.cp_id,
1359            a.employee_id, a.salesman_id, a.payment_id,
1360            a.language_id, a.delivery_customer_id, a.delivery_vendor_id, a.type,
1361            a.transaction_description,
1362            a.marge_total, a.marge_percent,
1363            e.name AS employee
1364          FROM ar a
1365          LEFT JOIN employee e ON (e.id = a.employee_id)
1366          WHERE a.id = ?|;
1367     $ref = selectfirst_hashref_query($form, $dbh, $query, $id);
1368     map { $form->{$_} = $ref->{$_} } keys %{ $ref };
1369
1370
1371     $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate}, "buy");
1372
1373     # get shipto
1374     $query = qq|SELECT * FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|;
1375     $ref = selectfirst_hashref_query($form, $dbh, $query, $id);
1376     delete $ref->{id};
1377     map { $form->{$_} = $ref->{$_} } keys %{ $ref };
1378
1379     foreach my $vc (qw(customer vendor)) {
1380       next if !$form->{"delivery_${vc}_id"};
1381       ($form->{"delivery_${vc}_string"}) = selectrow_query($form, $dbh, qq|SELECT name FROM customer WHERE id = ?|, $id);
1382     }
1383
1384     # get printed, emailed
1385     $query = qq|SELECT printed, emailed, spoolfile, formname FROM status WHERE trans_id = ?|;
1386     $sth = prepare_execute_query($form, $dbh, $query, $id);
1387
1388     while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
1389       $form->{printed} .= "$ref->{formname} " if $ref->{printed};
1390       $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
1391       $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
1392     }
1393     $sth->finish;
1394     map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
1395
1396     my $transdate = $form->{deliverydate} ? $dbh->quote($form->{deliverydate})
1397                   : $form->{invdate}      ? $dbh->quote($form->{invdate})
1398                   :                         "current_date";
1399      
1400
1401     my $taxzone_id = $form->{taxzone_id} *= 1;
1402     $taxzone_id = 0 if (0 > $taxzone_id) || (3 < $taxzone_id);
1403
1404     # retrieve individual items
1405     $query =
1406       qq|SELECT
1407            c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from AS inventory_valid,
1408            c2.accno AS income_accno,    c2.new_chart_id AS income_new_chart,    date($transdate) - c2.valid_from as income_valid,
1409            c3.accno AS expense_accno,   c3.new_chart_id AS expense_new_chart,   date($transdate) - c3.valid_from AS expense_valid,
1410
1411            i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.discount, i.parts_id AS id, i.unit, i.deliverydate AS reqdate,
1412            i.project_id, i.serialnumber, i.id AS invoice_pos, i.pricegroup_id, i.ordnumber, i.transdate, i.cusordnumber, i.subtotal, i.lastcost,
1413            i.price_factor_id, i.price_factor, i.marge_price_factor,
1414            p.partnumber, p.assembly, p.bin, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel,
1415            pr.projectnumber, pg.partsgroup, prg.pricegroup
1416
1417          FROM invoice i
1418          LEFT JOIN parts p ON (i.parts_id = p.id)
1419          LEFT JOIN project pr ON (i.project_id = pr.id)
1420          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1421          LEFT JOIN pricegroup prg ON (i.pricegroup_id = prg.id)
1422
1423          LEFT JOIN chart c1 ON ((SELECT inventory_accno_id             FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id)
1424          LEFT JOIN chart c2 ON ((SELECT income_accno_id_${taxzone_id}  FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c2.id)
1425          LEFT JOIN chart c3 ON ((SELECT expense_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c3.id)
1426
1427          WHERE (i.trans_id = ?) AND NOT (i.assemblyitem = '1') ORDER BY i.id|;
1428
1429     $sth = prepare_execute_query($form, $dbh, $query, $id);
1430
1431     while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1432       map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid)) if !$ref->{"part_inventory_accno_id"};
1433       delete($ref->{"part_inventory_accno_id"});
1434
1435       foreach my $type (qw(inventory income expense)) {
1436         while ($ref->{"${type}_new_chart"} && ($ref->{"${type}_valid"} >=0)) {
1437           my $query = qq|SELECT accno, new_chart_id, date($transdate) - valid_from FROM chart WHERE id = ?|;
1438           @$ref{ map $type.$_, qw(_accno _new_chart _valid) } = selectrow_query($form, $dbh, $query, $ref->{"${type}_new_chart"});
1439         }
1440       }
1441
1442       # get tax rates and description
1443       my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1444       $query =
1445         qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber FROM tax t
1446            LEFT JOIN chart c ON (c.id = t.chart_id)
1447            WHERE t.id IN
1448              (SELECT tk.tax_id FROM taxkeys tk
1449               WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?) 
1450                 AND startdate <= date($transdate)
1451               ORDER BY startdate DESC LIMIT 1)
1452            ORDER BY c.accno|;
1453       my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
1454       $ref->{taxaccounts} = "";
1455       my $i=0;
1456       while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
1457
1458         if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1459           $i++;
1460           $ptr->{accno} = $i;
1461         }
1462         $ref->{taxaccounts} .= "$ptr->{accno} ";
1463
1464         if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
1465           $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
1466           $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1467           $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
1468           $form->{taxaccounts} .= "$ptr->{accno} ";
1469         }
1470
1471       }
1472
1473       if ($form->{lizenzen}) {
1474         $query = qq|SELECT l.licensenumber, l.id AS licenseid FROM license l, licenseinvoice li WHERE l.id = li.license_id AND li.trans_id = ?|;
1475         my ($licensenumber, $licenseid) = selectrow_query($form, $dbh, $query, conv_i($ref->{invoice_pos}));
1476         $ref->{lizenzen} = "<option value=\"$licenseid\">$licensenumber</option>";
1477       }
1478
1479       $ref->{qty} *= -1 if $form->{type} eq "credit_note";
1480
1481       chop $ref->{taxaccounts};
1482       push @{ $form->{invoice_details} }, $ref;
1483       $stw->finish;
1484     }
1485     $sth->finish;
1486
1487     Common::webdav_folder($form) if ($main::webdav);
1488   }
1489
1490   my $rc = $dbh->commit;
1491   $dbh->disconnect;
1492
1493   $main::lxdebug->leave_sub();
1494
1495   return $rc;
1496 }
1497
1498 sub get_customer {
1499   $main::lxdebug->enter_sub();
1500
1501   my ($self, $myconfig, $form) = @_;
1502
1503   # connect to database
1504   my $dbh = $form->dbconnect($myconfig);
1505
1506   my $dateformat = $myconfig->{dateformat};
1507   $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
1508
1509   my (@values, $duedate, $ref, $query);
1510
1511   if ($form->{invdate}) {
1512     $duedate = "to_date(?, '$dateformat')";
1513     push @values, $form->{invdate};
1514   } else {
1515     $duedate = "current_date";
1516   }
1517
1518   my $cid = conv_i($form->{customer_id});
1519   my $payment_id;
1520
1521   if ($form->{payment_id}) {
1522     $payment_id = "(pt.id = ?) OR";
1523     push @values, conv_i($form->{payment_id});
1524   }
1525
1526   # get customer
1527   $query =
1528     qq|SELECT
1529          c.name AS customer, c.discount, c.creditlimit, c.terms,
1530          c.email, c.cc, c.bcc, c.language_id, c.payment_id,
1531          c.street, c.zipcode, c.city, c.country,
1532          c.notes AS intnotes, c.klass as customer_klass, c.taxzone_id, c.salesman_id,
1533          $duedate + COALESCE(pt.terms_netto, 0) AS duedate,
1534          b.discount AS tradediscount, b.description AS business
1535        FROM customer c
1536        LEFT JOIN business b ON (b.id = c.business_id)
1537        LEFT JOIN payment_terms pt ON ($payment_id (c.payment_id = pt.id))
1538        WHERE c.id = ?|;
1539   push @values, $cid;
1540   $ref = selectfirst_hashref_query($form, $dbh, $query, @values);
1541   map { $form->{$_} = $ref->{$_} } keys %$ref;
1542
1543   $query =
1544     qq|SELECT sum(amount - paid) AS dunning_amount
1545        FROM ar
1546        WHERE (paid < amount)
1547          AND (customer_id = ?)
1548          AND (dunning_config_id IS NOT NULL)|;
1549   $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1550   map { $form->{$_} = $ref->{$_} } keys %$ref;
1551
1552   $query =
1553     qq|SELECT dnn.dunning_description AS max_dunning_level
1554        FROM dunning_config dnn
1555        WHERE id IN (SELECT dunning_config_id
1556                     FROM ar
1557                     WHERE (paid < amount) AND (customer_id = ?) AND (dunning_config_id IS NOT NULL))
1558        ORDER BY dunning_level DESC LIMIT 1|;
1559   $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1560   map { $form->{$_} = $ref->{$_} } keys %$ref;
1561
1562   $form->{creditremaining} = $form->{creditlimit};
1563   $query = qq|SELECT SUM(amount - paid) FROM ar WHERE customer_id = ?|;
1564   my ($value) = selectrow_query($form, $dbh, $query, $cid);
1565   $form->{creditremaining} -= $value;
1566
1567   $query =
1568     qq|SELECT o.amount,
1569          (SELECT e.buy FROM exchangerate e
1570           WHERE e.curr = o.curr
1571             AND e.transdate = o.transdate)
1572        FROM oe o
1573        WHERE o.customer_id = ?
1574          AND o.quotation = '0'
1575          AND o.closed = '0'|;
1576   $sth = prepare_execute_query($form, $dbh, $query, $cid);
1577
1578   while (my ($amount, $exch) = $sth->fetchrow_array) {
1579     $exch = 1 unless $exch;
1580     $form->{creditremaining} -= $amount * $exch;
1581   }
1582   $sth->finish;
1583
1584   # get shipto if we did not converted an order or invoice
1585   if (!$form->{shipto}) {
1586     map { delete $form->{$_} }
1587       qw(shiptoname shiptodepartment_1 shiptodepartment_2
1588          shiptostreet shiptozipcode shiptocity shiptocountry
1589          shiptocontact shiptophone shiptofax shiptoemail);
1590
1591     $query = qq|SELECT * FROM shipto WHERE trans_id = ? AND module = 'CT'|;
1592     $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1593     delete $ref->{id};
1594     map { $form->{$_} = $ref->{$_} } keys %$ref;
1595   }
1596
1597   # setup last accounts used for this customer
1598   if (!$form->{id} && $form->{type} !~ /_(order|quotation)/) {
1599     $query =
1600       qq|SELECT c.id, c.accno, c.description, c.link, c.category
1601          FROM chart c
1602          JOIN acc_trans ac ON (ac.chart_id = c.id)
1603          JOIN ar a ON (a.id = ac.trans_id)
1604          WHERE a.customer_id = ?
1605            AND NOT (c.link LIKE '%_tax%' OR c.link LIKE '%_paid%')
1606            AND a.id IN (SELECT max(a2.id) FROM ar a2 WHERE a2.customer_id = ?)|;
1607     $sth = prepare_execute_query($form, $dbh, $query, $cid, $cid);
1608
1609     my $i = 0;
1610     while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
1611       if ($ref->{category} eq 'I') {
1612         $i++;
1613         $form->{"AR_amount_$i"} = "$ref->{accno}--$ref->{description}";
1614
1615         if ($form->{initial_transdate}) {
1616           my $tax_query =
1617             qq|SELECT tk.tax_id, t.rate
1618                FROM taxkeys tk
1619                LEFT JOIN tax t ON tk.tax_id = t.id
1620                WHERE (tk.chart_id = ?) AND (startdate <= date(?))
1621                ORDER BY tk.startdate DESC
1622                LIMIT 1|;
1623           my ($tax_id, $rate) =
1624             selectrow_query($form, $dbh, $tax_query, $ref->{id},
1625                             $form->{initial_transdate});
1626           $form->{"taxchart_$i"} = "${tax_id}--${rate}";
1627         }
1628       }
1629       if ($ref->{category} eq 'A') {
1630         $form->{ARselected} = $form->{AR_1} = $ref->{accno};
1631       }
1632     }
1633     $sth->finish;
1634     $form->{rowcount} = $i if ($i && !$form->{type});
1635   }
1636
1637   $dbh->disconnect;
1638
1639   $main::lxdebug->leave_sub();
1640 }
1641
1642 sub retrieve_item {
1643   $main::lxdebug->enter_sub();
1644
1645   my ($self, $myconfig, $form) = @_;
1646
1647   # connect to database
1648   my $dbh = $form->dbconnect($myconfig);
1649
1650   my $i = $form->{rowcount};
1651
1652   my $where = qq|NOT p.obsolete = '1'|;
1653   my @values;
1654
1655   foreach my $column (qw(p.partnumber p.description pgpartsgroup)) {
1656     my ($table, $field) = split m/\./, $column;
1657     next if !$form->{"${field}_${i}"};
1658     $where .= qq| AND lower(${column}) ILIKE ?|;
1659     push @values, '%' . $form->{"${field}_${i}"} . '%';
1660   }
1661
1662   if ($form->{"description_$i"}) {
1663     $where .= qq| ORDER BY p.description|;
1664   } else {
1665     $where .= qq| ORDER BY p.partnumber|;
1666   }
1667
1668   my $transdate;
1669   if ($form->{type} eq "invoice") {
1670     $transdate =
1671       $form->{deliverydate} ? $dbh->quote($form->{deliverydate}) :
1672       $form->{invdate}      ? $dbh->quote($form->{invdate}) :
1673                               "current_date";
1674   } else {
1675     $transdate =
1676       $form->{transdate}    ? $dbh->quote($form->{transdate}) :
1677                               "current_date";
1678   }
1679
1680   my $taxzone_id = $form->{taxzone_id} * 1;
1681   $taxzone_id = 0 if (0 > $taxzone_id) || (3 < $taxzone_id);
1682
1683   my $query =
1684     qq|SELECT
1685          p.id, p.partnumber, p.description, p.sellprice,
1686          p.listprice, p.inventory_accno_id, p.lastcost,
1687
1688          c1.accno AS inventory_accno,
1689          c1.new_chart_id AS inventory_new_chart,
1690          date($transdate) - c1.valid_from AS inventory_valid,
1691
1692          c2.accno AS income_accno,
1693          c2.new_chart_id AS income_new_chart,
1694          date($transdate)  - c2.valid_from AS income_valid,
1695
1696          c3.accno AS expense_accno,
1697          c3.new_chart_id AS expense_new_chart,
1698          date($transdate) - c3.valid_from AS expense_valid,
1699
1700          p.unit, p.assembly, p.bin, p.onhand,
1701          p.notes AS partnotes, p.notes AS longdescription,
1702          p.not_discountable, p.formel, p.payment_id AS part_payment_id,
1703          p.price_factor_id,
1704
1705          pfac.factor AS price_factor,
1706
1707          pg.partsgroup
1708
1709        FROM parts p
1710        LEFT JOIN chart c1 ON
1711          ((SELECT inventory_accno_id
1712            FROM buchungsgruppen
1713            WHERE id = p.buchungsgruppen_id) = c1.id)
1714        LEFT JOIN chart c2 ON
1715          ((SELECT income_accno_id_${taxzone_id}
1716            FROM buchungsgruppen
1717            WHERE id = p.buchungsgruppen_id) = c2.id)
1718        LEFT JOIN chart c3 ON
1719          ((SELECT expense_accno_id_${taxzone_id}
1720            FROM buchungsgruppen
1721            WHERE id = p.buchungsgruppen_id) = c3.id)
1722        LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
1723        LEFT JOIN price_factors pfac ON (pfac.id = p.price_factor_id)
1724        WHERE $where|;
1725   my $sth = prepare_execute_query($form, $dbh, $query, @values);
1726
1727   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1728
1729     # In der Buchungsgruppe ist immer ein Bestandskonto verknuepft, auch wenn
1730     # es sich um eine Dienstleistung handelt. Bei Dienstleistungen muss das
1731     # Buchungskonto also aus dem Ergebnis rausgenommen werden.
1732     if (!$ref->{inventory_accno_id}) {
1733       map({ delete($ref->{"inventory_${_}"}); } qw(accno new_chart valid));
1734     }
1735     delete($ref->{inventory_accno_id});
1736
1737     foreach my $type (qw(inventory income expense)) {
1738       while ($ref->{"${type}_new_chart"} && ($ref->{"${type}_valid"} >=0)) {
1739         my $query =
1740           qq|SELECT accno, new_chart_id, date($transdate) - valid_from
1741              FROM chart
1742              WHERE id = ?|;
1743         ($ref->{"${type}_accno"},
1744          $ref->{"${type}_new_chart"},
1745          $ref->{"${type}_valid"})
1746           = selectrow_query($form, $dbh, $query, $ref->{"${type}_new_chart"});
1747       }
1748     }
1749
1750     if ($form->{payment_id} eq "") {
1751       $form->{payment_id} = $form->{part_payment_id};
1752     }
1753
1754     # get tax rates and description
1755     $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1756     $query =
1757       qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber
1758          FROM tax t
1759          LEFT JOIN chart c ON (c.id = t.chart_id)
1760          WHERE t.id in
1761            (SELECT tk.tax_id
1762             FROM taxkeys tk
1763             WHERE tk.chart_id = (SELECT id from chart WHERE accno = ?)
1764               AND startdate <= ?
1765             ORDER BY startdate DESC
1766             LIMIT 1)
1767          ORDER BY c.accno|;
1768     @values = ($accno_id, $transdate eq "current_date" ? "now" : $transdate);
1769     $stw = $dbh->prepare($query);
1770     $stw->execute(@values) || $form->dberror($query);
1771
1772     $ref->{taxaccounts} = "";
1773     my $i = 0;
1774     while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
1775
1776       #    if ($customertax{$ref->{accno}}) {
1777       if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1778         $i++;
1779         $ptr->{accno} = $i;
1780       }
1781       $ref->{taxaccounts} .= "$ptr->{accno} ";
1782
1783       if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
1784         $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
1785         $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1786         $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
1787         $form->{taxaccounts} .= "$ptr->{accno} ";
1788       }
1789
1790     }
1791
1792     $stw->finish;
1793     chop $ref->{taxaccounts};
1794     if ($form->{language_id}) {
1795       $query =
1796         qq|SELECT tr.translation, tr.longdescription
1797            FROM translation tr
1798            WHERE tr.language_id = ? AND tr.parts_id = ?|;
1799       @values = (conv_i($form->{language_id}), conv_i($ref->{id}));
1800       my ($translation, $longdescription) = selectrow_query($form, $dbh, $query, @values);
1801       if ($translation ne "") {
1802         $ref->{description} = $translation;
1803         $ref->{longdescription} = $longdescription;
1804
1805       } else {
1806         $query =
1807           qq|SELECT tr.translation, tr.longdescription
1808              FROM translation tr
1809              WHERE tr.language_id IN
1810                (SELECT id
1811                 FROM language
1812                 WHERE article_code = (SELECT article_code FROM language WHERE id = ?))
1813                AND tr.parts_id = ?
1814              LIMIT 1|;
1815         @values = (conv_i($form->{language_id}), conv_i($ref->{id}));
1816         my ($translation, $longdescription) = selectrow_query($form, $dbh, $query, @values);
1817         if ($translation ne "") {
1818           $ref->{description} = $translation;
1819           $ref->{longdescription} = $longdescription;
1820         }
1821       }
1822     }
1823
1824     $ref->{onhand} *= 1;
1825
1826     push @{ $form->{item_list} }, $ref;
1827
1828     if ($form->{lizenzen}) {
1829       if ($ref->{inventory_accno} > 0) {
1830         $query =
1831           qq|SELECT l.*
1832              FROM license l
1833              WHERE l.parts_id = ? AND NOT l.id IN (SELECT li.license_id FROM licenseinvoice li)|;
1834         my $stw = prepare_execute_query($form, $dbh, $query, conv_i($ref->{id}));
1835         while (my $ptr = $stw->fetchrow_hashref(NAME_lc)) {
1836           push @{ $form->{LIZENZEN}{ $ref->{id} } }, $ptr;
1837         }
1838         $stw->finish;
1839       }
1840     }
1841   }
1842   $sth->finish;
1843   $dbh->disconnect;
1844
1845   $main::lxdebug->leave_sub();
1846 }
1847
1848 ##########################
1849 # get pricegroups from database
1850 # build up selected pricegroup
1851 # if an exchange rate - change price
1852 # for each part
1853 #
1854 sub get_pricegroups_for_parts {
1855
1856   $main::lxdebug->enter_sub();
1857
1858   my ($self, $myconfig, $form) = @_;
1859
1860   my $dbh = $form->dbconnect($myconfig);
1861
1862   $form->{"PRICES"} = {};
1863
1864   my $i  = 1;
1865   my $id = 0;
1866   my $all_units = AM->retrieve_units($myconfig, $form);
1867   while (($form->{"id_$i"}) or ($form->{"new_id_$i"})) {
1868     $form->{"PRICES"}{$i} = [];
1869
1870     $id = $form->{"id_$i"};
1871
1872     if (!($form->{"id_$i"}) and $form->{"new_id_$i"}) {
1873
1874       $id = $form->{"new_id_$i"};
1875     }
1876
1877     ($price, $selectedpricegroup_id) = split(/--/,
1878       $form->{"sellprice_pg_$i"});
1879
1880     $pricegroup_old = $form->{"pricegroup_old_$i"};
1881     $form->{"new_pricegroup_$i"} = $selectedpricegroup_id;
1882     $form->{"old_pricegroup_$i"} = $pricegroup_old;
1883
1884     $price_new = $form->{"price_new_$i"};
1885     $price_old = $form->{"price_old_$i"};
1886
1887     if (!$form->{"unit_old_$i"}) {
1888       # Neue Ware aus der Datenbank. In diesem Fall ist unit_$i die
1889       # Einheit, wie sie in den Stammdaten hinterlegt wurde.
1890       # Es sollte also angenommen werden, dass diese ausgewaehlt war.
1891       $form->{"unit_old_$i"} = $form->{"unit_$i"};
1892     }
1893
1894     # Die zuletzt ausgewaehlte mit der aktuell ausgewaehlten Einheit
1895     # vergleichen und bei Unterschied den Preis entsprechend umrechnen.
1896     $form->{"selected_unit_$i"} = $form->{"unit_$i"} unless ($form->{"selected_unit_$i"});
1897
1898     if (!$all_units->{$form->{"selected_unit_$i"}} ||
1899         ($all_units->{$form->{"selected_unit_$i"}}->{"base_unit"} ne
1900          $all_units->{$form->{"unit_old_$i"}}->{"base_unit"})) {
1901       # Die ausgewaehlte Einheit ist fuer diesen Artikel nicht gueltig
1902       # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber
1903       # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen.
1904       $form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"};
1905     }
1906
1907     my $basefactor = 1;
1908
1909     if ($form->{"unit_old_$i"} ne $form->{"selected_unit_$i"}) {
1910       if (defined($all_units->{$form->{"unit_old_$i"}}->{"factor"}) &&
1911           $all_units->{$form->{"unit_old_$i"}}->{"factor"}) {
1912         $basefactor = $all_units->{$form->{"selected_unit_$i"}}->{"factor"} /
1913           $all_units->{$form->{"unit_old_$i"}}->{"factor"};
1914       }
1915     }
1916
1917     if (!$form->{"basefactor_$i"}) {
1918       $form->{"basefactor_$i"} = 1;
1919     }
1920
1921     $query =
1922       qq|SELECT
1923            pricegroup_id,
1924            (SELECT p.sellprice FROM parts p WHERE p.id = ?) AS default_sellprice,
1925            (SELECT pg.pricegroup FROM pricegroup pg WHERE id = pricegroup_id) AS pricegroup,
1926            price,
1927            '' AS selected
1928           FROM prices
1929           WHERE parts_id = ?
1930
1931           UNION
1932
1933           SELECT
1934             0 as pricegroup_id,
1935             (SELECT sellprice FROM parts WHERE id = ?) AS default_sellprice,
1936             '' AS pricegroup,
1937             (SELECT DISTINCT sellprice FROM parts where id = ?) AS price,
1938             'selected' AS selected
1939           FROM prices
1940
1941           ORDER BY pricegroup|;
1942     @values = (conv_i($id), conv_i($id), conv_i($id), conv_i($id));
1943     my $pkq = prepare_execute_query($form, $dbh, $query, @values);
1944
1945     while ($pkr = $pkq->fetchrow_hashref(NAME_lc)) {
1946       $pkr->{id}       = $id;
1947       $pkr->{selected} = '';
1948
1949       # if there is an exchange rate change price
1950       if (($form->{exchangerate} * 1) != 0) {
1951
1952         $pkr->{price} /= $form->{exchangerate};
1953       }
1954
1955       $pkr->{price} *= $form->{"basefactor_$i"};
1956
1957       $pkr->{price} *= $basefactor;
1958
1959       $pkr->{price} = $form->format_amount($myconfig, $pkr->{price}, 5);
1960
1961       if ($selectedpricegroup_id eq undef) {
1962         if ($pkr->{pricegroup_id} eq $form->{customer_klass}) {
1963
1964           $pkr->{selected}  = ' selected';
1965
1966           # no customer pricesgroup set
1967           if ($pkr->{price} == $pkr->{default_sellprice}) {
1968
1969             $pkr->{price} = $form->{"sellprice_$i"};
1970
1971           } else {
1972
1973             $form->{"sellprice_$i"} = $pkr->{price};
1974           }
1975
1976         } elsif ($pkr->{price} == $pkr->{default_sellprice}) {
1977           $pkr->{price}    = $form->{"sellprice_$i"};
1978           $pkr->{selected} = ' selected';
1979         }
1980       }
1981
1982       if ($selectedpricegroup_id or $selectedpricegroup_id == 0) {
1983         if ($selectedpricegroup_id ne $pricegroup_old) {
1984           if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
1985             $pkr->{selected}  = ' selected';
1986           }
1987         } elsif (($price_new != $form->{"sellprice_$i"}) and ($price_new ne 0)) {
1988           if ($pkr->{pricegroup_id} == 0) {
1989             $pkr->{price}     = $form->{"sellprice_$i"};
1990             $pkr->{selected}  = ' selected';
1991           }
1992         } elsif ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
1993           $pkr->{selected}  = ' selected';
1994           if (    ($pkr->{pricegroup_id} == 0)
1995               and ($pkr->{price} == $form->{"sellprice_$i"})) {
1996             # $pkr->{price}                         = $form->{"sellprice_$i"};
1997           } else {
1998             $pkr->{price} = $form->{"sellprice_$i"};
1999           }
2000         }
2001       }
2002       push @{ $form->{PRICES}{$i} }, $pkr;
2003
2004     }
2005     $form->{"basefactor_$i"} *= $basefactor;
2006
2007     $i++;
2008
2009     $pkq->finish;
2010   }
2011
2012   $dbh->disconnect;
2013
2014   $main::lxdebug->leave_sub();
2015 }
2016
2017 sub has_storno {
2018   $main::lxdebug->enter_sub();
2019
2020   my ($self, $myconfig, $form, $table) = @_;
2021
2022   $main::lxdebug->leave_sub() and return 0 unless ($form->{id});
2023
2024   # make sure there's no funny stuff in $table
2025   # ToDO: die when this happens and throw an error
2026   $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
2027
2028   my $dbh = $form->dbconnect($myconfig);
2029
2030   my $query = qq|SELECT storno FROM $table WHERE storno_id = ?|;
2031   my ($result) = selectrow_query($form, $dbh, $query, $form->{id});
2032
2033   $dbh->disconnect();
2034
2035   $main::lxdebug->leave_sub();
2036
2037   return $result;
2038 }
2039
2040 sub is_storno {
2041   $main::lxdebug->enter_sub();
2042
2043   my ($self, $myconfig, $form, $table, $id) = @_;
2044
2045   $main::lxdebug->leave_sub() and return 0 unless ($id);
2046
2047   # make sure there's no funny stuff in $table
2048   # ToDO: die when this happens and throw an error
2049   $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
2050
2051   my $dbh = $form->dbconnect($myconfig);
2052
2053   my $query = qq|SELECT storno FROM $table WHERE id = ?|;
2054   my ($result) = selectrow_query($form, $dbh, $query, $id);
2055
2056   $dbh->disconnect();
2057
2058   $main::lxdebug->leave_sub();
2059
2060   return $result;
2061 }
2062
2063 1;