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