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