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