1 #=====================================================================
 
   4 # Based on SQL-Ledger Version 2.1.9
 
   5 # Web http://www.lx-office.org
 
   7 #=====================================================================
 
   8 # SQL-Ledger Accounting
 
   9 # Copyright (C) 1998-2002
 
  11 #  Author: Dieter Simader
 
  12 #   Email: dsimader@sql-ledger.org
 
  13 #     Web: http://www.sql-ledger.org
 
  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.
 
  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 #======================================================================
 
  31 # Inventory invoicing module
 
  33 #======================================================================
 
  37 use List::Util qw(max);
 
  43 use SL::DATEV qw(:CONSTANTS);
 
  46 use SL::GenericTranslations;
 
  59   $main::lxdebug->enter_sub();
 
  61   my ($self, $myconfig, $form, $locale) = @_;
 
  63   $form->{duedate} ||= $form->{invdate};
 
  66   my $dbh = $form->get_standard_dbh;
 
  69   my $query = qq|SELECT date | . conv_dateq($form->{duedate}) . qq| - date | . conv_dateq($form->{invdate}) . qq| AS terms|;
 
  70   ($form->{terms}) = selectrow_query($form, $dbh, $query);
 
  73   $form->{TEMPLATE_ARRAYS} = {};
 
  75   push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
 
  77   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
 
  80   foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
 
  81     $price_factors{$pfac->{id}}  = $pfac;
 
  83     $pfac->{formatted_factor}    = $form->format_amount($myconfig, $pfac->{factor});
 
  86   # sort items by partsgroup
 
  87   for my $i (1 .. $form->{rowcount}) {
 
  89 #    if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
 
  90 #      $partsgroup = $form->{"partsgroup_$i"};
 
  92 #    push @partsgroup, [$i, $partsgroup];
 
  93     push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
 
  99     $projects = SL::DB::Manager::Project->get_all(query => [ id => \@project_ids ]);
 
 100     %projects_by_id = map { $_->id => $_ } @$projects;
 
 103   if ($projects_by_id{$form->{"globalproject_id"}}) {
 
 104     $form->{globalprojectnumber} = $projects_by_id{$form->{"globalproject_id"}}->projectnumber;
 
 105     $form->{globalprojectdescription} = $projects_by_id{$form->{"globalproject_id"}}->description;
 
 107     for (@{ $projects_by_id{$form->{"globalproject_id"}}->cvars_by_config }) {
 
 108       $form->{"project_cvar_" . $_->config->name} = $_->value_as_text;
 
 118   # sort items by partsgroup
 
 119   for $i (1 .. $form->{rowcount}) {
 
 121     if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
 
 122       $partsgroup = $form->{"partsgroup_$i"};
 
 124     push @partsgroup, [$i, $partsgroup];
 
 137   my $nodiscount_subtotal = 0;
 
 138   my $discount_subtotal = 0;
 
 140   my $subtotal_header = 0;
 
 143   $form->{discount} = [];
 
 145   IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
 
 147   my $ic_cvar_configs = CVar->get_configs(module => 'IC');
 
 148   my $project_cvar_configs = CVar->get_configs(module => 'Projects');
 
 151     qw(runningnumber number description longdescription qty ship unit bin
 
 152        deliverydate_oe ordnumber_oe donumber_do transdate_oe validuntil
 
 153        partnotes serialnumber reqdate sellprice listprice netprice
 
 154        discount p_discount discount_sub nodiscount_sub
 
 155        linetotal  nodiscount_linetotal tax_rate projectnumber projectdescription
 
 156        price_factor price_factor_name partsgroup weight lineweight);
 
 158   push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
 
 159   push @arrays, map { "project_cvar_$_->{name}" } @{ $project_cvar_configs };
 
 161   my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
 
 163   my @payment_arrays = qw(payment paymentaccount paymentdate paymentsource paymentmemo);
 
 165   map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @payment_arrays);
 
 168   foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
 
 171     if ($item->[1] ne $sameitem) {
 
 172       push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
 
 173       $sameitem = $item->[1];
 
 175       map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
 178     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
 
 180     if ($form->{"id_$i"} != 0) {
 
 182       # add number, description and qty to $form->{number},
 
 183       if ($form->{"subtotal_$i"} && !$subtotal_header) {
 
 184         $subtotal_header = $i;
 
 185         $position = int($position);
 
 188       } elsif ($subtotal_header) {
 
 190         $position = int($position);
 
 191         $position = $position.".".$subposition;
 
 193         $position = int($position);
 
 197       my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
 
 199       push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} },     $position;
 
 200       push @{ $form->{TEMPLATE_ARRAYS}->{number} },            $form->{"partnumber_$i"};
 
 201       push @{ $form->{TEMPLATE_ARRAYS}->{serialnumber} },      $form->{"serialnumber_$i"};
 
 202       push @{ $form->{TEMPLATE_ARRAYS}->{bin} },               $form->{"bin_$i"};
 
 203       push @{ $form->{TEMPLATE_ARRAYS}->{partnotes} },         $form->{"partnotes_$i"};
 
 204       push @{ $form->{TEMPLATE_ARRAYS}->{description} },       $form->{"description_$i"};
 
 205       push @{ $form->{TEMPLATE_ARRAYS}->{longdescription} },   $form->{"longdescription_$i"};
 
 206       push @{ $form->{TEMPLATE_ARRAYS}->{qty} },               $form->format_amount($myconfig, $form->{"qty_$i"});
 
 207       push @{ $form->{TEMPLATE_ARRAYS}->{qty_nofmt} },         $form->{"qty_$i"};
 
 208       push @{ $form->{TEMPLATE_ARRAYS}->{unit} },              $form->{"unit_$i"};
 
 209       push @{ $form->{TEMPLATE_ARRAYS}->{deliverydate_oe} },   $form->{"reqdate_$i"};
 
 210       push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} },         $form->{"sellprice_$i"};
 
 211       push @{ $form->{TEMPLATE_ARRAYS}->{sellprice_nofmt} },   $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
 212       push @{ $form->{TEMPLATE_ARRAYS}->{ordnumber_oe} },      $form->{"ordnumber_$i"};
 
 213       push @{ $form->{TEMPLATE_ARRAYS}->{donumber_do} },       $form->{"donumber_$i"};
 
 214       push @{ $form->{TEMPLATE_ARRAYS}->{transdate_oe} },      $form->{"transdate_$i"};
 
 215       push @{ $form->{TEMPLATE_ARRAYS}->{invnumber} },         $form->{"invnumber"};
 
 216       push @{ $form->{TEMPLATE_ARRAYS}->{invdate} },           $form->{"invdate"};
 
 217       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} },      $price_factor->{formatted_factor};
 
 218       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
 
 219       push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} },        $form->{"partsgroup_$i"};
 
 220       push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} },           $form->{"reqdate_$i"};
 
 221       push(@{ $form->{TEMPLATE_ARRAYS}->{listprice} },         $form->{"listprice_$i"});
 
 223       my $sellprice     = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
 224       my ($dec)         = ($sellprice =~ /\.(\d+)/);
 
 225       my $decimalplaces = max 2, length($dec);
 
 227       my $parsed_discount            = $form->parse_amount($myconfig, $form->{"discount_$i"});
 
 229       my $linetotal_exact            = $form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor};
 
 230       my $linetotal                  = $form->round_amount($linetotal_exact, 2);
 
 232       my $nodiscount_exact_linetotal = $form->{"qty_$i"} * $sellprice                                  / $price_factor->{factor};
 
 233       my $nodiscount_linetotal       = $form->round_amount($nodiscount_exact_linetotal,2);
 
 235       my $discount                   = $nodiscount_linetotal - $linetotal; # is always rounded because $nodiscount_linetotal and $linetotal are rounded
 
 237       my $discount_round_error       = $discount + ($linetotal_exact - $nodiscount_exact_linetotal); # not used
 
 239       $form->{"netprice_$i"}   = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, 2);
 
 241       push @{ $form->{TEMPLATE_ARRAYS}->{netprice} },       ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
 
 242       push @{ $form->{TEMPLATE_ARRAYS}->{netprice_nofmt} }, ($form->{"netprice_$i"} != 0) ? $form->{"netprice_$i"} : '';
 
 244       $linetotal = ($linetotal != 0) ? $linetotal : '';
 
 246       push @{ $form->{TEMPLATE_ARRAYS}->{discount} },       ($discount != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
 
 247       push @{ $form->{TEMPLATE_ARRAYS}->{discount_nofmt} }, ($discount != 0) ? $discount * -1 : '';
 
 248       push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} },     $form->{"discount_$i"};
 
 250       $form->{total}            += $linetotal;
 
 251       $form->{nodiscount_total} += $nodiscount_linetotal;
 
 252       $form->{discount_total}   += $discount;
 
 254       if ($subtotal_header) {
 
 255         $discount_subtotal   += $linetotal;
 
 256         $nodiscount_subtotal += $nodiscount_linetotal;
 
 259       if ($form->{"subtotal_$i"} && $subtotal_header && ($subtotal_header != $i)) {
 
 260         push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} },         $form->format_amount($myconfig, $discount_subtotal,   2);
 
 261         push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub_nofmt} },   $discount_subtotal;
 
 262         push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} },       $form->format_amount($myconfig, $nodiscount_subtotal, 2);
 
 263         push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub_nofmt} }, $nodiscount_subtotal;
 
 265         $discount_subtotal   = 0;
 
 266         $nodiscount_subtotal = 0;
 
 267         $subtotal_header     = 0;
 
 270         push @{ $form->{TEMPLATE_ARRAYS}->{$_} }, "" for qw(discount_sub nodiscount_sub discount_sub_nofmt nodiscount_sub_nofmt);
 
 273       if (!$form->{"discount_$i"}) {
 
 274         $nodiscount += $linetotal;
 
 277       push @{ $form->{TEMPLATE_ARRAYS}->{linetotal} },                  $form->format_amount($myconfig, $linetotal, 2);
 
 278       push @{ $form->{TEMPLATE_ARRAYS}->{linetotal_nofmt} },            $linetotal_exact;
 
 279       push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} },       $form->format_amount($myconfig, $nodiscount_linetotal, 2);
 
 280       push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal_nofmt} }, $nodiscount_linetotal;
 
 282       my $project = $projects_by_id{$form->{"project_id_$i"}} || SL::DB::Project->new;
 
 284       push @{ $form->{TEMPLATE_ARRAYS}->{projectnumber} },              $project->projectnumber;
 
 285       push @{ $form->{TEMPLATE_ARRAYS}->{projectdescription} },         $project->description;
 
 287       my $lineweight = $form->{"qty_$i"} * $form->{"weight_$i"};
 
 288       $totalweight += $lineweight;
 
 289       push @{ $form->{TEMPLATE_ARRAYS}->{weight} },            $form->format_amount($myconfig, $form->{"weight_$i"}, 3);
 
 290       push @{ $form->{TEMPLATE_ARRAYS}->{weight_nofmt} },      $form->{"weight_$i"};
 
 291       push @{ $form->{TEMPLATE_ARRAYS}->{lineweight} },        $form->format_amount($myconfig, $lineweight, 3);
 
 292       push @{ $form->{TEMPLATE_ARRAYS}->{lineweight_nofmt} },  $lineweight;
 
 294       @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
 
 298       map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
 
 300       if ($form->{taxincluded}) {
 
 303         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
 
 304         $taxbase = $linetotal - $taxamount;
 
 306         $taxamount = $linetotal * $taxrate;
 
 307         $taxbase   = $linetotal;
 
 310       if ($form->round_amount($taxrate, 7) == 0) {
 
 311         if ($form->{taxincluded}) {
 
 312           foreach my $accno (@taxaccounts) {
 
 313             $taxamount            = $form->round_amount($linetotal * $form->{"${accno}_rate"} / (1 + abs($form->{"${accno}_rate"})), 2);
 
 315             $taxaccounts{$accno} += $taxamount;
 
 316             $taxdiff             += $taxamount;
 
 318             $taxbase{$accno}     += $taxbase;
 
 320           $taxaccounts{ $taxaccounts[0] } += $taxdiff;
 
 322           foreach my $accno (@taxaccounts) {
 
 323             $taxaccounts{$accno} += $linetotal * $form->{"${accno}_rate"};
 
 324             $taxbase{$accno}     += $taxbase;
 
 328         foreach my $accno (@taxaccounts) {
 
 329           $taxaccounts{$accno} += $taxamount * $form->{"${accno}_rate"} / $taxrate;
 
 330           $taxbase{$accno}     += $taxbase;
 
 333       my $tax_rate = $taxrate * 100;
 
 334       push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
 
 335       if ($form->{"assembly_$i"}) {
 
 338         # get parts and push them onto the stack
 
 340         if ($form->{groupitems}) {
 
 342             qq|ORDER BY pg.partsgroup, a.oid|;
 
 344           $sortorder = qq|ORDER BY a.oid|;
 
 348           qq|SELECT p.partnumber, p.description, p.unit, a.qty, pg.partsgroup
 
 350              JOIN parts p ON (a.parts_id = p.id)
 
 351              LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
 352              WHERE (a.bom = '1') AND (a.id = ?) $sortorder|;
 
 353         $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{"id_$i"}));
 
 355         while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
 
 356           if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
 
 357             map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
 358             $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
 
 359             push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
 
 362           map { $form->{"a_$_"} = $ref->{$_} } qw(partnumber description);
 
 364           push(@{ $form->{TEMPLATE_ARRAYS}->{description} },
 
 365                $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}
 
 367                  . qq| -- $form->{"a_partnumber"}, $form->{"a_description"}|);
 
 368           map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
 374       push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} },
 
 375         CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)
 
 376           for @{ $ic_cvar_configs };
 
 378       push @{ $form->{TEMPLATE_ARRAYS}->{"project_cvar_" . $_->config->name} }, $_->value_as_text for @{ $project->cvars_by_config };
 
 382   $form->{totalweight}       = $form->format_amount($myconfig, $totalweight, 3);
 
 383   $form->{totalweight_nofmt} = $totalweight;
 
 384   my $defaults = AM->get_defaults();
 
 385   $form->{weightunit}        = $defaults->{weightunit};
 
 387   foreach my $item (sort keys %taxaccounts) {
 
 388     $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
 
 390     push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase} },        $form->format_amount($myconfig, $taxbase{$item}, 2));
 
 391     push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase_nofmt} },  $taxbase{$item});
 
 392     push(@{ $form->{TEMPLATE_ARRAYS}->{tax} },            $form->format_amount($myconfig, $taxamount,      2));
 
 393     push(@{ $form->{TEMPLATE_ARRAYS}->{tax_nofmt} },      $taxamount );
 
 394     push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} },        $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
 
 395     push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate_nofmt} },  $form->{"${item}_rate"} * 100);
 
 396     push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} },      $form->{"${item}_taxnumber"});
 
 398     my $tax_obj     = SL::DB::Manager::Tax->find_by(taxnumber => $form->{"${item}_taxnumber"});
 
 399     my $description = $tax_obj->translated_attribute('taxdescription',  $form->{language_id}, 0) if $tax_obj;
 
 400     push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $description . q{ } . 100 * $form->{"${item}_rate"} . q{%});
 
 403   for my $i (1 .. $form->{paidaccounts}) {
 
 404     if ($form->{"paid_$i"}) {
 
 405       my ($accno, $description) = split(/--/, $form->{"AR_paid_$i"});
 
 407       push(@{ $form->{TEMPLATE_ARRAYS}->{payment} },        $form->{"paid_$i"});
 
 408       push(@{ $form->{TEMPLATE_ARRAYS}->{paymentaccount} }, $description);
 
 409       push(@{ $form->{TEMPLATE_ARRAYS}->{paymentdate} },    $form->{"datepaid_$i"});
 
 410       push(@{ $form->{TEMPLATE_ARRAYS}->{paymentsource} },  $form->{"source_$i"});
 
 411       push(@{ $form->{TEMPLATE_ARRAYS}->{paymentmemo} },    $form->{"memo_$i"});
 
 413       $form->{paid} += $form->parse_amount($myconfig, $form->{"paid_$i"});
 
 416   if($form->{taxincluded}) {
 
 417     $form->{subtotal}       = $form->format_amount($myconfig, $form->{total} - $tax, 2);
 
 418     $form->{subtotal_nofmt} = $form->{total} - $tax;
 
 421     $form->{subtotal}       = $form->format_amount($myconfig, $form->{total}, 2);
 
 422     $form->{subtotal_nofmt} = $form->{total};
 
 425   $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
 
 426   $form->{discount_total}      = $form->format_amount($myconfig, $form->{discount_total}, 2);
 
 427   $form->{nodiscount}          = $form->format_amount($myconfig, $nodiscount, 2);
 
 428   $form->{yesdiscount}         = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
 
 430   $form->{invtotal} = ($form->{taxincluded}) ? $form->{total} : $form->{total} + $tax;
 
 431   $form->{total}    = $form->format_amount($myconfig, $form->{invtotal} - $form->{paid}, 2);
 
 433   $form->{invtotal} = $form->format_amount($myconfig, $form->{invtotal}, 2);
 
 434   $form->{paid}     = $form->format_amount($myconfig, $form->{paid}, 2);
 
 436   $form->set_payment_options($myconfig, $form->{invdate});
 
 438   $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
 
 439   $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
 
 441   $form->{username} = $myconfig->{name};
 
 443   $main::lxdebug->leave_sub();
 
 446 sub project_description {
 
 447   $main::lxdebug->enter_sub();
 
 449   my ($self, $dbh, $id) = @_;
 
 450   my $form = \%main::form;
 
 452   my $query = qq|SELECT description FROM project WHERE id = ?|;
 
 453   my ($description) = selectrow_query($form, $dbh, $query, conv_i($id));
 
 455   $main::lxdebug->leave_sub();
 
 460 sub customer_details {
 
 461   $main::lxdebug->enter_sub();
 
 463   my ($self, $myconfig, $form, @wanted_vars) = @_;
 
 465   # connect to database
 
 466   my $dbh = $form->get_standard_dbh;
 
 468   my $language_id = $form->{language_id};
 
 470   # get contact id, set it if nessessary
 
 473   my @values =  (conv_i($form->{customer_id}));
 
 476   if ($form->{cp_id}) {
 
 477     $where = qq| AND (cp.cp_id = ?) |;
 
 478     push(@values, conv_i($form->{cp_id}));
 
 481   # get rest for the customer
 
 483     qq|SELECT ct.*, cp.*, ct.notes as customernotes,
 
 484          ct.phone AS customerphone, ct.fax AS customerfax, ct.email AS customeremail,
 
 487        LEFT JOIN contacts cp on ct.id = cp.cp_cv_id
 
 488        LEFT JOIN currencies cu ON (ct.currency_id = cu.id)
 
 489        WHERE (ct.id = ?) $where
 
 492   my $ref = selectfirst_hashref_query($form, $dbh, $query, @values);
 
 494   # remove id and taxincluded before copy back
 
 495   delete @$ref{qw(id taxincluded)};
 
 497   @wanted_vars = grep({ $_ } @wanted_vars);
 
 498   if (scalar(@wanted_vars) > 0) {
 
 500     map({ $h_wanted_vars{$_} = 1; } @wanted_vars);
 
 501     map({ delete($ref->{$_}) unless ($h_wanted_vars{$_}); } keys(%{$ref}));
 
 504   map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 506   if ($form->{delivery_customer_id}) {
 
 508       qq|SELECT *, notes as customernotes
 
 512     $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{delivery_customer_id}));
 
 514     map { $form->{"dc_$_"} = $ref->{$_} } keys %$ref;
 
 517   if ($form->{delivery_vendor_id}) {
 
 519       qq|SELECT *, notes as customernotes
 
 523     $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{delivery_vendor_id}));
 
 525     map { $form->{"dv_$_"} = $ref->{$_} } keys %$ref;
 
 528   my $custom_variables = CVar->get_custom_variables('dbh'      => $dbh,
 
 530                                                     'trans_id' => $form->{customer_id});
 
 531   map { $form->{"vc_cvar_$_->{name}"} = $_->{value} } @{ $custom_variables };
 
 533   $form->{cp_greeting} = GenericTranslations->get('dbh'              => $dbh,
 
 534                                                   'translation_type' => 'greetings::' . ($form->{cp_gender} eq 'f' ? 'female' : 'male'),
 
 535                                                   'language_id'      => $language_id,
 
 536                                                   'allow_fallback'   => 1);
 
 539   $main::lxdebug->leave_sub();
 
 543   $main::lxdebug->enter_sub();
 
 545   my ($self, $myconfig, $form, $provided_dbh, $payments_only) = @_;
 
 547   # connect to database, turn off autocommit
 
 548   my $dbh = $provided_dbh ? $provided_dbh : $form->get_standard_dbh;
 
 550   my ($query, $sth, $null, $project_id, @values);
 
 551   my $exchangerate = 0;
 
 553   my $ic_cvar_configs = CVar->get_configs(module => 'IC',
 
 556   if (!$form->{employee_id}) {
 
 557     $form->get_employee($dbh);
 
 560   $form->{defaultcurrency} = $form->get_default_currency($myconfig);
 
 561   my $defaultcurrency = $form->{defaultcurrency};
 
 563   # Seit neuestem wird die department_id schon Ã¼bergeben UND $form->department nicht mehr
 
 564   # korrekt zusammengebaut. Sehr wahrscheinlich beim Umstieg auf T8 kaputt gegangen
 
 565   # Ich lass den Code von 2005 erstmal noch stehen ;-) jb 03-2011
 
 566   if (!$form->{department_id}){
 
 567     ($null, $form->{department_id}) = split(/--/, $form->{department});
 
 570   my $all_units = AM->retrieve_units($myconfig, $form);
 
 572   if (!$payments_only) {
 
 574       &reverse_invoice($dbh, $form);
 
 577       my $trans_number   = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{invnumber}, save => 1);
 
 578       $form->{invnumber} = $trans_number->create_unique unless $trans_number->is_unique;
 
 580       $query = qq|SELECT nextval('glid')|;
 
 581       ($form->{"id"}) = selectrow_query($form, $dbh, $query);
 
 583       $query = qq|INSERT INTO ar (id, invnumber, currency_id) VALUES (?, ?, (SELECT id FROM currencies WHERE name=?))|;
 
 584       do_query($form, $dbh, $query, $form->{"id"}, $form->{"id"}, $form->{currency});
 
 586       if (!$form->{invnumber}) {
 
 587         my $trans_number   = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{invnumber}, id => $form->{id});
 
 588         $form->{invnumber} = $trans_number->create_unique;
 
 593   my ($netamount, $invoicediff) = (0, 0);
 
 594   my ($amount, $linetotal, $lastincomeaccno);
 
 596   if ($form->{currency} eq $defaultcurrency) {
 
 597     $form->{exchangerate} = 1;
 
 599     $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{invdate}, 'buy');
 
 602   $form->{exchangerate} =
 
 605     : $form->parse_amount($myconfig, $form->{exchangerate});
 
 607   $form->{expense_inventory} = "";
 
 611   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
 
 612   my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
 
 615   $form->{amount}      = {};
 
 616   $form->{amount_cogs} = {};
 
 618   foreach my $i (1 .. $form->{rowcount}) {
 
 619     if ($form->{type} eq "credit_note") {
 
 620       $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"}) * -1;
 
 621       $form->{shipped} = 1;
 
 623       $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
 
 628     $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
 
 629     $form->{"marge_absolut_$i"} = $form->parse_amount($myconfig, $form->{"marge_absolut_$i"}) * 1;
 
 630     $form->{"lastcost_$i"} = $form->parse_amount($myconfig, $form->{"lastcost_$i"}) * 1;
 
 632     if ($form->{storno}) {
 
 633       $form->{"qty_$i"} *= -1;
 
 636     if ($form->{"id_$i"}) {
 
 639       if (defined($baseunits{$form->{"id_$i"}})) {
 
 640         $item_unit = $baseunits{$form->{"id_$i"}};
 
 643         $query = qq|SELECT unit FROM parts WHERE id = ?|;
 
 644         ($item_unit) = selectrow_query($form, $dbh, $query, conv_i($form->{"id_$i"}));
 
 645         $baseunits{$form->{"id_$i"}} = $item_unit;
 
 648       if (defined($all_units->{$item_unit}->{factor})
 
 649           && ($all_units->{$item_unit}->{factor} ne '')
 
 650           && ($all_units->{$item_unit}->{factor} != 0)) {
 
 651         $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
 
 655       $baseqty = $form->{"qty_$i"} * $basefactor;
 
 657       my ($allocated, $taxrate) = (0, 0);
 
 661       map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
 
 663       # keep entered selling price
 
 665         $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
 667       my ($dec) = ($fxsellprice =~ /\.(\d+)/);
 
 669       my $decimalplaces = ($dec > 2) ? $dec : 2;
 
 671       # undo discount formatting
 
 672       $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
 
 675       $form->{"sellprice_$i"} = $fxsellprice * (1 - $form->{"discount_$i"});
 
 677       # round linetotal to 2 decimal places
 
 678       $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
 
 679       $linetotal    = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
 
 681       if ($form->{taxincluded}) {
 
 682         $taxamount = $linetotal * ($taxrate / (1 + $taxrate));
 
 683         $form->{"sellprice_$i"} =
 
 684           $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
 
 686         $taxamount = $linetotal * $taxrate;
 
 689       $netamount += $linetotal;
 
 691       if ($taxamount != 0) {
 
 693           $form->{amount}{ $form->{id} }{$_} +=
 
 694             $taxamount * $form->{"${_}_rate"} / $taxrate
 
 695         } split(/ /, $form->{"taxaccounts_$i"});
 
 698       # add amount to income, $form->{amount}{trans_id}{accno}
 
 699       $amount = $form->{"sellprice_$i"} * $form->{"qty_$i"} * $form->{exchangerate} / $price_factor;
 
 701       $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2) * $form->{exchangerate};
 
 702       $linetotal = $form->round_amount($linetotal, 2);
 
 704       # this is the difference from the inventory
 
 705       $invoicediff += ($amount - $linetotal);
 
 707       $form->{amount}{ $form->{id} }{ $form->{"income_accno_$i"} } +=
 
 710       $lastincomeaccno = $form->{"income_accno_$i"};
 
 712       # adjust and round sellprice
 
 713       $form->{"sellprice_$i"} =
 
 714         $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate},
 
 717       next if $payments_only;
 
 719       if ($form->{"inventory_accno_$i"} || $form->{"assembly_$i"}) {
 
 721         if ($form->{"assembly_$i"}) {
 
 722           # record assembly item as allocated
 
 723           &process_assembly($dbh, $myconfig, $form, $form->{"id_$i"}, $baseqty);
 
 726           $allocated = &cogs($dbh, $myconfig, $form, $form->{"id_$i"}, $baseqty, $basefactor, $i);
 
 730       # Get pricegroup_id and save it. Unfortunately the interface
 
 731       # also uses ID "0" for signalling that none is selected, but "0"
 
 732       # must not be stored in the database. Therefore we cannot simply
 
 734       ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
 
 736       $pricegroup_id  = undef if !$pricegroup_id;
 
 738       my ($invoice_id) = selectfirst_array_query($form, $dbh, qq|SELECT nextval('invoiceid')|);
 
 740       # save detail record in invoice table
 
 742         qq|INSERT INTO invoice (id, trans_id, parts_id, description, longdescription, qty,
 
 743                                 sellprice, fxsellprice, discount, allocated, assemblyitem,
 
 744                                 unit, deliverydate, project_id, serialnumber, pricegroup_id,
 
 745                                 ordnumber, donumber, transdate, cusordnumber, base_qty, subtotal,
 
 746                                 marge_percent, marge_total, lastcost,
 
 747                                 price_factor_id, price_factor, marge_price_factor)
 
 748            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
 
 749                    (SELECT factor FROM price_factors WHERE id = ?), ?)|;
 
 751       @values = ($invoice_id, conv_i($form->{id}), conv_i($form->{"id_$i"}),
 
 752                  $form->{"description_$i"}, $form->{"longdescription_$i"}, $form->{"qty_$i"},
 
 753                  $form->{"sellprice_$i"}, $fxsellprice,
 
 754                  $form->{"discount_$i"}, $allocated, 'f',
 
 755                  $form->{"unit_$i"}, conv_date($form->{"reqdate_$i"}), conv_i($form->{"project_id_$i"}),
 
 756                  $form->{"serialnumber_$i"}, $pricegroup_id,
 
 757                  $form->{"ordnumber_$i"}, $form->{"donumber_$i"}, conv_date($form->{"transdate_$i"}),
 
 758                  $form->{"cusordnumber_$i"}, $baseqty, $form->{"subtotal_$i"} ? 't' : 'f',
 
 759                  $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
 
 760                  $form->{"lastcost_$i"},
 
 761                  conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
 
 762                  conv_i($form->{"marge_price_factor_$i"}));
 
 763       do_query($form, $dbh, $query, @values);
 
 765       CVar->save_custom_variables(module       => 'IC',
 
 766                                   sub_module   => 'invoice',
 
 767                                   trans_id     => $invoice_id,
 
 768                                   configs      => $ic_cvar_configs,
 
 770                                   name_prefix  => 'ic_',
 
 771                                   name_postfix => "_$i",
 
 776   # total payments, don't move we need it here
 
 777   for my $i (1 .. $form->{paidaccounts}) {
 
 778     if ($form->{type} eq "credit_note") {
 
 779       $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"}) * -1;
 
 781       $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
 
 783     $form->{paid} += $form->{"paid_$i"};
 
 784     $form->{datepaid} = $form->{"datepaid_$i"} if ($form->{"datepaid_$i"});
 
 787   my ($tax, $diff) = (0, 0);
 
 789   $netamount = $form->round_amount($netamount, 2);
 
 791   # figure out rounding errors for total amount vs netamount + taxes
 
 792   if ($form->{taxincluded}) {
 
 794     $amount = $form->round_amount($netamount * $form->{exchangerate}, 2);
 
 795     $diff += $amount - $netamount * $form->{exchangerate};
 
 796     $netamount = $amount;
 
 798     foreach my $item (split(/ /, $form->{taxaccounts})) {
 
 799       $amount = $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate};
 
 800       $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount, 2);
 
 801       $tax += $form->{amount}{ $form->{id} }{$item};
 
 802       $netamount -= $form->{amount}{ $form->{id} }{$item};
 
 805     $invoicediff += $diff;
 
 806     ######## this only applies to tax included
 
 807     if ($lastincomeaccno) {
 
 808       $form->{amount}{ $form->{id} }{$lastincomeaccno} += $invoicediff;
 
 812     $amount    = $form->round_amount($netamount * $form->{exchangerate}, 2);
 
 813     $diff      = $amount - $netamount * $form->{exchangerate};
 
 814     $netamount = $amount;
 
 815     foreach my $item (split(/ /, $form->{taxaccounts})) {
 
 816       $form->{amount}{ $form->{id} }{$item} =
 
 817         $form->round_amount($form->{amount}{ $form->{id} }{$item}, 2);
 
 820                  $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate},
 
 823         $amount - $form->{amount}{ $form->{id} }{$item} *
 
 824         $form->{exchangerate};
 
 825       $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount, 2);
 
 826       $tax += $form->{amount}{ $form->{id} }{$item};
 
 830   $form->{amount}{ $form->{id} }{ $form->{AR} } = $netamount + $tax;
 
 832     $form->round_amount($form->{paid} * $form->{exchangerate} + $diff, 2);
 
 835   $form->{amount}{ $form->{id} }{ $form->{AR} } *= -1;
 
 837   # update exchangerate
 
 838   if (($form->{currency} ne $defaultcurrency) && !$exchangerate) {
 
 839     $form->update_exchangerate($dbh, $form->{currency}, $form->{invdate},
 
 840                                $form->{exchangerate}, 0);
 
 843   $project_id = conv_i($form->{"globalproject_id"});
 
 844   # entsprechend auch beim Bestimmen des Steuerschlüssels in Taxkey.pm berücksichtigen
 
 845   my $taxdate = $form->{deliverydate} ? $form->{deliverydate} : $form->{invdate};
 
 847   foreach my $trans_id (keys %{ $form->{amount_cogs} }) {
 
 848     foreach my $accno (keys %{ $form->{amount_cogs}{$trans_id} }) {
 
 849       next unless ($form->{expense_inventory} =~ /\Q$accno\E/);
 
 851       $form->{amount_cogs}{$trans_id}{$accno} = $form->round_amount($form->{amount_cogs}{$trans_id}{$accno}, 2);
 
 853       if (!$payments_only && ($form->{amount_cogs}{$trans_id}{$accno} != 0)) {
 
 855           qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
 
 856                VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, (SELECT id FROM tax WHERE taxkey=0), 0, ?, (SELECT link FROM chart WHERE accno = ?))|;
 
 857         @values = (conv_i($trans_id), $accno, $form->{amount_cogs}{$trans_id}{$accno}, conv_date($form->{invdate}), conv_i($project_id), $accno);
 
 858         do_query($form, $dbh, $query, @values);
 
 859         $form->{amount_cogs}{$trans_id}{$accno} = 0;
 
 863     foreach my $accno (keys %{ $form->{amount_cogs}{$trans_id} }) {
 
 864       $form->{amount_cogs}{$trans_id}{$accno} = $form->round_amount($form->{amount_cogs}{$trans_id}{$accno}, 2);
 
 866       if (!$payments_only && ($form->{amount_cogs}{$trans_id}{$accno} != 0)) {
 
 868           qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
 
 869                VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, (SELECT id FROM tax WHERE taxkey=0), 0, ?, (SELECT link FROM chart WHERE accno = ?))|;
 
 870         @values = (conv_i($trans_id), $accno, $form->{amount_cogs}{$trans_id}{$accno}, conv_date($form->{invdate}), conv_i($project_id), $accno);
 
 871         do_query($form, $dbh, $query, @values);
 
 876   foreach my $trans_id (keys %{ $form->{amount} }) {
 
 877     foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
 
 878       next unless ($form->{expense_inventory} =~ /\Q$accno\E/);
 
 880       $form->{amount}{$trans_id}{$accno} = $form->round_amount($form->{amount}{$trans_id}{$accno}, 2);
 
 882       if (!$payments_only && ($form->{amount}{$trans_id}{$accno} != 0)) {
 
 884           qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
 
 885              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
 
 888                       WHERE chart_id= (SELECT id
 
 892                       ORDER BY startdate DESC LIMIT 1),
 
 895                       WHERE chart_id= (SELECT id
 
 899                       ORDER BY startdate DESC LIMIT 1),
 
 901                      (SELECT link FROM chart WHERE accno = ?))|;
 
 902         @values = (conv_i($trans_id), $accno, $form->{amount}{$trans_id}{$accno}, conv_date($form->{invdate}), $accno, conv_date($taxdate), $accno, conv_date($taxdate), conv_i($project_id), $accno);
 
 903         do_query($form, $dbh, $query, @values);
 
 904         $form->{amount}{$trans_id}{$accno} = 0;
 
 908     foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
 
 909       $form->{amount}{$trans_id}{$accno} = $form->round_amount($form->{amount}{$trans_id}{$accno}, 2);
 
 911       if (!$payments_only && ($form->{amount}{$trans_id}{$accno} != 0)) {
 
 913           qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
 
 914              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
 
 917                       WHERE chart_id= (SELECT id
 
 921                       ORDER BY startdate DESC LIMIT 1),
 
 924                       WHERE chart_id= (SELECT id
 
 928                       ORDER BY startdate DESC LIMIT 1),
 
 930                      (SELECT link FROM chart WHERE accno = ?))|;
 
 931         @values = (conv_i($trans_id), $accno, $form->{amount}{$trans_id}{$accno}, conv_date($form->{invdate}), $accno, conv_date($taxdate), $accno, conv_date($taxdate), conv_i($project_id), $accno);
 
 932         do_query($form, $dbh, $query, @values);
 
 937   # deduct payment differences from diff
 
 938   for my $i (1 .. $form->{paidaccounts}) {
 
 939     if ($form->{"paid_$i"} != 0) {
 
 941         $form->round_amount($form->{"paid_$i"} * $form->{exchangerate}, 2);
 
 942       $diff -= $amount - $form->{"paid_$i"} * $form->{exchangerate};
 
 946   # record payments and offsetting AR
 
 947   if (!$form->{storno}) {
 
 948     for my $i (1 .. $form->{paidaccounts}) {
 
 950       if ($form->{"acc_trans_id_$i"}
 
 952           && (SL::DB::Default->get->payments_changeable == 0)) {
 
 956       next if ($form->{"paid_$i"} == 0);
 
 958       my ($accno) = split(/--/, $form->{"AR_paid_$i"});
 
 959       $form->{"datepaid_$i"} = $form->{invdate}
 
 960       unless ($form->{"datepaid_$i"});
 
 961       $form->{datepaid} = $form->{"datepaid_$i"};
 
 965       if ($form->{currency} eq $defaultcurrency) {
 
 966         $form->{"exchangerate_$i"} = 1;
 
 968         $exchangerate              = $form->check_exchangerate($myconfig, $form->{currency}, $form->{"datepaid_$i"}, 'buy');
 
 969         $form->{"exchangerate_$i"} = $exchangerate || $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
 
 973       $amount = $form->round_amount($form->{"paid_$i"} * $form->{exchangerate} + $diff, 2);
 
 975       if ($form->{amount}{ $form->{id} }{ $form->{AR} } != 0) {
 
 977         qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
 
 978            VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
 
 981                     WHERE chart_id= (SELECT id
 
 985                     ORDER BY startdate DESC LIMIT 1),
 
 988                     WHERE chart_id= (SELECT id
 
 992                     ORDER BY startdate DESC LIMIT 1),
 
 994                    (SELECT link FROM chart WHERE accno = ?))|;
 
 995         @values = (conv_i($form->{"id"}), $form->{AR}, $amount, $form->{"datepaid_$i"}, $form->{AR}, conv_date($taxdate), $form->{AR}, conv_date($taxdate), $project_id, $form->{AR});
 
 996         do_query($form, $dbh, $query, @values);
 
1000       $form->{"paid_$i"} *= -1;
 
1001       my $gldate = (conv_date($form->{"gldate_$i"}))? conv_date($form->{"gldate_$i"}) : conv_date($form->current_date($myconfig));
 
1004       qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, gldate, source, memo, tax_id, taxkey, project_id, chart_link)
 
1005          VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, ?, ?, ?,
 
1008                   WHERE chart_id= (SELECT id
 
1012                   ORDER BY startdate DESC LIMIT 1),
 
1015                   WHERE chart_id= (SELECT id
 
1019                   ORDER BY startdate DESC LIMIT 1),
 
1021                  (SELECT link FROM chart WHERE accno = ?))|;
 
1022       @values = (conv_i($form->{"id"}), $accno, $form->{"paid_$i"}, $form->{"datepaid_$i"},
 
1023                  $gldate, $form->{"source_$i"}, $form->{"memo_$i"}, $accno, conv_date($taxdate), $accno, conv_date($taxdate), $project_id, $accno);
 
1024       do_query($form, $dbh, $query, @values);
 
1026       # exchangerate difference
 
1027       $form->{fx}{$accno}{ $form->{"datepaid_$i"} } +=
 
1028         $form->{"paid_$i"} * ($form->{"exchangerate_$i"} - 1) + $diff;
 
1032         $form->{"paid_$i"} * $form->{exchangerate} - $form->{"paid_$i"} *
 
1033         $form->{"exchangerate_$i"};
 
1035         $form->{fx}{ $form->{fxgain_accno} }{ $form->{"datepaid_$i"} } += $amount;
 
1037         $form->{fx}{ $form->{fxloss_accno} }{ $form->{"datepaid_$i"} } += $amount;
 
1042       # update exchange rate
 
1043       if (($form->{currency} ne $defaultcurrency) && !$exchangerate) {
 
1044         $form->update_exchangerate($dbh, $form->{currency},
 
1045                                    $form->{"datepaid_$i"},
 
1046                                    $form->{"exchangerate_$i"}, 0);
 
1050   } else {                      # if (!$form->{storno})
 
1051     $form->{marge_total} *= -1;
 
1054   IO->set_datepaid(table => 'ar', id => $form->{id}, dbh => $dbh);
 
1056   # record exchange rate differences and gains/losses
 
1057   foreach my $accno (keys %{ $form->{fx} }) {
 
1058     foreach my $transdate (keys %{ $form->{fx}{$accno} }) {
 
1059       $form->{fx}{$accno}{$transdate} = $form->round_amount($form->{fx}{$accno}{$transdate}, 2);
 
1060       if ( $form->{fx}{$accno}{$transdate} != 0 ) {
 
1063           qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, cleared, fx_transaction, tax_id, taxkey, project_id, chart_link)
 
1064              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, '0', '1',
 
1067                   WHERE chart_id= (SELECT id
 
1071                   ORDER BY startdate DESC LIMIT 1),
 
1074                   WHERE chart_id= (SELECT id
 
1078                   ORDER BY startdate DESC LIMIT 1),
 
1080                  (SELECT link FROM chart WHERE accno = ?))|;
 
1081         @values = (conv_i($form->{"id"}), $accno, $form->{fx}{$accno}{$transdate}, conv_date($transdate), $accno, conv_date($taxdate), $accno, conv_date($taxdate), conv_i($project_id), $accno);
 
1082         do_query($form, $dbh, $query, @values);
 
1087   if ($payments_only) {
 
1088     $query = qq|UPDATE ar SET paid = ? WHERE id = ?|;
 
1089     do_query($form, $dbh, $query,  $form->{paid}, conv_i($form->{id}));
 
1091     $dbh->commit if !$provided_dbh;
 
1093     $main::lxdebug->leave_sub();
 
1097   $amount = $netamount + $tax;
 
1100   #erweiterung fuer lieferscheinnummer (donumber) 12.02.09 jb
 
1102   $query = qq|UPDATE ar set
 
1103                 invnumber   = ?, ordnumber     = ?, quonumber     = ?, cusordnumber  = ?,
 
1104                 transdate   = ?, orddate       = ?, quodate       = ?, customer_id   = ?,
 
1105                 amount      = ?, netamount     = ?, paid          = ?,
 
1106                 duedate     = ?, deliverydate  = ?, invoice       = ?, shippingpoint = ?,
 
1107                 shipvia     = ?, terms         = ?, notes         = ?, intnotes      = ?,
 
1108                 currency_id = (SELECT id FROM currencies WHERE name = ?),
 
1109                 department_id = ?, payment_id    = ?, taxincluded   = ?,
 
1110                 type        = ?, language_id   = ?, taxzone_id    = ?, shipto_id     = ?,
 
1111                 employee_id = ?, salesman_id   = ?, storno_id     = ?, storno        = ?,
 
1112                 cp_id       = ?, marge_total   = ?, marge_percent = ?,
 
1113                 globalproject_id               = ?, delivery_customer_id             = ?,
 
1114                 transaction_description        = ?, delivery_vendor_id               = ?,
 
1115                 donumber    = ?, invnumber_for_credit_note = ?,        direct_debit  = ?,
 
1116                 delivery_term_id = ?
 
1118   @values = (          $form->{"invnumber"},           $form->{"ordnumber"},             $form->{"quonumber"},          $form->{"cusordnumber"},
 
1119              conv_date($form->{"invdate"}),  conv_date($form->{"orddate"}),    conv_date($form->{"quodate"}),    conv_i($form->{"customer_id"}),
 
1120                        $amount,                        $netamount,                       $form->{"paid"},
 
1121              conv_date($form->{"duedate"}),  conv_date($form->{"deliverydate"}),    '1',                                $form->{"shippingpoint"},
 
1122                        $form->{"shipvia"},      conv_i($form->{"terms"}),                $form->{"notes"},              $form->{"intnotes"},
 
1123                        $form->{"currency"},     conv_i($form->{"department_id"}), conv_i($form->{"payment_id"}),        $form->{"taxincluded"} ? 't' : 'f',
 
1124                        $form->{"type"},         conv_i($form->{"language_id"}),   conv_i($form->{"taxzone_id"}), conv_i($form->{"shipto_id"}),
 
1125                 conv_i($form->{"employee_id"}), conv_i($form->{"salesman_id"}),   conv_i($form->{storno_id}),           $form->{"storno"} ? 't' : 'f',
 
1126                 conv_i($form->{"cp_id"}),            1 * $form->{marge_total} ,      1 * $form->{marge_percent},
 
1127                 conv_i($form->{"globalproject_id"}),                              conv_i($form->{"delivery_customer_id"}),
 
1128                        $form->{transaction_description},                          conv_i($form->{"delivery_vendor_id"}),
 
1129                        $form->{"donumber"}, $form->{"invnumber_for_credit_note"},        $form->{direct_debit} ? 't' : 'f',
 
1130                 conv_i($form->{delivery_term_id}),
 
1131                 conv_i($form->{"id"}));
 
1132   do_query($form, $dbh, $query, @values);
 
1135   if ($form->{storno}) {
 
1138            paid = paid + amount,
 
1140            intnotes = ? || intnotes
 
1142     do_query($form, $dbh, $query, "Rechnung storniert am $form->{invdate} ", conv_i($form->{"storno_id"}));
 
1143     do_query($form, $dbh, qq|UPDATE ar SET paid = amount WHERE id = ?|, conv_i($form->{"id"}));
 
1147   $form->{name} = $form->{customer};
 
1148   $form->{name} =~ s/--\Q$form->{customer_id}\E//;
 
1150   if (!$form->{shipto_id}) {
 
1151     $form->add_shipto($dbh, $form->{id}, "AR");
 
1154   # save printed, emailed and queued
 
1155   $form->save_status($dbh);
 
1157   Common::webdav_folder($form);
 
1159   # Link this record to the records it was created from.
 
1160   RecordLinks->create_links('dbh'        => $dbh,
 
1162                             'from_table' => 'oe',
 
1163                             'from_ids'   => $form->{convert_from_oe_ids},
 
1165                             'to_id'      => $form->{id},
 
1167   delete $form->{convert_from_oe_ids};
 
1169   my @convert_from_do_ids = map { $_ * 1 } grep { $_ } split m/\s+/, $form->{convert_from_do_ids};
 
1171   if (scalar @convert_from_do_ids) {
 
1172     DO->close_orders('dbh' => $dbh,
 
1173                      'ids' => \@convert_from_do_ids);
 
1175     RecordLinks->create_links('dbh'        => $dbh,
 
1177                               'from_table' => 'delivery_orders',
 
1178                               'from_ids'   => \@convert_from_do_ids,
 
1180                               'to_id'      => $form->{id},
 
1183   delete $form->{convert_from_do_ids};
 
1185   ARAP->close_orders_if_billed('dbh'     => $dbh,
 
1186                                'arap_id' => $form->{id},
 
1189   # safety check datev export
 
1190   if ($::instance_conf->get_datev_check_on_sales_invoice) {
 
1191     my $transdate = $::form->{invdate} ? DateTime->from_lxoffice($::form->{invdate}) : undef;
 
1192     $transdate  ||= DateTime->today;
 
1194     my $datev = SL::DATEV->new(
 
1195       exporttype => DATEV_ET_BUCHUNGEN,
 
1196       format     => DATEV_FORMAT_KNE,
 
1200       trans_id   => $form->{id},
 
1205     if ($datev->errors) {
 
1207       die join "\n", $::locale->text('DATEV check returned errors:'), $datev->errors;
 
1212   $dbh->commit if !$provided_dbh;
 
1214   $main::lxdebug->leave_sub();
 
1219 sub _delete_payments {
 
1220   $main::lxdebug->enter_sub();
 
1222   my ($self, $form, $dbh) = @_;
 
1224   my @delete_acc_trans_ids;
 
1226   # Delete old payment entries from acc_trans.
 
1228     qq|SELECT acc_trans_id
 
1230        WHERE (trans_id = ?) AND fx_transaction
 
1234        SELECT at.acc_trans_id
 
1236        LEFT JOIN chart c ON (at.chart_id = c.id)
 
1237        WHERE (trans_id = ?) AND (c.link LIKE '%AR_paid%')|;
 
1238   push @delete_acc_trans_ids, selectall_array_query($form, $dbh, $query, conv_i($form->{id}), conv_i($form->{id}));
 
1241     qq|SELECT at.acc_trans_id
 
1243        LEFT JOIN chart c ON (at.chart_id = c.id)
 
1244        WHERE (trans_id = ?)
 
1245          AND ((c.link = 'AR') OR (c.link LIKE '%:AR') OR (c.link LIKE 'AR:%'))
 
1246        ORDER BY at.acc_trans_id
 
1248   push @delete_acc_trans_ids, selectall_array_query($form, $dbh, $query, conv_i($form->{id}));
 
1250   if (@delete_acc_trans_ids) {
 
1251     $query = qq|DELETE FROM acc_trans WHERE acc_trans_id IN (| . join(", ", @delete_acc_trans_ids) . qq|)|;
 
1252     do_query($form, $dbh, $query);
 
1255   $main::lxdebug->leave_sub();
 
1259   $main::lxdebug->enter_sub();
 
1261   my ($self, $myconfig, $form, $locale) = @_;
 
1263   # connect to database, turn off autocommit
 
1264   my $dbh = $form->get_standard_dbh;
 
1266   my (%payments, $old_form, $row, $item, $query, %keep_vars);
 
1268   $old_form = save_form();
 
1270   # Delete all entries in acc_trans from prior payments.
 
1271   if (SL::DB::Default->get->payments_changeable != 0) {
 
1272     $self->_delete_payments($form, $dbh);
 
1275   # Save the new payments the user made before cleaning up $form.
 
1276   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 };
 
1278   # Clean up $form so that old content won't tamper the results.
 
1279   %keep_vars = map { $_, 1 } qw(login password id);
 
1280   map { delete $form->{$_} unless $keep_vars{$_} } keys %{ $form };
 
1282   # Retrieve the invoice from the database.
 
1283   $self->retrieve_invoice($myconfig, $form);
 
1285   # Set up the content of $form in the way that IS::post_invoice() expects.
 
1286   $form->{exchangerate} = $form->format_amount($myconfig, $form->{exchangerate});
 
1288   for $row (1 .. scalar @{ $form->{invoice_details} }) {
 
1289     $item = $form->{invoice_details}->[$row - 1];
 
1291     map { $item->{$_} = $form->format_amount($myconfig, $item->{$_}) } qw(qty sellprice discount);
 
1293     map { $form->{"${_}_${row}"} = $item->{$_} } keys %{ $item };
 
1296   $form->{rowcount} = scalar @{ $form->{invoice_details} };
 
1298   delete @{$form}{qw(invoice_details paidaccounts storno paid)};
 
1300   # Restore the payment options from the user input.
 
1301   map { $form->{$_} = $payments{$_} } keys %payments;
 
1303   # Get the AR accno (which is normally done by Form::create_links()).
 
1307        LEFT JOIN chart c ON (at.chart_id = c.id)
 
1308        WHERE (trans_id = ?)
 
1309          AND ((c.link = 'AR') OR (c.link LIKE '%:AR') OR (c.link LIKE 'AR:%'))
 
1310        ORDER BY at.acc_trans_id
 
1313   ($form->{AR}) = selectfirst_array_query($form, $dbh, $query, conv_i($form->{id}));
 
1315   # Post the new payments.
 
1316   $self->post_invoice($myconfig, $form, $dbh, 1);
 
1318   restore_form($old_form);
 
1320   my $rc = $dbh->commit();
 
1322   $main::lxdebug->leave_sub();
 
1327 sub process_assembly {
 
1328   $main::lxdebug->enter_sub();
 
1330   my ($dbh, $myconfig, $form, $id, $totalqty) = @_;
 
1333     qq|SELECT a.parts_id, a.qty, p.assembly, p.partnumber, p.description, p.unit,
 
1334          p.inventory_accno_id, p.income_accno_id, p.expense_accno_id
 
1336        JOIN parts p ON (a.parts_id = p.id)
 
1338   my $sth = prepare_execute_query($form, $dbh, $query, conv_i($id));
 
1340   while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
 
1344     $ref->{inventory_accno_id} *= 1;
 
1345     $ref->{expense_accno_id}   *= 1;
 
1347     # multiply by number of assemblies
 
1348     $ref->{qty} *= $totalqty;
 
1350     if ($ref->{assembly}) {
 
1351       &process_assembly($dbh, $myconfig, $form, $ref->{parts_id}, $ref->{qty});
 
1354       if ($ref->{inventory_accno_id}) {
 
1355         $allocated = &cogs($dbh, $myconfig, $form, $ref->{parts_id}, $ref->{qty});
 
1359     # save detail record for individual assembly item in invoice table
 
1361       qq|INSERT INTO invoice (trans_id, description, parts_id, qty, sellprice, fxsellprice, allocated, assemblyitem, unit)
 
1362          VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)|;
 
1363     my @values = (conv_i($form->{id}), $ref->{description}, conv_i($ref->{parts_id}), $ref->{qty}, 0, 0, $allocated, 't', $ref->{unit});
 
1364     do_query($form, $dbh, $query, @values);
 
1370   $main::lxdebug->leave_sub();
 
1374   $main::lxdebug->enter_sub();
 
1376   # adjust allocated in table invoice according to FIFO princicple
 
1377   # for a certain part with part_id $id
 
1379   my ($dbh, $myconfig, $form, $id, $totalqty, $basefactor, $row) = @_;
 
1383   $form->{taxzone_id} *=1;
 
1384   my $transdate  = $form->{invdate} ? $dbh->quote($form->{invdate}) : "current_date";
 
1385   my $taxzone_id = $form->{"taxzone_id"} * 1;
 
1387     qq|SELECT i.id, i.trans_id, i.base_qty, i.allocated, i.sellprice, i.price_factor,
 
1388          c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from AS inventory_valid,
 
1389          c2.accno AS    income_accno, c2.new_chart_id AS    income_new_chart, date($transdate) - c2.valid_from AS    income_valid,
 
1390          c3.accno AS   expense_accno, c3.new_chart_id AS   expense_new_chart, date($transdate) - c3.valid_from AS   expense_valid
 
1391        FROM invoice i, parts p
 
1392        LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id)
 
1393        LEFT JOIN chart c2 ON ((SELECT income_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c2.id)
 
1394        LEFT JOIN chart c3 ON ((select expense_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c3.id)
 
1395        WHERE (i.parts_id = p.id)
 
1396          AND (i.parts_id = ?)
 
1397          AND ((i.base_qty + i.allocated) < 0)
 
1399   my $sth = prepare_execute_query($form, $dbh, $query, conv_i($id));
 
1404 # all invoice entries of an example part:
 
1406 # id | trans_id | base_qty | allocated | sellprice | inventory_accno | income_accno | expense_accno
 
1407 # ---+----------+----------+-----------+-----------+-----------------+--------------+---------------
 
1408 #  4 |        4 |       -5 |         5 |  20.00000 | 1140            | 4400         | 5400     bought 5 for 20
 
1409 #  5 |        5 |        4 |        -4 |  50.00000 | 1140            | 4400         | 5400     sold   4 for 50
 
1410 #  6 |        6 |        1 |        -1 |  50.00000 | 1140            | 4400         | 5400     sold   1 for 50
 
1411 #  7 |        7 |       -5 |         1 |  20.00000 | 1140            | 4400         | 5400     bought 5 for 20
 
1412 #  8 |        8 |        1 |        -1 |  50.00000 | 1140            | 4400         | 5400     sold   1 for 50
 
1414 # 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
 
1415 # and all parts have been allocated
 
1417 # so transaction 8 only sees transaction 7 with unallocated parts and adjusts allocated for that transaction, before allocated was 0
 
1418 #  7 |        7 |       -5 |         1 |  20.00000 | 1140            | 4400         | 5400     bought 5 for 20
 
1420 # in this example there are still 4 unsold articles
 
1423   # search all invoice entries for the part in question, adjusting "allocated"
 
1424   # until the total number of sold parts has been reached
 
1426   # ORDER BY trans_id ensures FIFO
 
1429   while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
 
1430     if (($qty = (($ref->{base_qty} * -1) - $ref->{allocated})) > $totalqty) {
 
1434     # update allocated in invoice
 
1435     $form->update_balance($dbh, "invoice", "allocated", qq|id = $ref->{id}|, $qty);
 
1437     # total expenses and inventory
 
1438     # sellprice is the cost of the item
 
1439     my $linetotal = $form->round_amount(($ref->{sellprice} * $qty) / ( ($ref->{price_factor} || 1) * ( $basefactor || 1 )), 2);
 
1441     if ( $::instance_conf->get_inventory_system eq 'perpetual' ) {
 
1442       # Bestandsmethode: when selling parts, deduct their purchase value from the inventory account
 
1443       $ref->{expense_accno} = ($form->{"expense_accno_$row"}) ? $form->{"expense_accno_$row"} : $ref->{expense_accno};
 
1445       $form->{amount_cogs}{ $form->{id} }{ $ref->{expense_accno} } += -$linetotal;
 
1446       $form->{expense_inventory} .= " " . $ref->{expense_accno};
 
1447       $ref->{inventory_accno} = ($form->{"inventory_accno_$row"}) ? $form->{"inventory_accno_$row"} : $ref->{inventory_accno};
 
1449       $form->{amount_cogs}{ $form->{id} }{ $ref->{inventory_accno} } -= -$linetotal;
 
1450       $form->{expense_inventory} .= " " . $ref->{inventory_accno};
 
1456     last if (($totalqty -= $qty) <= 0);
 
1461   $main::lxdebug->leave_sub();
 
1466 sub reverse_invoice {
 
1467   $main::lxdebug->enter_sub();
 
1469   my ($dbh, $form) = @_;
 
1471   # reverse inventory items
 
1473     qq|SELECT i.id, i.parts_id, i.qty, i.assemblyitem, p.assembly, p.inventory_accno_id
 
1475        JOIN parts p ON (i.parts_id = p.id)
 
1476        WHERE i.trans_id = ?|;
 
1477   my $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{"id"}));
 
1479   while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
 
1481     if ($ref->{inventory_accno_id}) {
 
1482       # de-allocated purchases
 
1484         qq|SELECT i.id, i.trans_id, i.allocated
 
1486            WHERE (i.parts_id = ?) AND (i.allocated > 0)
 
1487            ORDER BY i.trans_id DESC|;
 
1488       my $sth2 = prepare_execute_query($form, $dbh, $query, conv_i($ref->{"parts_id"}));
 
1490       while (my $inhref = $sth2->fetchrow_hashref('NAME_lc')) {
 
1491         my $qty = $ref->{qty};
 
1492         if (($ref->{qty} - $inhref->{allocated}) > 0) {
 
1493           $qty = $inhref->{allocated};
 
1497         $form->update_balance($dbh, "invoice", "allocated", qq|id = $inhref->{id}|, $qty * -1);
 
1499         last if (($ref->{qty} -= $qty) <= 0);
 
1508   my @values = (conv_i($form->{id}));
 
1509   do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE trans_id = ?|, @values);
 
1510   do_query($form, $dbh, qq|DELETE FROM invoice WHERE trans_id = ?|, @values);
 
1511   do_query($form, $dbh, qq|DELETE FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|, @values);
 
1513   $main::lxdebug->leave_sub();
 
1516 sub delete_invoice {
 
1517   $main::lxdebug->enter_sub();
 
1519   my ($self, $myconfig, $form) = @_;
 
1521   # connect to database
 
1522   my $dbh = $form->get_standard_dbh;
 
1524   &reverse_invoice($dbh, $form);
 
1526   my @values = (conv_i($form->{id}));
 
1528   # Falls wir ein Storno haben, müssen zwei Felder in der stornierten Rechnung wieder
 
1529   # zurückgesetzt werden. Vgl:
 
1530   #  id | storno | storno_id |  paid   |  amount
 
1531   #----+--------+-----------+---------+-----------
 
1532   # 18 | f      |           | 0.00000 | 119.00000
 
1534   # 18 | t      |           |  119.00000 |  119.00000
 
1536   if($form->{storno}){
 
1537     # storno_id auslesen und korrigieren
 
1538     my ($invoice_id) = selectfirst_array_query($form, $dbh, qq|SELECT storno_id FROM ar WHERE id = ?|,@values);
 
1539     do_query($form, $dbh, qq|UPDATE ar SET storno = 'f', paid = 0 WHERE id = ?|, $invoice_id);
 
1542   # delete spool files
 
1543   my @spoolfiles = selectall_array_query($form, $dbh, qq|SELECT spoolfile FROM status WHERE trans_id = ?|, @values);
 
1546     qq|DELETE FROM status WHERE trans_id = ?|,
 
1547     qq|DELETE FROM periodic_invoices WHERE ar_id = ?|,
 
1548     qq|DELETE FROM ar WHERE id = ?|,
 
1551   map { do_query($form, $dbh, $_, @values) } @queries;
 
1553   my $rc = $dbh->commit;
 
1556     my $spool = $::lx_office_conf{paths}->{spool};
 
1557     map { unlink "$spool/$_" if -f "$spool/$_"; } @spoolfiles;
 
1560   $main::lxdebug->leave_sub();
 
1565 sub retrieve_invoice {
 
1566   $main::lxdebug->enter_sub();
 
1568   my ($self, $myconfig, $form) = @_;
 
1570   # connect to database
 
1571   my $dbh = $form->get_standard_dbh;
 
1573   my ($sth, $ref, $query);
 
1575   my $query_transdate = !$form->{id} ? ", current_date AS invdate" : '';
 
1579          (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
 
1580          (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id)    AS income_accno,
 
1581          (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id)   AS expense_accno,
 
1582          (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id)    AS fxgain_accno,
 
1583          (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id)    AS fxloss_accno
 
1587   $ref = selectfirst_hashref_query($form, $dbh, $query);
 
1588   map { $form->{$_} = $ref->{$_} } keys %{ $ref };
 
1591     my $id = conv_i($form->{id});
 
1594     #erweiterung um das entsprechende feld lieferscheinnummer (a.donumber) in der html-maske anzuzeigen 12.02.2009 jb
 
1598            a.invnumber, a.ordnumber, a.quonumber, a.cusordnumber,
 
1599            a.orddate, a.quodate, a.globalproject_id,
 
1600            a.transdate AS invdate, a.deliverydate, a.paid, a.storno, a.gldate,
 
1601            a.shippingpoint, a.shipvia, a.terms, a.notes, a.intnotes, a.taxzone_id,
 
1602            a.duedate, a.taxincluded, (SELECT cu.name FROM currencies cu WHERE cu.id=a.currency_id) AS currency, a.shipto_id, a.cp_id,
 
1603            a.employee_id, a.salesman_id, a.payment_id,
 
1604            a.language_id, a.delivery_customer_id, a.delivery_vendor_id, a.type,
 
1605            a.transaction_description, a.donumber, a.invnumber_for_credit_note,
 
1606            a.marge_total, a.marge_percent, a.direct_debit, a.delivery_term_id,
 
1609          LEFT JOIN employee e ON (e.id = a.employee_id)
 
1611     $ref = selectfirst_hashref_query($form, $dbh, $query, $id);
 
1612     map { $form->{$_} = $ref->{$_} } keys %{ $ref };
 
1614     $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate}, "buy");
 
1617     $query = qq|SELECT * FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|;
 
1618     $ref = selectfirst_hashref_query($form, $dbh, $query, $id);
 
1620     map { $form->{$_} = $ref->{$_} } keys %{ $ref };
 
1622     foreach my $vc (qw(customer vendor)) {
 
1623       next if !$form->{"delivery_${vc}_id"};
 
1624       ($form->{"delivery_${vc}_string"}) = selectrow_query($form, $dbh, qq|SELECT name FROM customer WHERE id = ?|, $id);
 
1627     # get printed, emailed
 
1628     $query = qq|SELECT printed, emailed, spoolfile, formname FROM status WHERE trans_id = ?|;
 
1629     $sth = prepare_execute_query($form, $dbh, $query, $id);
 
1631     while ($ref = $sth->fetchrow_hashref('NAME_lc')) {
 
1632       $form->{printed} .= "$ref->{formname} " if $ref->{printed};
 
1633       $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
 
1634       $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
 
1637     map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
 
1639     my $transdate = $form->{deliverydate} ? $dbh->quote($form->{deliverydate})
 
1640                   : $form->{invdate}      ? $dbh->quote($form->{invdate})
 
1644     my $taxzone_id = $form->{taxzone_id} *= 1;
 
1645     $taxzone_id = 0 if (0 > $taxzone_id) || (3 < $taxzone_id);
 
1647     # retrieve individual items
 
1650            c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from AS inventory_valid,
 
1651            c2.accno AS income_accno,    c2.new_chart_id AS income_new_chart,    date($transdate) - c2.valid_from as income_valid,
 
1652            c3.accno AS expense_accno,   c3.new_chart_id AS expense_new_chart,   date($transdate) - c3.valid_from AS expense_valid,
 
1655            i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.discount, i.parts_id AS id, i.unit, i.deliverydate AS reqdate,
 
1656            i.project_id, i.serialnumber, i.id AS invoice_pos, i.pricegroup_id, i.ordnumber, i.donumber, i.transdate, i.cusordnumber, i.subtotal, i.lastcost,
 
1657            i.price_factor_id, i.price_factor, i.marge_price_factor,
 
1658            p.partnumber, p.assembly, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel, p.listprice,
 
1659            pr.projectnumber, pg.partsgroup, prg.pricegroup
 
1662          LEFT JOIN parts p ON (i.parts_id = p.id)
 
1663          LEFT JOIN project pr ON (i.project_id = pr.id)
 
1664          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
1665          LEFT JOIN pricegroup prg ON (i.pricegroup_id = prg.id)
 
1667          LEFT JOIN chart c1 ON ((SELECT inventory_accno_id             FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id)
 
1668          LEFT JOIN chart c2 ON ((SELECT income_accno_id_${taxzone_id}  FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c2.id)
 
1669          LEFT JOIN chart c3 ON ((SELECT expense_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c3.id)
 
1671          WHERE (i.trans_id = ?) AND NOT (i.assemblyitem = '1') ORDER BY i.id|;
 
1673     $sth = prepare_execute_query($form, $dbh, $query, $id);
 
1675     while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
 
1676       # Retrieve custom variables.
 
1677       my $cvars = CVar->get_custom_variables(dbh        => $dbh,
 
1679                                              sub_module => 'invoice',
 
1680                                              trans_id   => $ref->{invoice_id},
 
1682       map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
 
1683       delete $ref->{invoice_id};
 
1685       map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid)) if !$ref->{"part_inventory_accno_id"};
 
1686       delete($ref->{"part_inventory_accno_id"});
 
1688       foreach my $type (qw(inventory income expense)) {
 
1689         while ($ref->{"${type}_new_chart"} && ($ref->{"${type}_valid"} >=0)) {
 
1690           my $query = qq|SELECT accno, new_chart_id, date($transdate) - valid_from FROM chart WHERE id = ?|;
 
1691           @$ref{ map $type.$_, qw(_accno _new_chart _valid) } = selectrow_query($form, $dbh, $query, $ref->{"${type}_new_chart"});
 
1695       # get tax rates and description
 
1696       my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
 
1698         qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber FROM tax t
 
1699            LEFT JOIN chart c ON (c.id = t.chart_id)
 
1701              (SELECT tk.tax_id FROM taxkeys tk
 
1702               WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?)
 
1703                 AND startdate <= date($transdate)
 
1704               ORDER BY startdate DESC LIMIT 1)
 
1706       my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
 
1707       $ref->{taxaccounts} = "";
 
1709       while (my $ptr = $stw->fetchrow_hashref('NAME_lc')) {
 
1711         if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
 
1715         $ref->{taxaccounts} .= "$ptr->{accno} ";
 
1717         if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
 
1718           $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
 
1719           $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
 
1720           $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
 
1721           $form->{taxaccounts} .= "$ptr->{accno} ";
 
1726       $ref->{qty} *= -1 if $form->{type} eq "credit_note";
 
1728       chop $ref->{taxaccounts};
 
1729       push @{ $form->{invoice_details} }, $ref;
 
1734     Common::webdav_folder($form);
 
1737   my $rc = $dbh->commit;
 
1739   $main::lxdebug->leave_sub();
 
1745   $main::lxdebug->enter_sub();
 
1747   my ($self, $myconfig, $form) = @_;
 
1749   # connect to database
 
1750   my $dbh = $form->get_standard_dbh;
 
1752   my $dateformat = $myconfig->{dateformat};
 
1753   $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
 
1755   my (@values, $duedate, $ref, $query);
 
1757   if ($form->{invdate}) {
 
1758     $duedate = "to_date(?, '$dateformat')";
 
1759     push @values, $form->{invdate};
 
1761     $duedate = "current_date";
 
1764   my $cid = conv_i($form->{customer_id});
 
1767   if ($form->{payment_id}) {
 
1768     $payment_id = "(pt.id = ?) OR";
 
1769     push @values, conv_i($form->{payment_id});
 
1775          c.id AS customer_id, c.name AS customer, c.discount as customer_discount, c.creditlimit, c.terms,
 
1776          c.email, c.cc, c.bcc, c.language_id, c.payment_id, c.delivery_term_id,
 
1777          c.street, c.zipcode, c.city, c.country,
 
1778          c.notes AS intnotes, c.klass as customer_klass, c.taxzone_id, c.salesman_id, cu.name AS curr,
 
1779          c.taxincluded_checked, c.direct_debit,
 
1780          $duedate + COALESCE(pt.terms_netto, 0) AS duedate,
 
1781          b.discount AS tradediscount, b.description AS business
 
1783        LEFT JOIN business b ON (b.id = c.business_id)
 
1784        LEFT JOIN payment_terms pt ON ($payment_id (c.payment_id = pt.id))
 
1785        LEFT JOIN currencies cu ON (c.currency_id=cu.id)
 
1788   $ref = selectfirst_hashref_query($form, $dbh, $query, @values);
 
1790   delete $ref->{salesman_id} if !$ref->{salesman_id};
 
1792   map { $form->{$_} = $ref->{$_} } keys %$ref;
 
1794   # use customer currency
 
1795   $form->{currency} = $form->{curr};
 
1798     qq|SELECT sum(amount - paid) AS dunning_amount
 
1800        WHERE (paid < amount)
 
1801          AND (customer_id = ?)
 
1802          AND (dunning_config_id IS NOT NULL)|;
 
1803   $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
 
1804   map { $form->{$_} = $ref->{$_} } keys %$ref;
 
1807     qq|SELECT dnn.dunning_description AS max_dunning_level
 
1808        FROM dunning_config dnn
 
1809        WHERE id IN (SELECT dunning_config_id
 
1811                     WHERE (paid < amount) AND (customer_id = ?) AND (dunning_config_id IS NOT NULL))
 
1812        ORDER BY dunning_level DESC LIMIT 1|;
 
1813   $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
 
1814   map { $form->{$_} = $ref->{$_} } keys %$ref;
 
1816   $form->{creditremaining} = $form->{creditlimit};
 
1817   $query = qq|SELECT SUM(amount - paid) FROM ar WHERE customer_id = ?|;
 
1818   my ($value) = selectrow_query($form, $dbh, $query, $cid);
 
1819   $form->{creditremaining} -= $value;
 
1823          (SELECT e.buy FROM exchangerate e
 
1824           WHERE e.currency_id = o.currency_id
 
1825             AND e.transdate = o.transdate)
 
1827        WHERE o.customer_id = ?
 
1828          AND o.quotation = '0'
 
1829          AND o.closed = '0'|;
 
1830   my $sth = prepare_execute_query($form, $dbh, $query, $cid);
 
1832   while (my ($amount, $exch) = $sth->fetchrow_array) {
 
1833     $exch = 1 unless $exch;
 
1834     $form->{creditremaining} -= $amount * $exch;
 
1838   # get shipto if we did not converted an order or invoice
 
1839   if (!$form->{shipto}) {
 
1840     map { delete $form->{$_} }
 
1841       qw(shiptoname shiptodepartment_1 shiptodepartment_2
 
1842          shiptostreet shiptozipcode shiptocity shiptocountry
 
1843          shiptocontact shiptophone shiptofax shiptoemail);
 
1845     $query = qq|SELECT * FROM shipto WHERE trans_id = ? AND module = 'CT'|;
 
1846     $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
 
1848     map { $form->{$_} = $ref->{$_} } keys %$ref;
 
1851   # setup last accounts used for this customer
 
1852   if (!$form->{id} && $form->{type} !~ /_(order|quotation)/) {
 
1854       qq|SELECT c.id, c.accno, c.description, c.link, c.category
 
1856          JOIN acc_trans ac ON (ac.chart_id = c.id)
 
1857          JOIN ar a ON (a.id = ac.trans_id)
 
1858          WHERE a.customer_id = ?
 
1859            AND NOT (c.link LIKE '%_tax%' OR c.link LIKE '%_paid%')
 
1860            AND a.id IN (SELECT max(a2.id) FROM ar a2 WHERE a2.customer_id = ?)|;
 
1861     $sth = prepare_execute_query($form, $dbh, $query, $cid, $cid);
 
1864     while ($ref = $sth->fetchrow_hashref('NAME_lc')) {
 
1865       if ($ref->{category} eq 'I') {
 
1867         $form->{"AR_amount_$i"} = "$ref->{accno}--$ref->{description}";
 
1869         if ($form->{initial_transdate}) {
 
1871             qq|SELECT tk.tax_id, t.rate
 
1873                LEFT JOIN tax t ON tk.tax_id = t.id
 
1874                WHERE (tk.chart_id = ?) AND (startdate <= date(?))
 
1875                ORDER BY tk.startdate DESC
 
1877           my ($tax_id, $rate) =
 
1878             selectrow_query($form, $dbh, $tax_query, $ref->{id},
 
1879                             $form->{initial_transdate});
 
1880           $form->{"taxchart_$i"} = "${tax_id}--${rate}";
 
1883       if ($ref->{category} eq 'A') {
 
1884         $form->{ARselected} = $form->{AR_1} = $ref->{accno};
 
1888     $form->{rowcount} = $i if ($i && !$form->{type});
 
1891   $main::lxdebug->leave_sub();
 
1895   $main::lxdebug->enter_sub();
 
1897   my ($self, $myconfig, $form) = @_;
 
1899   # connect to database
 
1900   my $dbh = $form->get_standard_dbh;
 
1902   my $i = $form->{rowcount};
 
1904   my $where = qq|NOT p.obsolete = '1'|;
 
1907   foreach my $column (qw(p.partnumber p.description pgpartsgroup )) {
 
1908     my ($table, $field) = split m/\./, $column;
 
1909     next if !$form->{"${field}_${i}"};
 
1910     $where .= qq| AND lower(${column}) ILIKE ?|;
 
1911     push @values, '%' . $form->{"${field}_${i}"} . '%';
 
1914   #Es soll auch nach EAN gesucht werden, ohne Einschränkung durch Beschreibung
 
1915   if ($form->{"partnumber_$i"} && !$form->{"description_$i"}) {
 
1916     $where .= qq| OR (NOT p.obsolete = '1' AND p.ean = ? )|;
 
1917     push @values, $form->{"partnumber_$i"};
 
1920   # Search for part ID overrides all other criteria.
 
1921   if ($form->{"id_${i}"}) {
 
1922     $where  = qq|p.id = ?|;
 
1923     @values = ($form->{"id_${i}"});
 
1926   if ($form->{"description_$i"}) {
 
1927     $where .= qq| ORDER BY p.description|;
 
1929     $where .= qq| ORDER BY p.partnumber|;
 
1933   if ($form->{type} eq "invoice") {
 
1935       $form->{deliverydate} ? $dbh->quote($form->{deliverydate}) :
 
1936       $form->{invdate}      ? $dbh->quote($form->{invdate}) :
 
1940       $form->{transdate}    ? $dbh->quote($form->{transdate}) :
 
1944   my $taxzone_id = $form->{taxzone_id} * 1;
 
1945   $taxzone_id = 0 if (0 > $taxzone_id) || (3 < $taxzone_id);
 
1949          p.id, p.partnumber, p.description, p.sellprice,
 
1950          p.listprice, p.inventory_accno_id, p.lastcost,
 
1952          c1.accno AS inventory_accno,
 
1953          c1.new_chart_id AS inventory_new_chart,
 
1954          date($transdate) - c1.valid_from AS inventory_valid,
 
1956          c2.accno AS income_accno,
 
1957          c2.new_chart_id AS income_new_chart,
 
1958          date($transdate)  - c2.valid_from AS income_valid,
 
1960          c3.accno AS expense_accno,
 
1961          c3.new_chart_id AS expense_new_chart,
 
1962          date($transdate) - c3.valid_from AS expense_valid,
 
1964          p.unit, p.assembly, p.onhand,
 
1965          p.notes AS partnotes, p.notes AS longdescription,
 
1966          p.not_discountable, p.formel, p.payment_id AS part_payment_id,
 
1967          p.price_factor_id, p.weight,
 
1969          pfac.factor AS price_factor,
 
1974        LEFT JOIN chart c1 ON
 
1975          ((SELECT inventory_accno_id
 
1976            FROM buchungsgruppen
 
1977            WHERE id = p.buchungsgruppen_id) = c1.id)
 
1978        LEFT JOIN chart c2 ON
 
1979          ((SELECT income_accno_id_${taxzone_id}
 
1980            FROM buchungsgruppen
 
1981            WHERE id = p.buchungsgruppen_id) = c2.id)
 
1982        LEFT JOIN chart c3 ON
 
1983          ((SELECT expense_accno_id_${taxzone_id}
 
1984            FROM buchungsgruppen
 
1985            WHERE id = p.buchungsgruppen_id) = c3.id)
 
1986        LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
 
1987        LEFT JOIN price_factors pfac ON (pfac.id = p.price_factor_id)
 
1989   my $sth = prepare_execute_query($form, $dbh, $query, @values);
 
1991   my @translation_queries = ( [ qq|SELECT tr.translation, tr.longdescription
 
1993                                    WHERE tr.language_id = ? AND tr.parts_id = ?| ],
 
1994                               [ qq|SELECT tr.translation, tr.longdescription
 
1996                                    WHERE tr.language_id IN
 
1999                                       WHERE article_code = (SELECT article_code FROM language WHERE id = ?))
 
2002   map { push @{ $_ }, prepare_query($form, $dbh, $_->[0]) } @translation_queries;
 
2004   while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
 
2006     # In der Buchungsgruppe ist immer ein Bestandskonto verknuepft, auch wenn
 
2007     # es sich um eine Dienstleistung handelt. Bei Dienstleistungen muss das
 
2008     # Buchungskonto also aus dem Ergebnis rausgenommen werden.
 
2009     if (!$ref->{inventory_accno_id}) {
 
2010       map({ delete($ref->{"inventory_${_}"}); } qw(accno new_chart valid));
 
2012     delete($ref->{inventory_accno_id});
 
2014     foreach my $type (qw(inventory income expense)) {
 
2015       while ($ref->{"${type}_new_chart"} && ($ref->{"${type}_valid"} >=0)) {
 
2017           qq|SELECT accno, new_chart_id, date($transdate) - valid_from
 
2020         ($ref->{"${type}_accno"},
 
2021          $ref->{"${type}_new_chart"},
 
2022          $ref->{"${type}_valid"})
 
2023           = selectrow_query($form, $dbh, $query, $ref->{"${type}_new_chart"});
 
2027     if ($form->{payment_id} eq "") {
 
2028       $form->{payment_id} = $form->{part_payment_id};
 
2031     # get tax rates and description
 
2032     my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
 
2034       qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber
 
2036          LEFT JOIN chart c ON (c.id = t.chart_id)
 
2040             WHERE tk.chart_id = (SELECT id from chart WHERE accno = ?)
 
2042             ORDER BY startdate DESC
 
2045     @values = ($accno_id, $transdate eq "current_date" ? "now" : $transdate);
 
2046     my $stw = $dbh->prepare($query);
 
2047     $stw->execute(@values) || $form->dberror($query);
 
2049     $ref->{taxaccounts} = "";
 
2051     while (my $ptr = $stw->fetchrow_hashref('NAME_lc')) {
 
2053       if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
 
2057       $ref->{taxaccounts} .= "$ptr->{accno} ";
 
2059       if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
 
2060         $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
 
2061         $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
 
2062         $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
 
2063         $form->{taxaccounts} .= "$ptr->{accno} ";
 
2069     chop $ref->{taxaccounts};
 
2071     if ($form->{language_id}) {
 
2072       for my $spec (@translation_queries) {
 
2073         do_statement($form, $spec->[1], $spec->[0], conv_i($form->{language_id}), conv_i($ref->{id}));
 
2074         my ($translation, $longdescription) = $spec->[1]->fetchrow_array;
 
2075         next unless $translation;
 
2076         $ref->{description} = $translation;
 
2077         $ref->{longdescription} = $longdescription;
 
2082     $ref->{onhand} *= 1;
 
2084     push @{ $form->{item_list} }, $ref;
 
2087   $_->[1]->finish for @translation_queries;
 
2089   foreach my $item (@{ $form->{item_list} }) {
 
2090     my $custom_variables = CVar->get_custom_variables(module   => 'IC',
 
2091                                                       trans_id => $item->{id},
 
2095     map { $item->{"ic_cvar_" . $_->{name} } = $_->{value} } @{ $custom_variables };
 
2098   $main::lxdebug->leave_sub();
 
2101 ##########################
 
2102 # get pricegroups from database
 
2103 # build up selected pricegroup
 
2104 # if an exchange rate - change price
 
2107 sub get_pricegroups_for_parts {
 
2109   $main::lxdebug->enter_sub();
 
2111   my ($self, $myconfig, $form) = @_;
 
2113   my $dbh = $form->get_standard_dbh;
 
2115   $form->{"PRICES"} = {};
 
2119   my $all_units = AM->retrieve_units($myconfig, $form);
 
2120   while (($form->{"id_$i"}) or ($form->{"new_id_$i"})) {
 
2121     $form->{"PRICES"}{$i} = [];
 
2123     $id = $form->{"id_$i"};
 
2125     if (!($form->{"id_$i"}) and $form->{"new_id_$i"}) {
 
2126       $id = $form->{"new_id_$i"};
 
2129     my ($price, $selectedpricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
 
2131     my $pricegroup_old = $form->{"pricegroup_old_$i"};
 
2133     # sellprice has format 13,0000 or 0,00000,  can't check for 0 numerically
 
2134     my $sellprice = $form->{"sellprice_$i"};
 
2135     my $pricegroup_id = $form->{"pricegroup_id_$i"};
 
2136     $form->{"new_pricegroup_$i"} = $selectedpricegroup_id;
 
2137     $form->{"old_pricegroup_$i"} = $pricegroup_old;
 
2139     my $price_new = $form->{"price_new_$i"};
 
2140     my $price_old = $form->{"price_old_$i"};
 
2142     if (!$form->{"unit_old_$i"}) {
 
2143       # Neue Ware aus der Datenbank. In diesem Fall ist unit_$i die
 
2144       # Einheit, wie sie in den Stammdaten hinterlegt wurde.
 
2145       # Es sollte also angenommen werden, dass diese ausgewaehlt war.
 
2146       $form->{"unit_old_$i"} = $form->{"unit_$i"};
 
2149     # Die zuletzt ausgewaehlte mit der aktuell ausgewaehlten Einheit
 
2150     # vergleichen und bei Unterschied den Preis entsprechend umrechnen.
 
2151     $form->{"selected_unit_$i"} = $form->{"unit_$i"} unless ($form->{"selected_unit_$i"});
 
2153     if (!$all_units->{$form->{"selected_unit_$i"}} ||
 
2154         ($all_units->{$form->{"selected_unit_$i"}}->{"base_unit"} ne
 
2155          $all_units->{$form->{"unit_old_$i"}}->{"base_unit"})) {
 
2156       # Die ausgewaehlte Einheit ist fuer diesen Artikel nicht gueltig
 
2157       # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber
 
2158       # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen.
 
2159       $form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"};
 
2164     if ($form->{"unit_old_$i"} ne $form->{"selected_unit_$i"}) {
 
2165       if (defined($all_units->{$form->{"unit_old_$i"}}->{"factor"}) &&
 
2166           $all_units->{$form->{"unit_old_$i"}}->{"factor"}) {
 
2167         $basefactor = $all_units->{$form->{"selected_unit_$i"}}->{"factor"} /
 
2168           $all_units->{$form->{"unit_old_$i"}}->{"factor"};
 
2172     if (!$form->{"basefactor_$i"}) {
 
2173       $form->{"basefactor_$i"} = 1;
 
2179             sellprice AS default_sellprice,
 
2182             'selected' AS selected
 
2188            parts.sellprice AS default_sellprice,
 
2189            pricegroup.pricegroup,
 
2193           LEFT JOIN parts ON parts.id = parts_id
 
2194           LEFT JOIN pricegroup ON pricegroup.id = pricegroup_id
 
2196           ORDER BY pricegroup|;
 
2197     my @values = (conv_i($id), conv_i($id));
 
2198     my $pkq = prepare_execute_query($form, $dbh, $query, @values);
 
2200     while (my $pkr = $pkq->fetchrow_hashref('NAME_lc')) {
 
2202       $pkr->{selected} = '';
 
2204       # if there is an exchange rate change price
 
2205       if (($form->{exchangerate} * 1) != 0) {
 
2206         $pkr->{price} /= $form->{exchangerate};
 
2209       $pkr->{price} *= $form->{"basefactor_$i"};
 
2210       $pkr->{price} *= $basefactor;
 
2211       $pkr->{price_ufmt} = $pkr->{price};
 
2212       $pkr->{price} = $form->format_amount($myconfig, $pkr->{price}, 5);
 
2214       if (!defined $selectedpricegroup_id) {
 
2215         # new entries in article list, either old invoice was loaded (edit) or a new article was added
 
2216         # Case A: open old invoice, no pricegroup selected
 
2217         # Case B: add new article to invoice, no pricegroup selected
 
2219         # to distinguish case A and B the variable pricegroup_id_$i is used
 
2220         # for new articles this variable isn't defined, for loaded articles it is
 
2221         # sellprice can't be used, as it already has 0,00 set
 
2223         if ($pkr->{pricegroup_id} eq $form->{"pricegroup_id_$i"} and defined $form->{"pricegroup_id_$i"}) {
 
2225           $pkr->{selected}  = ' selected';
 
2226         } elsif ($pkr->{pricegroup_id} eq $form->{customer_klass}
 
2227                  and not defined $form->{"pricegroup_id_$i"}
 
2228                  and $pkr->{price_ufmt} != 0    # only use customer pricegroup price if it has a value, else use default_sellprice
 
2229                                                 # for the case where pricegroup prices haven't been set
 
2231           # Case B: use default pricegroup of customer
 
2233           $pkr->{selected}  = ' selected'; # unless $form->{selected};
 
2234           # no customer pricesgroup set
 
2235           if ($pkr->{price_ufmt} == $pkr->{default_sellprice}) {
 
2237             $pkr->{price} = $form->{"sellprice_$i"};
 
2241 # this sub should not set anything and only return. --sschoeling, 20090506
 
2242 # is this correct? put in again... -- grichardson 20110119
 
2243             $form->{"sellprice_$i"} = $pkr->{price};
 
2246         } elsif ($pkr->{price_ufmt} == $pkr->{default_sellprice} and $pkr->{default_sellprice} != 0) {
 
2247           $pkr->{price}    = $form->{"sellprice_$i"};
 
2248           $pkr->{selected} = ' selected';
 
2252       # existing article: pricegroup or price changed
 
2253       if ($selectedpricegroup_id or $selectedpricegroup_id == 0) {
 
2254         if ($selectedpricegroup_id ne $pricegroup_old) {
 
2255           # pricegroup has changed
 
2256           if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
 
2257             $pkr->{selected}  = ' selected';
 
2259         } elsif ( ($form->parse_amount($myconfig, $price_new)
 
2260                  != $form->parse_amount($myconfig, $form->{"sellprice_$i"}))
 
2261                   and ($price_new ne 0) and defined $price_new) {
 
2262           # sellprice has changed
 
2263           # when loading existing invoices $price_new is NULL
 
2264           if ($pkr->{pricegroup_id} == 0) {
 
2265             $pkr->{price}     = $form->{"sellprice_$i"};
 
2266             $pkr->{selected}  = ' selected';
 
2268         } elsif ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
 
2269           # neither sellprice nor pricegroup changed
 
2270           $pkr->{selected}  = ' selected';
 
2271           if (    ($pkr->{pricegroup_id} == 0) and ($pkr->{price} == $form->{"sellprice_$i"})) {
 
2272             # $pkr->{price}                         = $form->{"sellprice_$i"};
 
2274             $pkr->{price} = $form->{"sellprice_$i"};
 
2278       push @{ $form->{PRICES}{$i} }, $pkr;
 
2281     $form->{"basefactor_$i"} *= $basefactor;
 
2288   $main::lxdebug->leave_sub();
 
2292   $main::lxdebug->enter_sub();
 
2294   my ($self, $myconfig, $form, $table) = @_;
 
2296   $main::lxdebug->leave_sub() and return 0 unless ($form->{id});
 
2298   # make sure there's no funny stuff in $table
 
2299   # ToDO: die when this happens and throw an error
 
2300   $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
 
2302   my $dbh = $form->get_standard_dbh;
 
2304   my $query = qq|SELECT storno FROM $table WHERE storno_id = ?|;
 
2305   my ($result) = selectrow_query($form, $dbh, $query, $form->{id});
 
2307   $main::lxdebug->leave_sub();
 
2313   $main::lxdebug->enter_sub();
 
2315   my ($self, $myconfig, $form, $table, $id) = @_;
 
2317   $main::lxdebug->leave_sub() and return 0 unless ($id);
 
2319   # make sure there's no funny stuff in $table
 
2320   # ToDO: die when this happens and throw an error
 
2321   $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
 
2323   my $dbh = $form->get_standard_dbh;
 
2325   my $query = qq|SELECT storno FROM $table WHERE id = ?|;
 
2326   my ($result) = selectrow_query($form, $dbh, $query, $id);
 
2328   $main::lxdebug->leave_sub();
 
2333 sub get_standard_accno_current_assets {
 
2334   $main::lxdebug->enter_sub();
 
2336   my ($self, $myconfig, $form) = @_;
 
2338   my $dbh = $form->get_standard_dbh;
 
2340   my $query = qq| SELECT accno FROM chart WHERE id = (SELECT ar_paid_accno_id FROM defaults)|;
 
2341   my ($result) = selectrow_query($form, $dbh, $query);
 
2343   $main::lxdebug->leave_sub();