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