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