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