8e7dcc9f156464d6bd6fe433c6f127baa63d2b6f
[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->{invdate}, '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_absolut_$i"} = $form->parse_amount($myconfig, $form->{"marge_absolut_$i"}) * 1;
595     $form->{"lastcost_$i"} = $form->parse_amount($myconfig, $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_absolut_$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, i.price_factor,
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) / ($ref->{price_factor} * ( $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->get_standard_dbh;
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
1579   $main::lxdebug->leave_sub();
1580
1581   return $rc;
1582 }
1583
1584 sub get_customer {
1585   $main::lxdebug->enter_sub();
1586
1587   my ($self, $myconfig, $form) = @_;
1588
1589   # connect to database
1590   my $dbh = $form->get_standard_dbh;
1591
1592   my $dateformat = $myconfig->{dateformat};
1593   $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
1594
1595   my (@values, $duedate, $ref, $query);
1596
1597   if ($form->{invdate}) {
1598     $duedate = "to_date(?, '$dateformat')";
1599     push @values, $form->{invdate};
1600   } else {
1601     $duedate = "current_date";
1602   }
1603
1604   my $cid = conv_i($form->{customer_id});
1605   my $payment_id;
1606
1607   if ($form->{payment_id}) {
1608     $payment_id = "(pt.id = ?) OR";
1609     push @values, conv_i($form->{payment_id});
1610   }
1611
1612   # get customer
1613   $query =
1614     qq|SELECT
1615          c.id AS customer_id, c.name AS customer, c.discount as customer_discount, c.creditlimit, c.terms,
1616          c.email, c.cc, c.bcc, c.language_id, c.payment_id,
1617          c.street, c.zipcode, c.city, c.country,
1618          c.notes AS intnotes, c.klass as customer_klass, c.taxzone_id, c.salesman_id,
1619          $duedate + COALESCE(pt.terms_netto, 0) AS duedate,
1620          b.discount AS tradediscount, b.description AS business
1621        FROM customer c
1622        LEFT JOIN business b ON (b.id = c.business_id)
1623        LEFT JOIN payment_terms pt ON ($payment_id (c.payment_id = pt.id))
1624        WHERE c.id = ?|;
1625   push @values, $cid;
1626   $ref = selectfirst_hashref_query($form, $dbh, $query, @values);
1627
1628   delete $ref->{salesman_id} if !$ref->{salesman_id};
1629
1630   map { $form->{$_} = $ref->{$_} } keys %$ref;
1631
1632   $query =
1633     qq|SELECT sum(amount - paid) AS dunning_amount
1634        FROM ar
1635        WHERE (paid < amount)
1636          AND (customer_id = ?)
1637          AND (dunning_config_id IS NOT NULL)|;
1638   $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1639   map { $form->{$_} = $ref->{$_} } keys %$ref;
1640
1641   $query =
1642     qq|SELECT dnn.dunning_description AS max_dunning_level
1643        FROM dunning_config dnn
1644        WHERE id IN (SELECT dunning_config_id
1645                     FROM ar
1646                     WHERE (paid < amount) AND (customer_id = ?) AND (dunning_config_id IS NOT NULL))
1647        ORDER BY dunning_level DESC LIMIT 1|;
1648   $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1649   map { $form->{$_} = $ref->{$_} } keys %$ref;
1650
1651   $form->{creditremaining} = $form->{creditlimit};
1652   $query = qq|SELECT SUM(amount - paid) FROM ar WHERE customer_id = ?|;
1653   my ($value) = selectrow_query($form, $dbh, $query, $cid);
1654   $form->{creditremaining} -= $value;
1655
1656   $query =
1657     qq|SELECT o.amount,
1658          (SELECT e.buy FROM exchangerate e
1659           WHERE e.curr = o.curr
1660             AND e.transdate = o.transdate)
1661        FROM oe o
1662        WHERE o.customer_id = ?
1663          AND o.quotation = '0'
1664          AND o.closed = '0'|;
1665   my $sth = prepare_execute_query($form, $dbh, $query, $cid);
1666
1667   while (my ($amount, $exch) = $sth->fetchrow_array) {
1668     $exch = 1 unless $exch;
1669     $form->{creditremaining} -= $amount * $exch;
1670   }
1671   $sth->finish;
1672
1673   # get shipto if we did not converted an order or invoice
1674   if (!$form->{shipto}) {
1675     map { delete $form->{$_} }
1676       qw(shiptoname shiptodepartment_1 shiptodepartment_2
1677          shiptostreet shiptozipcode shiptocity shiptocountry
1678          shiptocontact shiptophone shiptofax shiptoemail);
1679
1680     $query = qq|SELECT * FROM shipto WHERE trans_id = ? AND module = 'CT'|;
1681     $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1682     delete $ref->{id};
1683     map { $form->{$_} = $ref->{$_} } keys %$ref;
1684   }
1685
1686   # setup last accounts used for this customer
1687   if (!$form->{id} && $form->{type} !~ /_(order|quotation)/) {
1688     $query =
1689       qq|SELECT c.id, c.accno, c.description, c.link, c.category
1690          FROM chart c
1691          JOIN acc_trans ac ON (ac.chart_id = c.id)
1692          JOIN ar a ON (a.id = ac.trans_id)
1693          WHERE a.customer_id = ?
1694            AND NOT (c.link LIKE '%_tax%' OR c.link LIKE '%_paid%')
1695            AND a.id IN (SELECT max(a2.id) FROM ar a2 WHERE a2.customer_id = ?)|;
1696     $sth = prepare_execute_query($form, $dbh, $query, $cid, $cid);
1697
1698     my $i = 0;
1699     while ($ref = $sth->fetchrow_hashref('NAME_lc')) {
1700       if ($ref->{category} eq 'I') {
1701         $i++;
1702         $form->{"AR_amount_$i"} = "$ref->{accno}--$ref->{description}";
1703
1704         if ($form->{initial_transdate}) {
1705           my $tax_query =
1706             qq|SELECT tk.tax_id, t.rate
1707                FROM taxkeys tk
1708                LEFT JOIN tax t ON tk.tax_id = t.id
1709                WHERE (tk.chart_id = ?) AND (startdate <= date(?))
1710                ORDER BY tk.startdate DESC
1711                LIMIT 1|;
1712           my ($tax_id, $rate) =
1713             selectrow_query($form, $dbh, $tax_query, $ref->{id},
1714                             $form->{initial_transdate});
1715           $form->{"taxchart_$i"} = "${tax_id}--${rate}";
1716         }
1717       }
1718       if ($ref->{category} eq 'A') {
1719         $form->{ARselected} = $form->{AR_1} = $ref->{accno};
1720       }
1721     }
1722     $sth->finish;
1723     $form->{rowcount} = $i if ($i && !$form->{type});
1724   }
1725
1726   $main::lxdebug->leave_sub();
1727 }
1728
1729 sub retrieve_item {
1730   $main::lxdebug->enter_sub();
1731
1732   my ($self, $myconfig, $form) = @_;
1733
1734   # connect to database
1735   my $dbh = $form->dbconnect($myconfig);
1736
1737   my $i = $form->{rowcount};
1738
1739   my $where = qq|NOT p.obsolete = '1'|;
1740   my @values;
1741
1742   foreach my $column (qw(p.partnumber p.description pgpartsgroup )) {
1743     my ($table, $field) = split m/\./, $column;
1744     next if !$form->{"${field}_${i}"};
1745     $where .= qq| AND lower(${column}) ILIKE ?|;
1746     push @values, '%' . $form->{"${field}_${i}"} . '%';
1747   }
1748
1749   #Es soll auch nach EAN gesucht werden, ohne Einschränkung durch Beschreibung
1750   if ($form->{"partnumber_$i"} && !$form->{"description_$i"}) {
1751     $where .= qq| OR (NOT p.obsolete = '1' AND p.ean = ? )|;
1752     push @values, $form->{"partnumber_$i"};
1753   }
1754
1755   if ($form->{"description_$i"}) {
1756     $where .= qq| ORDER BY p.description|;
1757   } else {
1758     $where .= qq| ORDER BY p.partnumber|;
1759   }
1760
1761   my $transdate;
1762   if ($form->{type} eq "invoice") {
1763     $transdate =
1764       $form->{deliverydate} ? $dbh->quote($form->{deliverydate}) :
1765       $form->{invdate}      ? $dbh->quote($form->{invdate}) :
1766                               "current_date";
1767   } else {
1768     $transdate =
1769       $form->{transdate}    ? $dbh->quote($form->{transdate}) :
1770                               "current_date";
1771   }
1772
1773   my $taxzone_id = $form->{taxzone_id} * 1;
1774   $taxzone_id = 0 if (0 > $taxzone_id) || (3 < $taxzone_id);
1775
1776   my $query =
1777     qq|SELECT
1778          p.id, p.partnumber, p.description, p.sellprice,
1779          p.listprice, p.inventory_accno_id, p.lastcost,
1780
1781          c1.accno AS inventory_accno,
1782          c1.new_chart_id AS inventory_new_chart,
1783          date($transdate) - c1.valid_from AS inventory_valid,
1784
1785          c2.accno AS income_accno,
1786          c2.new_chart_id AS income_new_chart,
1787          date($transdate)  - c2.valid_from AS income_valid,
1788
1789          c3.accno AS expense_accno,
1790          c3.new_chart_id AS expense_new_chart,
1791          date($transdate) - c3.valid_from AS expense_valid,
1792
1793          p.unit, p.assembly, p.bin, p.onhand,
1794          p.notes AS partnotes, p.notes AS longdescription,
1795          p.not_discountable, p.formel, p.payment_id AS part_payment_id,
1796          p.price_factor_id,
1797
1798          pfac.factor AS price_factor,
1799
1800          pg.partsgroup
1801
1802        FROM parts p
1803        LEFT JOIN chart c1 ON
1804          ((SELECT inventory_accno_id
1805            FROM buchungsgruppen
1806            WHERE id = p.buchungsgruppen_id) = c1.id)
1807        LEFT JOIN chart c2 ON
1808          ((SELECT income_accno_id_${taxzone_id}
1809            FROM buchungsgruppen
1810            WHERE id = p.buchungsgruppen_id) = c2.id)
1811        LEFT JOIN chart c3 ON
1812          ((SELECT expense_accno_id_${taxzone_id}
1813            FROM buchungsgruppen
1814            WHERE id = p.buchungsgruppen_id) = c3.id)
1815        LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
1816        LEFT JOIN price_factors pfac ON (pfac.id = p.price_factor_id)
1817        WHERE $where|;
1818   my $sth = prepare_execute_query($form, $dbh, $query, @values);
1819
1820   while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
1821
1822     # In der Buchungsgruppe ist immer ein Bestandskonto verknuepft, auch wenn
1823     # es sich um eine Dienstleistung handelt. Bei Dienstleistungen muss das
1824     # Buchungskonto also aus dem Ergebnis rausgenommen werden.
1825     if (!$ref->{inventory_accno_id}) {
1826       map({ delete($ref->{"inventory_${_}"}); } qw(accno new_chart valid));
1827     }
1828     delete($ref->{inventory_accno_id});
1829
1830     foreach my $type (qw(inventory income expense)) {
1831       while ($ref->{"${type}_new_chart"} && ($ref->{"${type}_valid"} >=0)) {
1832         my $query =
1833           qq|SELECT accno, new_chart_id, date($transdate) - valid_from
1834              FROM chart
1835              WHERE id = ?|;
1836         ($ref->{"${type}_accno"},
1837          $ref->{"${type}_new_chart"},
1838          $ref->{"${type}_valid"})
1839           = selectrow_query($form, $dbh, $query, $ref->{"${type}_new_chart"});
1840       }
1841     }
1842
1843     if ($form->{payment_id} eq "") {
1844       $form->{payment_id} = $form->{part_payment_id};
1845     }
1846
1847     # get tax rates and description
1848     my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1849     $query =
1850       qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber
1851          FROM tax t
1852          LEFT JOIN chart c ON (c.id = t.chart_id)
1853          WHERE t.id in
1854            (SELECT tk.tax_id
1855             FROM taxkeys tk
1856             WHERE tk.chart_id = (SELECT id from chart WHERE accno = ?)
1857               AND startdate <= ?
1858             ORDER BY startdate DESC
1859             LIMIT 1)
1860          ORDER BY c.accno|;
1861     @values = ($accno_id, $transdate eq "current_date" ? "now" : $transdate);
1862     my $stw = $dbh->prepare($query);
1863     $stw->execute(@values) || $form->dberror($query);
1864
1865     $ref->{taxaccounts} = "";
1866     my $i = 0;
1867     while (my $ptr = $stw->fetchrow_hashref('NAME_lc')) {
1868
1869       #    if ($customertax{$ref->{accno}})
1870       if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1871         $i++;
1872         $ptr->{accno} = $i;
1873       }
1874       $ref->{taxaccounts} .= "$ptr->{accno} ";
1875
1876       if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
1877         $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
1878         $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1879         $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
1880         $form->{taxaccounts} .= "$ptr->{accno} ";
1881       }
1882
1883     }
1884
1885     $stw->finish;
1886     chop $ref->{taxaccounts};
1887     if ($form->{language_id}) {
1888       $query =
1889         qq|SELECT tr.translation, tr.longdescription
1890            FROM translation tr
1891            WHERE tr.language_id = ? AND tr.parts_id = ?|;
1892       @values = (conv_i($form->{language_id}), conv_i($ref->{id}));
1893       my ($translation, $longdescription) = selectrow_query($form, $dbh, $query, @values);
1894       if ($translation ne "") {
1895         $ref->{description} = $translation;
1896         $ref->{longdescription} = $longdescription;
1897
1898       } else {
1899         $query =
1900           qq|SELECT tr.translation, tr.longdescription
1901              FROM translation tr
1902              WHERE tr.language_id IN
1903                (SELECT id
1904                 FROM language
1905                 WHERE article_code = (SELECT article_code FROM language WHERE id = ?))
1906                AND tr.parts_id = ?
1907              LIMIT 1|;
1908         @values = (conv_i($form->{language_id}), conv_i($ref->{id}));
1909         my ($translation, $longdescription) = selectrow_query($form, $dbh, $query, @values);
1910         if ($translation ne "") {
1911           $ref->{description} = $translation;
1912           $ref->{longdescription} = $longdescription;
1913         }
1914       }
1915     }
1916
1917     $ref->{onhand} *= 1;
1918
1919     push @{ $form->{item_list} }, $ref;
1920
1921     if ($form->{lizenzen}) {
1922       if ($ref->{inventory_accno} > 0) {
1923         $query =
1924           qq|SELECT l.*
1925              FROM license l
1926              WHERE l.parts_id = ? AND NOT l.id IN (SELECT li.license_id FROM licenseinvoice li)|;
1927         my $stw = prepare_execute_query($form, $dbh, $query, conv_i($ref->{id}));
1928         while (my $ptr = $stw->fetchrow_hashref('NAME_lc')) {
1929           push @{ $form->{LIZENZEN}{ $ref->{id} } }, $ptr;
1930         }
1931         $stw->finish;
1932       }
1933     }
1934   }
1935   $sth->finish;
1936
1937   foreach my $item (@{ $form->{item_list} }) {
1938     my $custom_variables = CVar->get_custom_variables(module   => 'IC',
1939                                                       trans_id => $item->{id},
1940                                                       dbh      => $dbh,
1941                                                      );
1942
1943     map { $item->{"ic_cvar_" . $_->{name} } = $_->{value} } @{ $custom_variables };
1944   }
1945
1946   $dbh->disconnect;
1947
1948   $main::lxdebug->leave_sub();
1949 }
1950
1951 ##########################
1952 # get pricegroups from database
1953 # build up selected pricegroup
1954 # if an exchange rate - change price
1955 # for each part
1956 #
1957 sub get_pricegroups_for_parts {
1958
1959   $main::lxdebug->enter_sub();
1960
1961   my ($self, $myconfig, $form) = @_;
1962
1963   my $dbh = $form->dbconnect($myconfig);
1964
1965   $form->{"PRICES"} = {};
1966
1967   my $i  = 1;
1968   my $id = 0;
1969   my $all_units = AM->retrieve_units($myconfig, $form);
1970   while (($form->{"id_$i"}) or ($form->{"new_id_$i"})) {
1971     $form->{"PRICES"}{$i} = [];
1972
1973     $id = $form->{"id_$i"};
1974
1975     if (!($form->{"id_$i"}) and $form->{"new_id_$i"}) {
1976       $id = $form->{"new_id_$i"};
1977     }
1978
1979     my ($price, $selectedpricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
1980
1981     my $pricegroup_old = $form->{"pricegroup_old_$i"};
1982     $form->{"new_pricegroup_$i"} = $selectedpricegroup_id;
1983     $form->{"old_pricegroup_$i"} = $pricegroup_old;
1984
1985     my $price_new = $form->{"price_new_$i"};
1986     my $price_old = $form->{"price_old_$i"};
1987
1988     if (!$form->{"unit_old_$i"}) {
1989       # Neue Ware aus der Datenbank. In diesem Fall ist unit_$i die
1990       # Einheit, wie sie in den Stammdaten hinterlegt wurde.
1991       # Es sollte also angenommen werden, dass diese ausgewaehlt war.
1992       $form->{"unit_old_$i"} = $form->{"unit_$i"};
1993     }
1994
1995     # Die zuletzt ausgewaehlte mit der aktuell ausgewaehlten Einheit
1996     # vergleichen und bei Unterschied den Preis entsprechend umrechnen.
1997     $form->{"selected_unit_$i"} = $form->{"unit_$i"} unless ($form->{"selected_unit_$i"});
1998
1999     if (!$all_units->{$form->{"selected_unit_$i"}} ||
2000         ($all_units->{$form->{"selected_unit_$i"}}->{"base_unit"} ne
2001          $all_units->{$form->{"unit_old_$i"}}->{"base_unit"})) {
2002       # Die ausgewaehlte Einheit ist fuer diesen Artikel nicht gueltig
2003       # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber
2004       # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen.
2005       $form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"};
2006     }
2007
2008     my $basefactor = 1;
2009
2010     if ($form->{"unit_old_$i"} ne $form->{"selected_unit_$i"}) {
2011       if (defined($all_units->{$form->{"unit_old_$i"}}->{"factor"}) &&
2012           $all_units->{$form->{"unit_old_$i"}}->{"factor"}) {
2013         $basefactor = $all_units->{$form->{"selected_unit_$i"}}->{"factor"} /
2014           $all_units->{$form->{"unit_old_$i"}}->{"factor"};
2015       }
2016     }
2017
2018     if (!$form->{"basefactor_$i"}) {
2019       $form->{"basefactor_$i"} = 1;
2020     }
2021
2022     my $query =
2023       qq|SELECT
2024            pricegroup_id,
2025            (SELECT p.sellprice FROM parts p WHERE p.id = ?) AS default_sellprice,
2026            (SELECT pg.pricegroup FROM pricegroup pg WHERE id = pricegroup_id) AS pricegroup,
2027            price,
2028            '' AS selected
2029           FROM prices
2030           WHERE parts_id = ?
2031
2032           UNION
2033
2034           SELECT
2035             0 as pricegroup_id,
2036             (SELECT sellprice FROM parts WHERE id = ?) AS default_sellprice,
2037             '' AS pricegroup,
2038             (SELECT DISTINCT sellprice FROM parts where id = ?) AS price,
2039             'selected' AS selected
2040           FROM prices
2041
2042           ORDER BY pricegroup|;
2043     my @values = (conv_i($id), conv_i($id), conv_i($id), conv_i($id));
2044     my $pkq = prepare_execute_query($form, $dbh, $query, @values);
2045
2046     while (my $pkr = $pkq->fetchrow_hashref('NAME_lc')) {
2047       $pkr->{id}       = $id;
2048       $pkr->{selected} = '';
2049
2050       # if there is an exchange rate change price
2051       if (($form->{exchangerate} * 1) != 0) {
2052         $pkr->{price} /= $form->{exchangerate};
2053       }
2054
2055       $pkr->{price} *= $form->{"basefactor_$i"};
2056       $pkr->{price} *= $basefactor;
2057       $pkr->{price} = $form->format_amount($myconfig, $pkr->{price}, 5);
2058
2059       if ($selectedpricegroup_id eq undef) {
2060         if ($pkr->{pricegroup_id} eq $form->{customer_klass}) {
2061
2062           $pkr->{selected}  = ' selected';
2063
2064           # no customer pricesgroup set
2065           if ($pkr->{price} == $pkr->{default_sellprice}) {
2066
2067             $pkr->{price} = $form->{"sellprice_$i"};
2068
2069           } else {
2070
2071 # this sub should not set anything and only return. --sschoeling, 20090506
2072 #            $form->{"sellprice_$i"} = $pkr->{price};
2073           }
2074
2075         } elsif ($pkr->{price} == $pkr->{default_sellprice}) {
2076           $pkr->{price}    = $form->{"sellprice_$i"};
2077           $pkr->{selected} = ' selected';
2078         }
2079       }
2080
2081       if ($selectedpricegroup_id or $selectedpricegroup_id == 0) {
2082         if ($selectedpricegroup_id ne $pricegroup_old) {
2083           if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
2084             $pkr->{selected}  = ' selected';
2085           }
2086         } elsif (($price_new != $form->{"sellprice_$i"}) and ($price_new ne 0)) {
2087           if ($pkr->{pricegroup_id} == 0) {
2088             $pkr->{price}     = $form->{"sellprice_$i"};
2089             $pkr->{selected}  = ' selected';
2090           }
2091         } elsif ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
2092           $pkr->{selected}  = ' selected';
2093           if (    ($pkr->{pricegroup_id} == 0)
2094               and ($pkr->{price} == $form->{"sellprice_$i"})) {
2095             # $pkr->{price}                         = $form->{"sellprice_$i"};
2096           } else {
2097             $pkr->{price} = $form->{"sellprice_$i"};
2098           }
2099         }
2100       }
2101       push @{ $form->{PRICES}{$i} }, $pkr;
2102
2103     }
2104     $form->{"basefactor_$i"} *= $basefactor;
2105
2106     $i++;
2107
2108     $pkq->finish;
2109   }
2110
2111   $dbh->disconnect;
2112
2113   $main::lxdebug->leave_sub();
2114 }
2115
2116 sub has_storno {
2117   $main::lxdebug->enter_sub();
2118
2119   my ($self, $myconfig, $form, $table) = @_;
2120
2121   $main::lxdebug->leave_sub() and return 0 unless ($form->{id});
2122
2123   # make sure there's no funny stuff in $table
2124   # ToDO: die when this happens and throw an error
2125   $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
2126
2127   my $dbh = $form->dbconnect($myconfig);
2128
2129   my $query = qq|SELECT storno FROM $table WHERE storno_id = ?|;
2130   my ($result) = selectrow_query($form, $dbh, $query, $form->{id});
2131
2132   $dbh->disconnect();
2133
2134   $main::lxdebug->leave_sub();
2135
2136   return $result;
2137 }
2138
2139 sub is_storno {
2140   $main::lxdebug->enter_sub();
2141
2142   my ($self, $myconfig, $form, $table, $id) = @_;
2143
2144   $main::lxdebug->leave_sub() and return 0 unless ($id);
2145
2146   # make sure there's no funny stuff in $table
2147   # ToDO: die when this happens and throw an error
2148   $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
2149
2150   my $dbh = $form->dbconnect($myconfig);
2151
2152   my $query = qq|SELECT storno FROM $table WHERE id = ?|;
2153   my ($result) = selectrow_query($form, $dbh, $query, $id);
2154
2155   $dbh->disconnect();
2156
2157   $main::lxdebug->leave_sub();
2158
2159   return $result;
2160 }
2161
2162 1;