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