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