Umstellung von eur zu 3 Variablen in defaults
[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, $form, $form->{"id_$i"}, $baseqty);
691
692         } else {
693           $allocated = &cogs($dbh, $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, $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, $form, $ref->{parts_id}, $ref->{qty});
1210       next;
1211     } else {
1212       if ($ref->{inventory_accno_id}) {
1213         $allocated = &cogs($dbh, $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   my ($dbh, $form, $id, $totalqty, $basefactor, $row) = @_;
1235
1236   $basefactor ||= 1;
1237
1238   $form->{taxzone_id} *=1;
1239   my $transdate  = $form->{invdate} ? $dbh->quote($form->{invdate}) : "current_date";
1240   my $taxzone_id = $form->{"taxzone_id"} * 1;
1241   my $query =
1242     qq|SELECT i.id, i.trans_id, i.base_qty, i.allocated, i.sellprice, i.price_factor,
1243          c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from AS inventory_valid,
1244          c2.accno AS    income_accno, c2.new_chart_id AS    income_new_chart, date($transdate) - c2.valid_from AS    income_valid,
1245          c3.accno AS   expense_accno, c3.new_chart_id AS   expense_new_chart, date($transdate) - c3.valid_from AS   expense_valid
1246        FROM invoice i, parts p
1247        LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id)
1248        LEFT JOIN chart c2 ON ((SELECT income_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c2.id)
1249        LEFT JOIN chart c3 ON ((select expense_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c3.id)
1250        WHERE (i.parts_id = p.id)
1251          AND (i.parts_id = ?)
1252          AND ((i.base_qty + i.allocated) < 0)
1253        ORDER BY trans_id|;
1254   my $sth = prepare_execute_query($form, $dbh, $query, conv_i($id));
1255
1256   my $allocated = 0;
1257   my $qty;
1258
1259 # all invoice entries of an example part:
1260
1261 # id | trans_id | base_qty | allocated | sellprice | inventory_accno | income_accno | expense_accno 
1262 # ---+----------+----------+-----------+-----------+-----------------+--------------+---------------
1263 #  4 |        4 |       -5 |         5 |  20.00000 | 1140            | 4400         | 5400     bought 5 for 20
1264 #  5 |        5 |        4 |        -4 |  50.00000 | 1140            | 4400         | 5400     sold   4 for 50
1265 #  6 |        6 |        1 |        -1 |  50.00000 | 1140            | 4400         | 5400     sold   1 for 50
1266 #  7 |        7 |       -5 |         1 |  20.00000 | 1140            | 4400         | 5400     bought 5 for 20
1267 #  8 |        8 |        1 |        -1 |  50.00000 | 1140            | 4400         | 5400     sold   1 for 50
1268
1269 # 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
1270 # and all parts have been allocated
1271
1272 # so transaction 8 only sees transaction 7 with unallocated parts and adjusts allocated for that transaction, before allocated was 0
1273 #  7 |        7 |       -5 |         1 |  20.00000 | 1140            | 4400         | 5400     bought 5 for 20
1274
1275 # in this example there are still 4 unsold articles
1276
1277
1278   # search all invoice entries for the part in question, adjusting "allocated"
1279   # until the total number of sold parts has been reached
1280
1281   # ORDER BY trans_id ensures FIFO
1282
1283
1284   while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
1285     if (($qty = (($ref->{base_qty} * -1) - $ref->{allocated})) > $totalqty) {
1286       $qty = $totalqty;
1287     }
1288
1289     # update allocated in invoice
1290     $form->update_balance($dbh, "invoice", "allocated", qq|id = $ref->{id}|, $qty);
1291
1292     # total expenses and inventory
1293     # sellprice is the cost of the item
1294     my $linetotal = $form->round_amount(($ref->{sellprice} * $qty) / ( ($ref->{price_factor} || 1) * ( $basefactor || 1 )), 2);
1295
1296     if ( $::instance_conf->get_inventory_system eq 'perpetual' ) {
1297       # Bestandsmethode: when selling parts, deduct their purchase value from the inventory account
1298       $ref->{expense_accno} = ($form->{"expense_accno_$row"}) ? $form->{"expense_accno_$row"} : $ref->{expense_accno};
1299       # add to expense
1300       $form->{amount_cogs}{ $form->{id} }{ $ref->{expense_accno} } += -$linetotal;
1301       $form->{expense_inventory} .= " " . $ref->{expense_accno};
1302       $ref->{inventory_accno} = ($form->{"inventory_accno_$row"}) ? $form->{"inventory_accno_$row"} : $ref->{inventory_accno};
1303       # deduct inventory
1304       $form->{amount_cogs}{ $form->{id} }{ $ref->{inventory_accno} } -= -$linetotal;
1305       $form->{expense_inventory} .= " " . $ref->{inventory_accno};
1306     }
1307
1308     # add allocated
1309     $allocated -= $qty;
1310
1311     last if (($totalqty -= $qty) <= 0);
1312   }
1313
1314   $sth->finish;
1315
1316   $main::lxdebug->leave_sub();
1317
1318   return $allocated;
1319 }
1320
1321 sub reverse_invoice {
1322   $main::lxdebug->enter_sub();
1323
1324   my ($dbh, $form) = @_;
1325
1326   # reverse inventory items
1327   my $query =
1328     qq|SELECT i.id, i.parts_id, i.qty, i.assemblyitem, p.assembly, p.inventory_accno_id
1329        FROM invoice i
1330        JOIN parts p ON (i.parts_id = p.id)
1331        WHERE i.trans_id = ?|;
1332   my $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{"id"}));
1333
1334   while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
1335
1336     if ($ref->{inventory_accno_id}) {
1337       # de-allocated purchases
1338       $query =
1339         qq|SELECT i.id, i.trans_id, i.allocated
1340            FROM invoice i
1341            WHERE (i.parts_id = ?) AND (i.allocated > 0)
1342            ORDER BY i.trans_id DESC|;
1343       my $sth2 = prepare_execute_query($form, $dbh, $query, conv_i($ref->{"parts_id"}));
1344
1345       while (my $inhref = $sth2->fetchrow_hashref('NAME_lc')) {
1346         my $qty = $ref->{qty};
1347         if (($ref->{qty} - $inhref->{allocated}) > 0) {
1348           $qty = $inhref->{allocated};
1349         }
1350
1351         # update invoice
1352         $form->update_balance($dbh, "invoice", "allocated", qq|id = $inhref->{id}|, $qty * -1);
1353
1354         last if (($ref->{qty} -= $qty) <= 0);
1355       }
1356       $sth2->finish;
1357     }
1358   }
1359
1360   $sth->finish;
1361
1362   # delete acc_trans
1363   my @values = (conv_i($form->{id}));
1364   do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE trans_id = ?|, @values);
1365   do_query($form, $dbh, qq|DELETE FROM invoice WHERE trans_id = ?|, @values);
1366   do_query($form, $dbh, qq|DELETE FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|, @values);
1367
1368   $main::lxdebug->leave_sub();
1369 }
1370
1371 sub delete_invoice {
1372   $main::lxdebug->enter_sub();
1373
1374   my ($self, $myconfig, $form) = @_;
1375
1376   # connect to database
1377   my $dbh = $form->get_standard_dbh;
1378   $dbh->begin_work;
1379
1380   &reverse_invoice($dbh, $form);
1381
1382   my @values = (conv_i($form->{id}));
1383
1384   # Falls wir ein Storno haben, müssen zwei Felder in der stornierten Rechnung wieder
1385   # zurückgesetzt werden. Vgl:
1386   #  id | storno | storno_id |  paid   |  amount
1387   #----+--------+-----------+---------+-----------
1388   # 18 | f      |           | 0.00000 | 119.00000
1389   # ZU:
1390   # 18 | t      |           |  119.00000 |  119.00000
1391   #
1392   if($form->{storno}){
1393     # storno_id auslesen und korrigieren
1394     my ($invoice_id) = selectfirst_array_query($form, $dbh, qq|SELECT storno_id FROM ar WHERE id = ?|,@values);
1395     do_query($form, $dbh, qq|UPDATE ar SET storno = 'f', paid = 0 WHERE id = ?|, $invoice_id);
1396   }
1397
1398   # delete AR record
1399   do_query($form, $dbh, qq|DELETE FROM ar WHERE id = ?|, @values);
1400
1401   # delete spool files
1402   my @spoolfiles = selectall_array_query($form, $dbh, qq|SELECT spoolfile FROM status WHERE trans_id = ?|, @values);
1403
1404   # delete status entries
1405   do_query($form, $dbh, qq|DELETE FROM status WHERE trans_id = ?|, @values);
1406
1407   my $rc = $dbh->commit;
1408
1409   if ($rc) {
1410     my $spool = $::lx_office_conf{paths}->{spool};
1411     map { unlink "$spool/$_" if -f "$spool/$_"; } @spoolfiles;
1412   }
1413
1414   $main::lxdebug->leave_sub();
1415
1416   return $rc;
1417 }
1418
1419 sub retrieve_invoice {
1420   $main::lxdebug->enter_sub();
1421
1422   my ($self, $myconfig, $form) = @_;
1423
1424   # connect to database
1425   my $dbh = $form->get_standard_dbh;
1426
1427   my ($sth, $ref, $query);
1428
1429   my $query_transdate = ", current_date AS invdate" if !$form->{id};
1430
1431   $query =
1432     qq|SELECT
1433          (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
1434          (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id)    AS income_accno,
1435          (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id)   AS expense_accno,
1436          (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id)    AS fxgain_accno,
1437          (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id)    AS fxloss_accno,
1438          d.curr AS currencies
1439          ${query_transdate}
1440        FROM defaults d|;
1441
1442   $ref = selectfirst_hashref_query($form, $dbh, $query);
1443   map { $form->{$_} = $ref->{$_} } keys %{ $ref };
1444
1445   if ($form->{id}) {
1446     my $id = conv_i($form->{id});
1447
1448     # retrieve invoice
1449     #erweiterung um das entsprechende feld lieferscheinnummer (a.donumber) in der html-maske anzuzeigen 12.02.2009 jb
1450
1451     $query =
1452       qq|SELECT
1453            a.invnumber, a.ordnumber, a.quonumber, a.cusordnumber,
1454            a.orddate, a.quodate, a.globalproject_id,
1455            a.transdate AS invdate, a.deliverydate, a.paid, a.storno, a.gldate,
1456            a.shippingpoint, a.shipvia, a.terms, a.notes, a.intnotes, a.taxzone_id,
1457            a.duedate, a.taxincluded, a.curr AS currency, a.shipto_id, a.cp_id,
1458            a.employee_id, a.salesman_id, a.payment_id,
1459            a.language_id, a.delivery_customer_id, a.delivery_vendor_id, a.type,
1460            a.transaction_description, a.donumber, a.invnumber_for_credit_note,
1461            a.marge_total, a.marge_percent,
1462            e.name AS employee
1463          FROM ar a
1464          LEFT JOIN employee e ON (e.id = a.employee_id)
1465          WHERE a.id = ?|;
1466     $ref = selectfirst_hashref_query($form, $dbh, $query, $id);
1467     map { $form->{$_} = $ref->{$_} } keys %{ $ref };
1468
1469
1470     $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate}, "buy");
1471
1472     # get shipto
1473     $query = qq|SELECT * FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|;
1474     $ref = selectfirst_hashref_query($form, $dbh, $query, $id);
1475     delete $ref->{id};
1476     map { $form->{$_} = $ref->{$_} } keys %{ $ref };
1477
1478     foreach my $vc (qw(customer vendor)) {
1479       next if !$form->{"delivery_${vc}_id"};
1480       ($form->{"delivery_${vc}_string"}) = selectrow_query($form, $dbh, qq|SELECT name FROM customer WHERE id = ?|, $id);
1481     }
1482
1483     # get printed, emailed
1484     $query = qq|SELECT printed, emailed, spoolfile, formname FROM status WHERE trans_id = ?|;
1485     $sth = prepare_execute_query($form, $dbh, $query, $id);
1486
1487     while ($ref = $sth->fetchrow_hashref('NAME_lc')) {
1488       $form->{printed} .= "$ref->{formname} " if $ref->{printed};
1489       $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
1490       $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
1491     }
1492     $sth->finish;
1493     map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
1494
1495     my $transdate = $form->{deliverydate} ? $dbh->quote($form->{deliverydate})
1496                   : $form->{invdate}      ? $dbh->quote($form->{invdate})
1497                   :                         "current_date";
1498
1499
1500     my $taxzone_id = $form->{taxzone_id} *= 1;
1501     $taxzone_id = 0 if (0 > $taxzone_id) || (3 < $taxzone_id);
1502
1503     # retrieve individual items
1504     $query =
1505       qq|SELECT
1506            c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from AS inventory_valid,
1507            c2.accno AS income_accno,    c2.new_chart_id AS income_new_chart,    date($transdate) - c2.valid_from as income_valid,
1508            c3.accno AS expense_accno,   c3.new_chart_id AS expense_new_chart,   date($transdate) - c3.valid_from AS expense_valid,
1509
1510            i.id AS invoice_id,
1511            i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.discount, i.parts_id AS id, i.unit, i.deliverydate AS reqdate,
1512            i.project_id, i.serialnumber, i.id AS invoice_pos, i.pricegroup_id, i.ordnumber, i.transdate, i.cusordnumber, i.subtotal, i.lastcost,
1513            i.price_factor_id, i.price_factor, i.marge_price_factor,
1514            p.partnumber, p.assembly, p.bin, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel,
1515            pr.projectnumber, pg.partsgroup, prg.pricegroup
1516
1517          FROM invoice i
1518          LEFT JOIN parts p ON (i.parts_id = p.id)
1519          LEFT JOIN project pr ON (i.project_id = pr.id)
1520          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1521          LEFT JOIN pricegroup prg ON (i.pricegroup_id = prg.id)
1522
1523          LEFT JOIN chart c1 ON ((SELECT inventory_accno_id             FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id)
1524          LEFT JOIN chart c2 ON ((SELECT income_accno_id_${taxzone_id}  FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c2.id)
1525          LEFT JOIN chart c3 ON ((SELECT expense_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c3.id)
1526
1527          WHERE (i.trans_id = ?) AND NOT (i.assemblyitem = '1') ORDER BY i.id|;
1528
1529     $sth = prepare_execute_query($form, $dbh, $query, $id);
1530
1531     while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
1532       # Retrieve custom variables.
1533       my $cvars = CVar->get_custom_variables(dbh        => $dbh,
1534                                              module     => 'IC',
1535                                              sub_module => 'invoice',
1536                                              trans_id   => $ref->{invoice_id},
1537                                             );
1538       map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
1539       delete $ref->{invoice_id};
1540
1541       map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid)) if !$ref->{"part_inventory_accno_id"};
1542       delete($ref->{"part_inventory_accno_id"});
1543
1544       foreach my $type (qw(inventory income expense)) {
1545         while ($ref->{"${type}_new_chart"} && ($ref->{"${type}_valid"} >=0)) {
1546           my $query = qq|SELECT accno, new_chart_id, date($transdate) - valid_from FROM chart WHERE id = ?|;
1547           @$ref{ map $type.$_, qw(_accno _new_chart _valid) } = selectrow_query($form, $dbh, $query, $ref->{"${type}_new_chart"});
1548         }
1549       }
1550
1551       # get tax rates and description
1552       my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1553       $query =
1554         qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber FROM tax t
1555            LEFT JOIN chart c ON (c.id = t.chart_id)
1556            WHERE t.id IN
1557              (SELECT tk.tax_id FROM taxkeys tk
1558               WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?)
1559                 AND startdate <= date($transdate)
1560               ORDER BY startdate DESC LIMIT 1)
1561            ORDER BY c.accno|;
1562       my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
1563       $ref->{taxaccounts} = "";
1564       my $i=0;
1565       while (my $ptr = $stw->fetchrow_hashref('NAME_lc')) {
1566
1567         if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1568           $i++;
1569           $ptr->{accno} = $i;
1570         }
1571         $ref->{taxaccounts} .= "$ptr->{accno} ";
1572
1573         if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
1574           $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
1575           $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1576           $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
1577           $form->{taxaccounts} .= "$ptr->{accno} ";
1578         }
1579
1580       }
1581
1582       $ref->{qty} *= -1 if $form->{type} eq "credit_note";
1583
1584       chop $ref->{taxaccounts};
1585       push @{ $form->{invoice_details} }, $ref;
1586       $stw->finish;
1587     }
1588     $sth->finish;
1589
1590     Common::webdav_folder($form);
1591   }
1592
1593   my $rc = $dbh->commit;
1594
1595   $main::lxdebug->leave_sub();
1596
1597   return $rc;
1598 }
1599
1600 sub get_customer {
1601   $main::lxdebug->enter_sub();
1602
1603   my ($self, $myconfig, $form) = @_;
1604
1605   # connect to database
1606   my $dbh = $form->get_standard_dbh;
1607
1608   my $dateformat = $myconfig->{dateformat};
1609   $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
1610
1611   my (@values, $duedate, $ref, $query);
1612
1613   if ($form->{invdate}) {
1614     $duedate = "to_date(?, '$dateformat')";
1615     push @values, $form->{invdate};
1616   } else {
1617     $duedate = "current_date";
1618   }
1619
1620   my $cid = conv_i($form->{customer_id});
1621   my $payment_id;
1622
1623   if ($form->{payment_id}) {
1624     $payment_id = "(pt.id = ?) OR";
1625     push @values, conv_i($form->{payment_id});
1626   }
1627
1628   # get customer
1629   $query =
1630     qq|SELECT
1631          c.id AS customer_id, c.name AS customer, c.discount as customer_discount, c.creditlimit, c.terms,
1632          c.email, c.cc, c.bcc, c.language_id, c.payment_id,
1633          c.street, c.zipcode, c.city, c.country,
1634          c.notes AS intnotes, c.klass as customer_klass, c.taxzone_id, c.salesman_id,
1635          $duedate + COALESCE(pt.terms_netto, 0) AS duedate,
1636          b.discount AS tradediscount, b.description AS business
1637        FROM customer c
1638        LEFT JOIN business b ON (b.id = c.business_id)
1639        LEFT JOIN payment_terms pt ON ($payment_id (c.payment_id = pt.id))
1640        WHERE c.id = ?|;
1641   push @values, $cid;
1642   $ref = selectfirst_hashref_query($form, $dbh, $query, @values);
1643
1644   delete $ref->{salesman_id} if !$ref->{salesman_id};
1645
1646   map { $form->{$_} = $ref->{$_} } keys %$ref;
1647
1648   $query =
1649     qq|SELECT sum(amount - paid) AS dunning_amount
1650        FROM ar
1651        WHERE (paid < amount)
1652          AND (customer_id = ?)
1653          AND (dunning_config_id IS NOT NULL)|;
1654   $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1655   map { $form->{$_} = $ref->{$_} } keys %$ref;
1656
1657   $query =
1658     qq|SELECT dnn.dunning_description AS max_dunning_level
1659        FROM dunning_config dnn
1660        WHERE id IN (SELECT dunning_config_id
1661                     FROM ar
1662                     WHERE (paid < amount) AND (customer_id = ?) AND (dunning_config_id IS NOT NULL))
1663        ORDER BY dunning_level DESC LIMIT 1|;
1664   $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1665   map { $form->{$_} = $ref->{$_} } keys %$ref;
1666
1667   $form->{creditremaining} = $form->{creditlimit};
1668   $query = qq|SELECT SUM(amount - paid) FROM ar WHERE customer_id = ?|;
1669   my ($value) = selectrow_query($form, $dbh, $query, $cid);
1670   $form->{creditremaining} -= $value;
1671
1672   $query =
1673     qq|SELECT o.amount,
1674          (SELECT e.buy FROM exchangerate e
1675           WHERE e.curr = o.curr
1676             AND e.transdate = o.transdate)
1677        FROM oe o
1678        WHERE o.customer_id = ?
1679          AND o.quotation = '0'
1680          AND o.closed = '0'|;
1681   my $sth = prepare_execute_query($form, $dbh, $query, $cid);
1682
1683   while (my ($amount, $exch) = $sth->fetchrow_array) {
1684     $exch = 1 unless $exch;
1685     $form->{creditremaining} -= $amount * $exch;
1686   }
1687   $sth->finish;
1688
1689   # get shipto if we did not converted an order or invoice
1690   if (!$form->{shipto}) {
1691     map { delete $form->{$_} }
1692       qw(shiptoname shiptodepartment_1 shiptodepartment_2
1693          shiptostreet shiptozipcode shiptocity shiptocountry
1694          shiptocontact shiptophone shiptofax shiptoemail);
1695
1696     $query = qq|SELECT * FROM shipto WHERE trans_id = ? AND module = 'CT'|;
1697     $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1698     delete $ref->{id};
1699     map { $form->{$_} = $ref->{$_} } keys %$ref;
1700   }
1701
1702   # setup last accounts used for this customer
1703   if (!$form->{id} && $form->{type} !~ /_(order|quotation)/) {
1704     $query =
1705       qq|SELECT c.id, c.accno, c.description, c.link, c.category
1706          FROM chart c
1707          JOIN acc_trans ac ON (ac.chart_id = c.id)
1708          JOIN ar a ON (a.id = ac.trans_id)
1709          WHERE a.customer_id = ?
1710            AND NOT (c.link LIKE '%_tax%' OR c.link LIKE '%_paid%')
1711            AND a.id IN (SELECT max(a2.id) FROM ar a2 WHERE a2.customer_id = ?)|;
1712     $sth = prepare_execute_query($form, $dbh, $query, $cid, $cid);
1713
1714     my $i = 0;
1715     while ($ref = $sth->fetchrow_hashref('NAME_lc')) {
1716       if ($ref->{category} eq 'I') {
1717         $i++;
1718         $form->{"AR_amount_$i"} = "$ref->{accno}--$ref->{description}";
1719
1720         if ($form->{initial_transdate}) {
1721           my $tax_query =
1722             qq|SELECT tk.tax_id, t.rate
1723                FROM taxkeys tk
1724                LEFT JOIN tax t ON tk.tax_id = t.id
1725                WHERE (tk.chart_id = ?) AND (startdate <= date(?))
1726                ORDER BY tk.startdate DESC
1727                LIMIT 1|;
1728           my ($tax_id, $rate) =
1729             selectrow_query($form, $dbh, $tax_query, $ref->{id},
1730                             $form->{initial_transdate});
1731           $form->{"taxchart_$i"} = "${tax_id}--${rate}";
1732         }
1733       }
1734       if ($ref->{category} eq 'A') {
1735         $form->{ARselected} = $form->{AR_1} = $ref->{accno};
1736       }
1737     }
1738     $sth->finish;
1739     $form->{rowcount} = $i if ($i && !$form->{type});
1740   }
1741
1742   $main::lxdebug->leave_sub();
1743 }
1744
1745 sub retrieve_item {
1746   $main::lxdebug->enter_sub();
1747
1748   my ($self, $myconfig, $form) = @_;
1749
1750   # connect to database
1751   my $dbh = $form->get_standard_dbh;
1752
1753   my $i = $form->{rowcount};
1754
1755   my $where = qq|NOT p.obsolete = '1'|;
1756   my @values;
1757
1758   foreach my $column (qw(p.partnumber p.description pgpartsgroup )) {
1759     my ($table, $field) = split m/\./, $column;
1760     next if !$form->{"${field}_${i}"};
1761     $where .= qq| AND lower(${column}) ILIKE ?|;
1762     push @values, '%' . $form->{"${field}_${i}"} . '%';
1763   }
1764
1765   #Es soll auch nach EAN gesucht werden, ohne Einschränkung durch Beschreibung
1766   if ($form->{"partnumber_$i"} && !$form->{"description_$i"}) {
1767     $where .= qq| OR (NOT p.obsolete = '1' AND p.ean = ? )|;
1768     push @values, $form->{"partnumber_$i"};
1769   }
1770
1771   if ($form->{"id_${i}"}) {
1772     $where .= qq| AND p.id = ?|;
1773     push @values, $form->{"id_${i}"};
1774   }
1775
1776   if ($form->{"description_$i"}) {
1777     $where .= qq| ORDER BY p.description|;
1778   } else {
1779     $where .= qq| ORDER BY p.partnumber|;
1780   }
1781
1782   my $transdate;
1783   if ($form->{type} eq "invoice") {
1784     $transdate =
1785       $form->{deliverydate} ? $dbh->quote($form->{deliverydate}) :
1786       $form->{invdate}      ? $dbh->quote($form->{invdate}) :
1787                               "current_date";
1788   } else {
1789     $transdate =
1790       $form->{transdate}    ? $dbh->quote($form->{transdate}) :
1791                               "current_date";
1792   }
1793
1794   my $taxzone_id = $form->{taxzone_id} * 1;
1795   $taxzone_id = 0 if (0 > $taxzone_id) || (3 < $taxzone_id);
1796
1797   my $query =
1798     qq|SELECT
1799          p.id, p.partnumber, p.description, p.sellprice,
1800          p.listprice, p.inventory_accno_id, p.lastcost,
1801
1802          c1.accno AS inventory_accno,
1803          c1.new_chart_id AS inventory_new_chart,
1804          date($transdate) - c1.valid_from AS inventory_valid,
1805
1806          c2.accno AS income_accno,
1807          c2.new_chart_id AS income_new_chart,
1808          date($transdate)  - c2.valid_from AS income_valid,
1809
1810          c3.accno AS expense_accno,
1811          c3.new_chart_id AS expense_new_chart,
1812          date($transdate) - c3.valid_from AS expense_valid,
1813
1814          p.unit, p.assembly, p.bin, p.onhand,
1815          p.notes AS partnotes, p.notes AS longdescription,
1816          p.not_discountable, p.formel, p.payment_id AS part_payment_id,
1817          p.price_factor_id,
1818
1819          pfac.factor AS price_factor,
1820
1821          pg.partsgroup
1822
1823        FROM parts p
1824        LEFT JOIN chart c1 ON
1825          ((SELECT inventory_accno_id
1826            FROM buchungsgruppen
1827            WHERE id = p.buchungsgruppen_id) = c1.id)
1828        LEFT JOIN chart c2 ON
1829          ((SELECT income_accno_id_${taxzone_id}
1830            FROM buchungsgruppen
1831            WHERE id = p.buchungsgruppen_id) = c2.id)
1832        LEFT JOIN chart c3 ON
1833          ((SELECT expense_accno_id_${taxzone_id}
1834            FROM buchungsgruppen
1835            WHERE id = p.buchungsgruppen_id) = c3.id)
1836        LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
1837        LEFT JOIN price_factors pfac ON (pfac.id = p.price_factor_id)
1838        WHERE $where|;
1839   my $sth = prepare_execute_query($form, $dbh, $query, @values);
1840
1841   my @translation_queries = ( [ qq|SELECT tr.translation, tr.longdescription
1842                                    FROM translation tr
1843                                    WHERE tr.language_id = ? AND tr.parts_id = ?| ],
1844                               [ qq|SELECT tr.translation, tr.longdescription
1845                                    FROM translation tr
1846                                    WHERE tr.language_id IN
1847                                      (SELECT id
1848                                       FROM language
1849                                       WHERE article_code = (SELECT article_code FROM language WHERE id = ?))
1850                                      AND tr.parts_id = ?
1851                                    LIMIT 1| ] );
1852   map { push @{ $_ }, prepare_query($form, $dbh, $_->[0]) } @translation_queries;
1853
1854   while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
1855
1856     # In der Buchungsgruppe ist immer ein Bestandskonto verknuepft, auch wenn
1857     # es sich um eine Dienstleistung handelt. Bei Dienstleistungen muss das
1858     # Buchungskonto also aus dem Ergebnis rausgenommen werden.
1859     if (!$ref->{inventory_accno_id}) {
1860       map({ delete($ref->{"inventory_${_}"}); } qw(accno new_chart valid));
1861     }
1862     delete($ref->{inventory_accno_id});
1863
1864     foreach my $type (qw(inventory income expense)) {
1865       while ($ref->{"${type}_new_chart"} && ($ref->{"${type}_valid"} >=0)) {
1866         my $query =
1867           qq|SELECT accno, new_chart_id, date($transdate) - valid_from
1868              FROM chart
1869              WHERE id = ?|;
1870         ($ref->{"${type}_accno"},
1871          $ref->{"${type}_new_chart"},
1872          $ref->{"${type}_valid"})
1873           = selectrow_query($form, $dbh, $query, $ref->{"${type}_new_chart"});
1874       }
1875     }
1876
1877     if ($form->{payment_id} eq "") {
1878       $form->{payment_id} = $form->{part_payment_id};
1879     }
1880
1881     # get tax rates and description
1882     my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1883     $query =
1884       qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber
1885          FROM tax t
1886          LEFT JOIN chart c ON (c.id = t.chart_id)
1887          WHERE t.id in
1888            (SELECT tk.tax_id
1889             FROM taxkeys tk
1890             WHERE tk.chart_id = (SELECT id from chart WHERE accno = ?)
1891               AND startdate <= ?
1892             ORDER BY startdate DESC
1893             LIMIT 1)
1894          ORDER BY c.accno|;
1895     @values = ($accno_id, $transdate eq "current_date" ? "now" : $transdate);
1896     my $stw = $dbh->prepare($query);
1897     $stw->execute(@values) || $form->dberror($query);
1898
1899     $ref->{taxaccounts} = "";
1900     my $i = 0;
1901     while (my $ptr = $stw->fetchrow_hashref('NAME_lc')) {
1902
1903       #    if ($customertax{$ref->{accno}})
1904       if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1905         $i++;
1906         $ptr->{accno} = $i;
1907       }
1908       $ref->{taxaccounts} .= "$ptr->{accno} ";
1909
1910       if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
1911         $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
1912         $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1913         $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
1914         $form->{taxaccounts} .= "$ptr->{accno} ";
1915       }
1916
1917     }
1918
1919     $stw->finish;
1920     chop $ref->{taxaccounts};
1921
1922     if ($form->{language_id}) {
1923       for my $spec (@translation_queries) {
1924         do_statement($form, $spec->[1], $spec->[0], conv_i($form->{language_id}), conv_i($ref->{id}));
1925         my ($translation, $longdescription) = $spec->[1]->fetchrow_array;
1926         next unless $translation;
1927         $ref->{description} = $translation;
1928         $ref->{longdescription} = $longdescription;
1929         last;
1930       }
1931     }
1932
1933     $ref->{onhand} *= 1;
1934
1935     push @{ $form->{item_list} }, $ref;
1936   }
1937   $sth->finish;
1938   $_->[1]->finish for @translation_queries;
1939
1940   foreach my $item (@{ $form->{item_list} }) {
1941     my $custom_variables = CVar->get_custom_variables(module   => 'IC',
1942                                                       trans_id => $item->{id},
1943                                                       dbh      => $dbh,
1944                                                      );
1945
1946     map { $item->{"ic_cvar_" . $_->{name} } = $_->{value} } @{ $custom_variables };
1947   }
1948
1949   $main::lxdebug->leave_sub();
1950 }
1951
1952 ##########################
1953 # get pricegroups from database
1954 # build up selected pricegroup
1955 # if an exchange rate - change price
1956 # for each part
1957 #
1958 sub get_pricegroups_for_parts {
1959
1960   $main::lxdebug->enter_sub();
1961
1962   my ($self, $myconfig, $form) = @_;
1963
1964   my $dbh = $form->get_standard_dbh;
1965
1966   $form->{"PRICES"} = {};
1967
1968   my $i  = 1;
1969   my $id = 0;
1970   my $all_units = AM->retrieve_units($myconfig, $form);
1971   while (($form->{"id_$i"}) or ($form->{"new_id_$i"})) {
1972     $form->{"PRICES"}{$i} = [];
1973
1974     $id = $form->{"id_$i"};
1975
1976     if (!($form->{"id_$i"}) and $form->{"new_id_$i"}) {
1977       $id = $form->{"new_id_$i"};
1978     }
1979
1980     my ($price, $selectedpricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
1981
1982     my $pricegroup_old = $form->{"pricegroup_old_$i"};
1983
1984     # sellprice has format 13,0000 or 0,00000, can't check for 0 numerically
1985     my $sellprice = $form->{"sellprice_$i"};
1986     my $pricegroup_id = $form->{"pricegroup_id_$i"};
1987     $form->{"new_pricegroup_$i"} = $selectedpricegroup_id;
1988     $form->{"old_pricegroup_$i"} = $pricegroup_old;
1989
1990     my $price_new = $form->{"price_new_$i"};
1991     my $price_old = $form->{"price_old_$i"};
1992
1993     if (!$form->{"unit_old_$i"}) {
1994       # Neue Ware aus der Datenbank. In diesem Fall ist unit_$i die
1995       # Einheit, wie sie in den Stammdaten hinterlegt wurde.
1996       # Es sollte also angenommen werden, dass diese ausgewaehlt war.
1997       $form->{"unit_old_$i"} = $form->{"unit_$i"};
1998     }
1999
2000     # Die zuletzt ausgewaehlte mit der aktuell ausgewaehlten Einheit
2001     # vergleichen und bei Unterschied den Preis entsprechend umrechnen.
2002     $form->{"selected_unit_$i"} = $form->{"unit_$i"} unless ($form->{"selected_unit_$i"});
2003
2004     if (!$all_units->{$form->{"selected_unit_$i"}} ||
2005         ($all_units->{$form->{"selected_unit_$i"}}->{"base_unit"} ne
2006          $all_units->{$form->{"unit_old_$i"}}->{"base_unit"})) {
2007       # Die ausgewaehlte Einheit ist fuer diesen Artikel nicht gueltig
2008       # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber
2009       # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen.
2010       $form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"};
2011     }
2012
2013     my $basefactor = 1;
2014
2015     if ($form->{"unit_old_$i"} ne $form->{"selected_unit_$i"}) {
2016       if (defined($all_units->{$form->{"unit_old_$i"}}->{"factor"}) &&
2017           $all_units->{$form->{"unit_old_$i"}}->{"factor"}) {
2018         $basefactor = $all_units->{$form->{"selected_unit_$i"}}->{"factor"} /
2019           $all_units->{$form->{"unit_old_$i"}}->{"factor"};
2020       }
2021     }
2022
2023     if (!$form->{"basefactor_$i"}) {
2024       $form->{"basefactor_$i"} = 1;
2025     }
2026
2027     my $query =
2028        qq|SELECT
2029             0 as pricegroup_id,
2030             sellprice AS default_sellprice,
2031             '' AS pricegroup,
2032             sellprice AS price,
2033             'selected' AS selected
2034           FROM parts
2035           WHERE id = ?
2036           UNION ALL
2037           SELECT
2038            pricegroup_id,
2039            parts.sellprice AS default_sellprice,
2040            pricegroup.pricegroup,
2041            price,
2042            '' AS selected
2043           FROM prices
2044           LEFT JOIN parts ON parts.id = parts_id
2045           LEFT JOIN pricegroup ON pricegroup.id = pricegroup_id
2046           WHERE parts_id = ?
2047           ORDER BY pricegroup|;
2048     my @values = (conv_i($id), conv_i($id));
2049     my $pkq = prepare_execute_query($form, $dbh, $query, @values);
2050
2051     while (my $pkr = $pkq->fetchrow_hashref('NAME_lc')) {
2052       $pkr->{id}       = $id;
2053       $pkr->{selected} = '';
2054
2055       # if there is an exchange rate change price
2056       if (($form->{exchangerate} * 1) != 0) {
2057         $pkr->{price} /= $form->{exchangerate};
2058       }
2059
2060       $pkr->{price} *= $form->{"basefactor_$i"};
2061       $pkr->{price} *= $basefactor;
2062       $pkr->{price} = $form->format_amount($myconfig, $pkr->{price}, 5);
2063
2064       if ($selectedpricegroup_id eq undef) {
2065         # new entries in article list, either old invoice was loaded (edit) or a new article was added
2066         # Case A: open old invoice, no pricegroup selected
2067         # Case B: add new article to invoice, no pricegroup selected
2068
2069         # to distinguish case A and B the variable pricegroup_id_$i is used
2070         # for new articles this variable isn't defined, for loaded articles it is
2071         # sellprice can't be used, as it already has 0,00 set
2072
2073         if ($pkr->{pricegroup_id} eq $form->{"pricegroup_id_$i"} and defined $form->{"pricegroup_id_$i"}) {
2074           # Case A
2075           $pkr->{selected}  = ' selected';
2076
2077         } elsif ($pkr->{pricegroup_id} eq $form->{customer_klass}
2078                  and not defined $form->{"pricegroup_id_$i"}
2079                  and $pkr->{price} != 0    # only use customer pricegroup price if it has a value, else use default_sellprice
2080                                            # for the case where pricegroup prices haven't been set
2081                 ) {
2082           # Case B: use default pricegroup of customer
2083
2084           $pkr->{selected}  = ' selected'; # unless $form->{selected};
2085
2086           # no customer pricesgroup set
2087           if ($pkr->{price} == $pkr->{default_sellprice}) {
2088
2089             $pkr->{price} = $form->{"sellprice_$i"};
2090
2091           } else {
2092
2093 # this sub should not set anything and only return. --sschoeling, 20090506
2094 # is this correct? put in again... -- grichardson 20110119
2095             $form->{"sellprice_$i"} = $pkr->{price};
2096           }
2097
2098         } elsif ($pkr->{price} == $pkr->{default_sellprice} and $pkr->{default_sellprice} != 0) {
2099           $pkr->{price}    = $form->{"sellprice_$i"};
2100           $pkr->{selected} = ' selected';
2101         }
2102       }
2103
2104       # existing article: pricegroup or price changed
2105       if ($selectedpricegroup_id or $selectedpricegroup_id == 0) {
2106         if ($selectedpricegroup_id ne $pricegroup_old) {
2107           # pricegroup has changed
2108           if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
2109             $pkr->{selected}  = ' selected';
2110           }
2111         } elsif ( ($form->parse_amount($myconfig, $price_new)
2112                  != $form->parse_amount($myconfig, $form->{"sellprice_$i"}))
2113                   and ($price_new ne 0) and defined $price_new) {
2114           # sellprice has changed
2115           # when loading existing invoices $price_new is NULL
2116           if ($pkr->{pricegroup_id} == 0) {
2117             $pkr->{price}     = $form->{"sellprice_$i"};
2118             $pkr->{selected}  = ' selected';
2119           }
2120         } elsif ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
2121           # neither sellprice nor pricegroup changed
2122           $pkr->{selected}  = ' selected';
2123           if (    ($pkr->{pricegroup_id} == 0) and ($pkr->{price} == $form->{"sellprice_$i"})) {
2124             # $pkr->{price}                         = $form->{"sellprice_$i"};
2125           } else {
2126             $pkr->{price} = $form->{"sellprice_$i"};
2127           }
2128         }
2129       }
2130       push @{ $form->{PRICES}{$i} }, $pkr;
2131
2132     }
2133     $form->{"basefactor_$i"} *= $basefactor;
2134
2135     $i++;
2136
2137     $pkq->finish;
2138   }
2139
2140   $main::lxdebug->leave_sub();
2141 }
2142
2143 sub has_storno {
2144   $main::lxdebug->enter_sub();
2145
2146   my ($self, $myconfig, $form, $table) = @_;
2147
2148   $main::lxdebug->leave_sub() and return 0 unless ($form->{id});
2149
2150   # make sure there's no funny stuff in $table
2151   # ToDO: die when this happens and throw an error
2152   $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
2153
2154   my $dbh = $form->get_standard_dbh;
2155
2156   my $query = qq|SELECT storno FROM $table WHERE storno_id = ?|;
2157   my ($result) = selectrow_query($form, $dbh, $query, $form->{id});
2158
2159   $main::lxdebug->leave_sub();
2160
2161   return $result;
2162 }
2163
2164 sub is_storno {
2165   $main::lxdebug->enter_sub();
2166
2167   my ($self, $myconfig, $form, $table, $id) = @_;
2168
2169   $main::lxdebug->leave_sub() and return 0 unless ($id);
2170
2171   # make sure there's no funny stuff in $table
2172   # ToDO: die when this happens and throw an error
2173   $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
2174
2175   my $dbh = $form->get_standard_dbh;
2176
2177   my $query = qq|SELECT storno FROM $table WHERE id = ?|;
2178   my ($result) = selectrow_query($form, $dbh, $query, $id);
2179
2180   $main::lxdebug->leave_sub();
2181
2182   return $result;
2183 }
2184
2185 sub get_standard_accno_current_assets {
2186   $main::lxdebug->enter_sub();
2187
2188   my ($self, $myconfig, $form) = @_;
2189
2190   my $dbh = $form->get_standard_dbh;
2191
2192   my $query = qq| SELECT accno FROM chart WHERE id = (SELECT ar_paid_accno_id FROM defaults)|;
2193   my ($result) = selectrow_query($form, $dbh, $query);
2194
2195   $main::lxdebug->leave_sub();
2196
2197   return $result;
2198 }
2199
2200 1;