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) 1999-2003
 
  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 #======================================================================
 
  33 #======================================================================
 
  37 use List::Util qw(max first);
 
  48 OE.pm - Order entry module
 
  52 OE.pm is part of the OE module. OE is responsible for sales and purchase orders, as well as sales quotations and purchase requests. This file abstracts the database tables C<oe> and C<orderitems>.
 
  61   $main::lxdebug->enter_sub();
 
  63   my ($self, $myconfig, $form) = @_;
 
  66   my $dbh = $form->dbconnect($myconfig);
 
  69   my $ordnumber = 'ordnumber';
 
  75   my $rate = ($form->{vc} eq 'customer') ? 'buy' : 'sell';
 
  77   if ($form->{type} =~ /_quotation$/) {
 
  79     $ordnumber = 'quonumber';
 
  82   my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
 
  85     qq|SELECT o.id, o.ordnumber, o.transdate, o.reqdate, | .
 
  86     qq|  o.amount, ct.name, o.netamount, o.${vc}_id, o.globalproject_id, | .
 
  87     qq|  o.closed, o.delivered, o.quonumber, o.shippingpoint, o.shipvia, | .
 
  88     qq|  o.transaction_description, | .
 
  89     qq|  o.marge_total, o.marge_percent, | .
 
  90     qq|  ex.$rate AS exchangerate, | .
 
  91     qq|  pr.projectnumber AS globalprojectnumber, | .
 
  92     qq|  e.name AS employee, s.name AS salesman, | .
 
  93     qq|  ct.country, ct.ustid  | .
 
  95     qq|JOIN $vc ct ON (o.${vc}_id = ct.id) | .
 
  96     qq|LEFT JOIN employee e ON (o.employee_id = e.id) | .
 
  97     qq|LEFT JOIN employee s ON (o.salesman_id = s.id) | .
 
  98     qq|LEFT JOIN exchangerate ex ON (ex.curr = o.curr | .
 
  99     qq|  AND ex.transdate = o.transdate) | .
 
 100     qq|LEFT JOIN project pr ON (o.globalproject_id = pr.id) | .
 
 101     qq|WHERE (o.quotation = ?) |;
 
 102   push(@values, $quotation);
 
 104   my ($null, $split_department_id) = split /--/, $form->{department};
 
 105   my $department_id = $form->{department_id} || $split_department_id;
 
 106   if ($department_id) {
 
 107     $query .= qq| AND o.department_id = ?|;
 
 108     push(@values, $department_id);
 
 111   if ($form->{"project_id"}) {
 
 113       qq|AND ((globalproject_id = ?) OR EXISTS | .
 
 114       qq|  (SELECT * FROM orderitems oi | .
 
 115       qq|   WHERE oi.project_id = ? AND oi.trans_id = o.id))|;
 
 116     push(@values, conv_i($form->{"project_id"}), conv_i($form->{"project_id"}));
 
 119   if ($form->{"projectnumber"}) {
 
 121       AND (pr.projectnumber ILIKE ?) OR EXISTS (
 
 122         SELECT * FROM orderitems oi
 
 123         LEFT JOIN project proi ON proi.id = oi.project_id
 
 124         WHERE proi.projectnumber ILIKE ? AND oi.trans_id = o.id
 
 127     push @values, "%" . $form->{"projectnumber"} . "%", "%" . $form->{"projectnumber"} . "%" ;
 
 130   if ($form->{"${vc}_id"}) {
 
 131     $query .= " AND o.${vc}_id = ?";
 
 132     push(@values, $form->{"${vc}_id"});
 
 134   } elsif ($form->{$vc}) {
 
 135     $query .= " AND ct.name ILIKE ?";
 
 136     push(@values, '%' . $form->{$vc} . '%');
 
 139   if ($form->{employee_id}) {
 
 140     $query .= " AND o.employee_id = ?";
 
 141     push @values, conv_i($form->{employee_id});
 
 144   if ($form->{salesman_id}) {
 
 145     $query .= " AND o.salesman_id = ?";
 
 146     push @values, conv_i($form->{salesman_id});
 
 149   if (!$form->{open} && !$form->{closed}) {
 
 150     $query .= " AND o.id = 0";
 
 151   } elsif (!($form->{open} && $form->{closed})) {
 
 152     $query .= ($form->{open}) ? " AND o.closed = '0'" : " AND o.closed = '1'";
 
 155   if (($form->{"notdelivered"} || $form->{"delivered"}) &&
 
 156       ($form->{"notdelivered"} ne $form->{"delivered"})) {
 
 157     $query .= $form->{"delivered"} ?
 
 158       " AND o.delivered " : " AND NOT o.delivered";
 
 161   if ($form->{$ordnumber}) {
 
 162     $query .= qq| AND o.$ordnumber ILIKE ?|;
 
 163     push(@values, '%' . $form->{$ordnumber} . '%');
 
 166   if($form->{transdatefrom}) {
 
 167     $query .= qq| AND o.transdate >= ?|;
 
 168     push(@values, conv_date($form->{transdatefrom}));
 
 171   if($form->{transdateto}) {
 
 172     $query .= qq| AND o.transdate <= ?|;
 
 173     push(@values, conv_date($form->{transdateto}));
 
 176   if($form->{reqdatefrom}) {
 
 177     $query .= qq| AND o.reqdate >= ?|;
 
 178     push(@values, conv_date($form->{reqdatefrom}));
 
 181   if($form->{reqdateto}) {
 
 182     $query .= qq| AND o.reqdate <= ?|;
 
 183     push(@values, conv_date($form->{reqdateto}));
 
 186   if ($form->{transaction_description}) {
 
 187     $query .= qq| AND o.transaction_description ILIKE ?|;
 
 188     push(@values, '%' . $form->{transaction_description} . '%');
 
 191   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
 
 192   my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name")));
 
 193   my %allowed_sort_columns = (
 
 194     "transdate"               => "o.transdate",
 
 195     "reqdate"                 => "o.reqdate",
 
 197     "ordnumber"               => "o.ordnumber",
 
 198     "quonumber"               => "o.quonumber",
 
 200     "employee"                => "e.name",
 
 201     "salesman"                => "e.name",
 
 202     "shipvia"                 => "o.shipvia",
 
 203     "transaction_description" => "o.transaction_description"
 
 205   if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
 
 206     $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}";
 
 208   $query .= qq| ORDER by | . $sortorder;
 
 210   my $sth = $dbh->prepare($query);
 
 211   $sth->execute(@values) ||
 
 212     $form->dberror($query . " (" . join(", ", @values) . ")");
 
 216   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 217     $ref->{exchangerate} = 1 unless $ref->{exchangerate};
 
 218     push @{ $form->{OE} }, $ref if $ref->{id} != $id{ $ref->{id} };
 
 219     $id{ $ref->{id} } = $ref->{id};
 
 225   $main::lxdebug->leave_sub();
 
 228 sub transactions_for_todo_list {
 
 229   $main::lxdebug->enter_sub();
 
 234   my $myconfig = \%main::myconfig;
 
 235   my $form     = $main::form;
 
 237   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
 
 239   my $query    = qq|SELECT id FROM employee WHERE login = ?|;
 
 240   my ($e_id)   = selectrow_query($form, $dbh, $query, $form->{login});
 
 243     qq|SELECT oe.id, oe.transdate, oe.reqdate, oe.quonumber, oe.transaction_description, oe.amount,
 
 244          CASE WHEN (COALESCE(oe.customer_id, 0) = 0) THEN 'vendor' ELSE 'customer' END AS vc,
 
 249        LEFT JOIN customer c ON (oe.customer_id = c.id)
 
 250        LEFT JOIN vendor v   ON (oe.vendor_id   = v.id)
 
 251        LEFT JOIN employee e ON (oe.employee_id = e.id)
 
 252        WHERE (COALESCE(quotation, FALSE) = TRUE)
 
 253          AND (COALESCE(closed,    FALSE) = FALSE)
 
 254          AND ((oe.employee_id = ?) OR (oe.salesman_id = ?))
 
 255          AND NOT (oe.reqdate ISNULL)
 
 256          AND (oe.reqdate < current_date)
 
 259   my $quotations = selectall_hashref_query($form, $dbh, $query, $e_id, $e_id);
 
 261   $main::lxdebug->leave_sub();
 
 267   $main::lxdebug->enter_sub();
 
 269   my ($self, $myconfig, $form) = @_;
 
 271   # connect to database, turn off autocommit
 
 272   my $dbh = $form->dbconnect_noauto($myconfig);
 
 274   my ($query, @values, $sth, $null);
 
 275   my $exchangerate = 0;
 
 277   my $all_units = AM->retrieve_units($myconfig, $form);
 
 278   $form->{all_units} = $all_units;
 
 280   my $ic_cvar_configs = CVar->get_configs(module => 'IC',
 
 283   $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id};
 
 284   unless ($form->{employee_id}) {
 
 285     $form->get_employee($dbh);
 
 288   my $ml = ($form->{type} eq 'sales_order') ? 1 : -1;
 
 291     $query = qq|DELETE FROM custom_variables
 
 292                 WHERE (config_id IN (SELECT id FROM custom_variable_configs WHERE module = 'IC'))
 
 293                   AND (sub_module = 'orderitems')
 
 294                   AND (trans_id IN (SELECT id FROM orderitems WHERE trans_id = ?))|;
 
 295     do_query($form, $dbh, $query, $form->{id});
 
 297     $query = qq|DELETE FROM orderitems WHERE trans_id = ?|;
 
 298     do_query($form, $dbh, $query, $form->{id});
 
 300     $query = qq|DELETE FROM shipto | .
 
 301              qq|WHERE trans_id = ? AND module = 'OE'|;
 
 302     do_query($form, $dbh, $query, $form->{id});
 
 306     $query = qq|SELECT nextval('id')|;
 
 307     ($form->{id}) = selectrow_query($form, $dbh, $query);
 
 309     $query = qq|INSERT INTO oe (id, ordnumber, employee_id) VALUES (?, '', ?)|;
 
 310     do_query($form, $dbh, $query, $form->{id}, $form->{employee_id});
 
 328   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
 
 329   my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
 
 332   for my $i (1 .. $form->{rowcount}) {
 
 334     map({ $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) } qw(qty ship));
 
 336     if ($form->{"id_$i"}) {
 
 339       $query = qq|SELECT unit FROM parts WHERE id = ?|;
 
 340       my ($item_unit) = selectrow_query($form, $dbh, $query, $form->{"id_$i"});
 
 343       if (defined($all_units->{$item_unit}->{factor}) &&
 
 344           (($all_units->{$item_unit}->{factor} * 1) != 0)) {
 
 345         $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
 
 347       my $baseqty = $form->{"qty_$i"} * $basefactor;
 
 349       $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
 
 350       $form->{"marge_total_$i"} = $form->parse_amount($myconfig, $form->{"marge_total_$i"}) * 1;
 
 351       $form->{"lastcost_$i"} = $form->{"lastcost_$i"} * 1;
 
 353       # set values to 0 if nothing entered
 
 354       $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
 
 356       $form->{"sellprice_$i"} = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
 357       $fxsellprice = $form->{"sellprice_$i"};
 
 359       my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
 361       my $decimalplaces = ($dec > 2) ? $dec : 2;
 
 363       $discount = $form->round_amount($form->{"sellprice_$i"} * $form->{"discount_$i"}, $decimalplaces);
 
 364       $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"} - $discount, $decimalplaces);
 
 366       $form->{"inventory_accno_$i"} *= 1;
 
 367       $form->{"expense_accno_$i"}   *= 1;
 
 369       $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
 
 370       $linetotal    = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
 
 372       @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
 
 376       map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
 
 378       if ($form->{taxincluded}) {
 
 379         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
 
 380         $taxbase   = $linetotal - $taxamount;
 
 382         # we are not keeping a natural price, do not round
 
 383         $form->{"sellprice_$i"} =
 
 384           $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
 
 386         $taxamount = $linetotal * $taxrate;
 
 387         $taxbase   = $linetotal;
 
 390       if ($form->round_amount($taxrate, 7) == 0) {
 
 391         if ($form->{taxincluded}) {
 
 392           foreach my $item (@taxaccounts) {
 
 393             $taxamount = $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
 
 394             $taxaccounts{$item} += $taxamount;
 
 395             $taxdiff            += $taxamount;
 
 396             $taxbase{$item}     += $taxbase;
 
 398           $taxaccounts{ $taxaccounts[0] } += $taxdiff;
 
 400           foreach my $item (@taxaccounts) {
 
 401             $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
 
 402             $taxbase{$item}     += $taxbase;
 
 406         foreach my $item (@taxaccounts) {
 
 407           $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
 
 408           $taxbase{$item} += $taxbase;
 
 412       $netamount += $form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor;
 
 414       $reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef;
 
 416       # get pricegroup_id and save ist
 
 417       ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
 
 420       # save detail record in orderitems table
 
 421       my $orderitems_id = $form->{"orderitems_id_$i"};
 
 422       ($orderitems_id)  = selectfirst_array_query($form, $dbh, qq|SELECT nextval('orderitemsid')|) if (!$orderitems_id);
 
 425       $query = qq|INSERT INTO orderitems (
 
 426                     id, trans_id, parts_id, description, longdescription, qty, base_qty,
 
 427                     sellprice, discount, unit, reqdate, project_id, serialnumber, ship,
 
 428                     pricegroup_id, ordnumber, transdate, cusordnumber, subtotal,
 
 429                     marge_percent, marge_total, lastcost, price_factor_id, price_factor, marge_price_factor)
 
 430                   VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
 
 431                           (SELECT factor FROM price_factors WHERE id = ?), ?)|;
 
 433            conv_i($orderitems_id), conv_i($form->{id}), conv_i($form->{"id_$i"}),
 
 434            $form->{"description_$i"}, $form->{"longdescription_$i"},
 
 435            $form->{"qty_$i"}, $baseqty,
 
 436            $fxsellprice, $form->{"discount_$i"},
 
 437            $form->{"unit_$i"}, conv_date($reqdate), conv_i($form->{"project_id_$i"}),
 
 438            $form->{"serialnumber_$i"}, $form->{"ship_$i"}, conv_i($pricegroup_id),
 
 439            $form->{"ordnumber_$i"}, conv_date($form->{"transdate_$i"}),
 
 440            $form->{"cusordnumber_$i"}, $form->{"subtotal_$i"} ? 't' : 'f',
 
 441            $form->{"marge_percent_$i"}, $form->{"marge_total_$i"},
 
 442            $form->{"lastcost_$i"},
 
 443            conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
 
 444            conv_i($form->{"marge_price_factor_$i"}));
 
 445       do_query($form, $dbh, $query, @values);
 
 447       $form->{"sellprice_$i"} = $fxsellprice;
 
 448       $form->{"discount_$i"} *= 100;
 
 450       CVar->save_custom_variables(module       => 'IC',
 
 451                                   sub_module   => 'orderitems',
 
 452                                   trans_id     => $orderitems_id,
 
 453                                   configs      => $ic_cvar_configs,
 
 455                                   name_prefix  => 'ic_',
 
 456                                   name_postfix => "_$i",
 
 461   $reqdate = ($form->{reqdate}) ? $form->{reqdate} : undef;
 
 465   map { $tax += $form->round_amount($taxaccounts{$_}, 2) } keys %taxaccounts;
 
 467   $amount = $form->round_amount($netamount + $tax, 2);
 
 468   $netamount = $form->round_amount($netamount, 2);
 
 470   if ($form->{currency} eq $form->{defaultcurrency}) {
 
 471     $form->{exchangerate} = 1;
 
 473     $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? 'buy' : 'sell');
 
 476   $form->{exchangerate} = $exchangerate || $form->parse_amount($myconfig, $form->{exchangerate});
 
 478   my $quotation = $form->{type} =~ /_order$/ ? 'f' : 't';
 
 480   ($null, $form->{department_id}) = split(/--/, $form->{department}) if $form->{department};
 
 485          ordnumber = ?, quonumber = ?, cusordnumber = ?, transdate = ?, vendor_id = ?,
 
 486          customer_id = ?, amount = ?, netamount = ?, reqdate = ?, taxincluded = ?,
 
 487          shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, curr = ?, closed = ?,
 
 488          delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
 
 489          taxzone_id = ?, shipto_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,
 
 490          globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
 
 493   @values = ($form->{ordnumber} || '', $form->{quonumber},
 
 494              $form->{cusordnumber}, conv_date($form->{transdate}),
 
 495              conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
 
 496              $amount, $netamount, conv_date($reqdate),
 
 497              $form->{taxincluded} ? 't' : 'f', $form->{shippingpoint},
 
 498              $form->{shipvia}, $form->{notes}, $form->{intnotes},
 
 499              substr($form->{currency}, 0, 3), $form->{closed} ? 't' : 'f',
 
 500              $form->{delivered} ? "t" : "f", $form->{proforma} ? 't' : 'f',
 
 501              $quotation, conv_i($form->{department_id}),
 
 502              conv_i($form->{language_id}), conv_i($form->{taxzone_id}),
 
 503              conv_i($form->{shipto_id}), conv_i($form->{payment_id}),
 
 504              conv_i($form->{delivery_vendor_id}),
 
 505              conv_i($form->{delivery_customer_id}),
 
 506              conv_i($form->{globalproject_id}), conv_i($form->{employee_id}),
 
 507              conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
 
 508              $form->{transaction_description},
 
 509              $form->{marge_total} * 1, $form->{marge_percent} * 1,
 
 510              conv_i($form->{id}));
 
 511   do_query($form, $dbh, $query, @values);
 
 513   $form->{ordtotal} = $amount;
 
 516   $form->{name} = $form->{ $form->{vc} };
 
 517   $form->{name} =~ s/--\Q$form->{"$form->{vc}_id"}\E//;
 
 519   if (!$form->{shipto_id}) {
 
 520     $form->add_shipto($dbh, $form->{id}, "OE");
 
 523   # save printed, emailed, queued
 
 524   $form->save_status($dbh);
 
 526   # Link this record to the records it was created from.
 
 527   $form->{convert_from_oe_ids} =~ s/^\s+//;
 
 528   $form->{convert_from_oe_ids} =~ s/\s+$//;
 
 529   my @convert_from_oe_ids      =  split m/\s+/, $form->{convert_from_oe_ids};
 
 530   delete $form->{convert_from_oe_ids};
 
 532   if (scalar @convert_from_oe_ids) {
 
 533     RecordLinks->create_links('dbh'        => $dbh,
 
 535                               'from_table' => 'oe',
 
 536                               'from_ids'   => \@convert_from_oe_ids,
 
 538                               'to_id'      => $form->{id},
 
 541     $self->_close_quotations_rfqs('dbh'     => $dbh,
 
 542                                   'from_id' => \@convert_from_oe_ids,
 
 543                                   'to_id'   => $form->{id});
 
 546   if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
 
 547     if ($form->{vc} eq 'customer') {
 
 548       $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
 
 550     if ($form->{vc} eq 'vendor') {
 
 551       $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
 
 555   $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ?
 
 556                                        "quonumber" : "ordnumber"};
 
 558   Common::webdav_folder($form) if ($main::webdav);
 
 560   my $rc = $dbh->commit;
 
 563   $main::lxdebug->leave_sub();
 
 568 sub _close_quotations_rfqs {
 
 569   $main::lxdebug->enter_sub();
 
 574   Common::check_params(\%params, qw(from_id to_id));
 
 576   my $myconfig = \%main::myconfig;
 
 577   my $form     = $main::form;
 
 579   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
 
 581   my $query    = qq|SELECT quotation FROM oe WHERE id = ?|;
 
 582   my $sth      = prepare_query($form, $dbh, $query);
 
 584   do_statement($form, $sth, $query, conv_i($params{to_id}));
 
 586   my ($quotation) = $sth->fetchrow_array();
 
 589     $main::lxdebug->leave_sub();
 
 595   foreach my $from_id (@{ $params{from_id} }) {
 
 596     $from_id = conv_i($from_id);
 
 597     do_statement($form, $sth, $query, $from_id);
 
 598     ($quotation) = $sth->fetchrow_array();
 
 599     push @close_ids, $from_id if ($quotation);
 
 604   if (scalar @close_ids) {
 
 605     $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_ids) . qq|)|;
 
 606     do_query($form, $dbh, $query, @close_ids);
 
 608     $dbh->commit() unless ($params{dbh});
 
 611   $main::lxdebug->leave_sub();
 
 615   $main::lxdebug->enter_sub();
 
 617   my ($self, $myconfig, $form, $spool) = @_;
 
 619   # connect to database
 
 620   my $dbh = $form->dbconnect_noauto($myconfig);
 
 623   my $query = qq|SELECT s.spoolfile FROM status s | .
 
 624               qq|WHERE s.trans_id = ?|;
 
 625   my @values = (conv_i($form->{id}));
 
 626   my $sth = $dbh->prepare($query);
 
 627   $sth->execute(@values) || $self->dberror($query);
 
 632   while (($spoolfile) = $sth->fetchrow_array) {
 
 633     push @spoolfiles, $spoolfile;
 
 638   @values = (conv_i($form->{id}));
 
 640   # delete status entries
 
 641   $query = qq|DELETE FROM status | .
 
 642            qq|WHERE trans_id = ?|;
 
 643   do_query($form, $dbh, $query, @values);
 
 646   $query = qq|DELETE FROM oe | .
 
 648   do_query($form, $dbh, $query, @values);
 
 650   # delete individual entries
 
 651   $query = qq|DELETE FROM orderitems | .
 
 652            qq|WHERE trans_id = ?|;
 
 653   do_query($form, $dbh, $query, @values);
 
 655   $query = qq|DELETE FROM shipto | .
 
 656            qq|WHERE trans_id = ? AND module = 'OE'|;
 
 657   do_query($form, $dbh, $query, @values);
 
 659   my $rc = $dbh->commit;
 
 663     foreach $spoolfile (@spoolfiles) {
 
 664       unlink "$spool/$spoolfile" if $spoolfile;
 
 668   $main::lxdebug->leave_sub();
 
 674   $main::lxdebug->enter_sub();
 
 676   my ($self, $myconfig, $form) = @_;
 
 678   # connect to database
 
 679   my $dbh = $form->dbconnect_noauto($myconfig);
 
 681   my ($query, $query_add, @values, @ids, $sth);
 
 683   my $ic_cvar_configs = CVar->get_configs(module => 'IC',
 
 686   # translate the ids (given by id_# and trans_id_#) into one array of ids, so we can join them later
 
 688     push @ids, $form->{"trans_id_$_"}
 
 689       if ($form->{"multi_id_$_"} and $form->{"trans_id_$_"})
 
 690   } (1 .. $form->{"rowcount"});
 
 692   if ($form->{rowcount} && scalar @ids) {
 
 693     $form->{convert_from_oe_ids} = join ' ', @ids;
 
 696   # if called in multi id mode, and still only got one id, switch back to single id
 
 697   if ($form->{"rowcount"} and $#ids == 0) {
 
 698     $form->{"id"} = $ids[0];
 
 702   # and remember for the rest of the function
 
 703   my $is_collective_order = scalar @ids;
 
 706     my $wday         = (localtime(time))[6];
 
 707     my $next_workday = $wday == 5 ? 3 : $wday == 6 ? 2 : 1;
 
 708     $query_add       = qq|, current_date AS transdate, date(current_date + interval '${next_workday} days') AS reqdate|;
 
 711   # get default accounts
 
 712   $query = qq|SELECT (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
 
 713                      (SELECT c.accno FROM chart c WHERE d.income_accno_id    = c.id) AS income_accno,
 
 714                      (SELECT c.accno FROM chart c WHERE d.expense_accno_id   = c.id) AS expense_accno,
 
 715                      (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id    = c.id) AS fxgain_accno,
 
 716                      (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id    = c.id) AS fxloss_accno,
 
 720   my $ref = selectfirst_hashref_query($form, $dbh, $query);
 
 721   map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 723   ($form->{currency}) = split(/:/, $form->{currencies}) unless ($form->{currency});
 
 725   # set reqdate if this is an invoice->order conversion. If someone knows a better check to ensure
 
 726   # we come from invoices, feel free.
 
 727   $form->{reqdate} = $form->{deliverydate}
 
 728     if (    $form->{deliverydate}
 
 729         and $form->{callback} =~ /action=ar_transactions/);
 
 731   my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
 
 733   if ($form->{id} or @ids) {
 
 735     # retrieve order for single id
 
 736     # NOTE: this query is intended to fetch all information only ONCE.
 
 737     # so if any of these infos is important (or even different) for any item,
 
 738     # it will be killed out and then has to be fetched from the item scope query further down
 
 740       qq|SELECT o.cp_id, o.ordnumber, o.transdate, o.reqdate,
 
 741            o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
 
 742            o.curr AS currency, e.name AS employee, o.employee_id, o.salesman_id,
 
 743            o.${vc}_id, cv.name AS ${vc}, o.amount AS invtotal,
 
 744            o.closed, o.reqdate, o.quonumber, o.department_id, o.cusordnumber,
 
 745            d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
 
 746            o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id,
 
 747            o.globalproject_id, o.delivered, o.transaction_description
 
 749          JOIN ${vc} cv ON (o.${vc}_id = cv.id)
 
 750          LEFT JOIN employee e ON (o.employee_id = e.id)
 
 751          LEFT JOIN department d ON (o.department_id = d.id) | .
 
 754          : "WHERE o.id IN (" . join(', ', map("? ", @ids)) . ")"
 
 756     @values = $form->{id} ? ($form->{id}) : @ids;
 
 757     $sth = prepare_execute_query($form, $dbh, $query, @values);
 
 759     $ref = $sth->fetchrow_hashref("NAME_lc");
 
 760     map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 762     $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ?
 
 763                                          "quonumber" : "ordnumber"};
 
 765     # set all entries for multiple ids blank that yield different information
 
 766     while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 767       map { $form->{$_} = '' if ($ref->{$_} ne $form->{$_}) } keys %$ref;
 
 770     # if not given, fill transdate with current_date
 
 771     $form->{transdate} = $form->current_date($myconfig)
 
 772       unless $form->{transdate};
 
 776     if ($form->{delivery_customer_id}) {
 
 777       $query = qq|SELECT name FROM customer WHERE id = ?|;
 
 778       ($form->{delivery_customer_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_customer_id});
 
 781     if ($form->{delivery_vendor_id}) {
 
 782       $query = qq|SELECT name FROM customer WHERE id = ?|;
 
 783       ($form->{delivery_vendor_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_vendor_id});
 
 786     # shipto and pinted/mailed/queued status makes only sense for single id retrieve
 
 788       $query = qq|SELECT s.* FROM shipto s WHERE s.trans_id = ? AND s.module = 'OE'|;
 
 789       $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
 
 791       $ref = $sth->fetchrow_hashref("NAME_lc");
 
 793       map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 796       # get printed, emailed and queued
 
 797       $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
 
 798       $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
 
 800       while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 801         $form->{printed} .= "$ref->{formname} " if $ref->{printed};
 
 802         $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
 
 803         $form->{queued}  .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
 
 806       map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
 
 809     my %oid = ('Pg'     => 'oid',
 
 810                'Oracle' => 'rowid');
 
 812     my $transdate = $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
 
 814     $form->{taxzone_id} = 0 unless ($form->{taxzone_id});
 
 816     # retrieve individual items
 
 817     # this query looks up all information about the items
 
 818     # stuff different from the whole will not be overwritten, but saved with a suffix.
 
 820       qq|SELECT o.id AS orderitems_id,
 
 821            c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from as inventory_valid,
 
 822            c2.accno AS income_accno,    c2.new_chart_id AS income_new_chart,    date($transdate) - c2.valid_from as income_valid,
 
 823            c3.accno AS expense_accno,   c3.new_chart_id AS expense_new_chart,   date($transdate) - c3.valid_from as expense_valid,
 
 824            oe.ordnumber AS ordnumber_oe, oe.transdate AS transdate_oe, oe.cusordnumber AS cusordnumber_oe,
 
 825            p.partnumber, p.assembly, o.description, o.qty,
 
 826            o.sellprice, o.parts_id AS id, o.unit, o.discount, p.bin, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id,
 
 827            o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
 
 828            o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
 
 829            o.price_factor_id, o.price_factor, o.marge_price_factor,
 
 830            pr.projectnumber, p.formel,
 
 831            pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
 
 833          JOIN parts p ON (o.parts_id = p.id)
 
 834          JOIN oe ON (o.trans_id = oe.id)
 
 835          LEFT JOIN chart c1 ON ((SELECT inventory_accno_id                   FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c1.id)
 
 836          LEFT JOIN chart c2 ON ((SELECT income_accno_id_$form->{taxzone_id}  FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c2.id)
 
 837          LEFT JOIN chart c3 ON ((SELECT expense_accno_id_$form->{taxzone_id} FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c3.id)
 
 838          LEFT JOIN project pr ON (o.project_id = pr.id)
 
 839          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
 
 841        ? qq|WHERE o.trans_id = ?|
 
 842        : qq|WHERE o.trans_id IN (| . join(", ", map("?", @ids)) . qq|)|) .
 
 843       qq|ORDER BY o.$oid{$myconfig->{dbdriver}}|;
 
 845     @ids = $form->{id} ? ($form->{id}) : @ids;
 
 846     $sth = prepare_execute_query($form, $dbh, $query, @values);
 
 848     while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 849       # Retrieve custom variables.
 
 850       my $cvars = CVar->get_custom_variables(dbh        => $dbh,
 
 852                                              sub_module => 'orderitems',
 
 853                                              trans_id   => $ref->{orderitems_id},
 
 855       map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
 
 858       if (!$ref->{"part_inventory_accno_id"}) {
 
 859         map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid));
 
 861       delete($ref->{"part_inventory_accno_id"});
 
 863       # in collective order, copy global ordnumber, transdate, cusordnumber into item scope
 
 864       #   unless already present there
 
 865       # remove _oe entries afterwards
 
 866       map { $ref->{$_} = $ref->{"${_}_oe"} if ($ref->{$_} eq '') }
 
 867         qw|ordnumber transdate cusordnumber|
 
 869       map { delete $ref->{$_} } qw|ordnumber_oe transdate_oe cusordnumber_oe|;
 
 873       while ($ref->{inventory_new_chart} && ($ref->{inventory_valid} >= 0)) {
 
 875           qq|SELECT accno AS inventory_accno, | .
 
 876           qq|  new_chart_id AS inventory_new_chart, | .
 
 877           qq|  date($transdate) - valid_from AS inventory_valid | .
 
 878           qq|FROM chart WHERE id = $ref->{inventory_new_chart}|;
 
 879         ($ref->{inventory_accno}, $ref->{inventory_new_chart},
 
 880          $ref->{inventory_valid}) = selectrow_query($form, $dbh, $query);
 
 883       while ($ref->{income_new_chart} && ($ref->{income_valid} >= 0)) {
 
 885           qq|SELECT accno AS income_accno, | .
 
 886           qq|  new_chart_id AS income_new_chart, | .
 
 887           qq|  date($transdate) - valid_from AS income_valid | .
 
 888           qq|FROM chart WHERE id = $ref->{income_new_chart}|;
 
 889         ($ref->{income_accno}, $ref->{income_new_chart},
 
 890          $ref->{income_valid}) = selectrow_query($form, $dbh, $query);
 
 893       while ($ref->{expense_new_chart} && ($ref->{expense_valid} >= 0)) {
 
 895           qq|SELECT accno AS expense_accno, | .
 
 896           qq|  new_chart_id AS expense_new_chart, | .
 
 897           qq|  date($transdate) - valid_from AS expense_valid | .
 
 898           qq|FROM chart WHERE id = $ref->{expense_new_chart}|;
 
 899         ($ref->{expense_accno}, $ref->{expense_new_chart},
 
 900          $ref->{expense_valid}) = selectrow_query($form, $dbh, $query);
 
 903       # delete orderitems_id in collective orders, so that they get cloned no matter what
 
 904       delete $ref->{orderitems_id} if (@ids);
 
 906       # get tax rates and description
 
 907       my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
 
 909         qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber | .
 
 910         qq|FROM tax t LEFT JOIN chart c on (c.id = t.chart_id) | .
 
 911         qq|WHERE t.id IN (SELECT tk.tax_id FROM taxkeys tk | .
 
 912         qq|               WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?) | .
 
 913         qq|                 AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1) | .
 
 914         qq|ORDER BY c.accno|;
 
 915       my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
 
 916       $ref->{taxaccounts} = "";
 
 918       while (my $ptr = $stw->fetchrow_hashref("NAME_lc")) {
 
 919         if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
 
 923         $ref->{taxaccounts} .= "$ptr->{accno} ";
 
 924         if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
 
 925           $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
 
 926           $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
 
 927           $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
 
 928           $form->{taxaccounts} .= "$ptr->{accno} ";
 
 933       chop $ref->{taxaccounts};
 
 935       push @{ $form->{form_details} }, $ref;
 
 943     $form->lastname_used($dbh, $myconfig, $form->{vc})
 
 944       unless $form->{"$form->{vc}_id"};
 
 948   $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? "buy" : "sell");
 
 950   Common::webdav_folder($form) if ($main::webdav);
 
 952   my $rc = $dbh->commit;
 
 955   $main::lxdebug->leave_sub();
 
 960 =item retrieve_simple PARAMS
 
 962 simple OE retrieval by id. does not look up customer, vendor, units or any other stuff. only oe and orderitems.
 
 964   my $order = retrieve_simple(id => 2);
 
 976 sub retrieve_simple {
 
 977   $main::lxdebug->enter_sub();
 
 982   Common::check_params(\%params, qw(id));
 
 984   my $myconfig    = \%main::myconfig;
 
 985   my $form        = $main::form;
 
 987   my $dbh         = $params{dbh} || $form->get_standard_dbh($myconfig);
 
 989   my $oe_query    = qq|SELECT * FROM oe         WHERE id = ?|;
 
 990   my $oi_query    = qq|SELECT * FROM orderitems WHERE trans_id = ?|;
 
 992   my $order            = selectfirst_hashref_query($form, $dbh, $oe_query, conv_i($params{id}));
 
 993   $order->{orderitems} = selectall_hashref_query(  $form, $dbh, $oi_query, conv_i($params{id}));
 
 995   $main::lxdebug->leave_sub();
 
1001   $main::lxdebug->enter_sub();
 
1003   my ($self, $myconfig, $form) = @_;
 
1005   # connect to database
 
1006   my $dbh = $form->dbconnect($myconfig);
 
1012   my $nodiscount_subtotal = 0;
 
1013   my $discount_subtotal = 0;
 
1016   my @partsgroup = ();
 
1019   my $subtotal_header = 0;
 
1020   my $subposition = 0;
 
1027   my %oid = ('Pg'     => 'oid',
 
1028              'Oracle' => 'rowid');
 
1030   my (@project_ids, %projectnumbers);
 
1032   push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
 
1034   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
 
1035                    'departments'   => 'ALL_DEPARTMENTS');
 
1038   foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
 
1039     $price_factors{$pfac->{id}}  = $pfac;
 
1040     $pfac->{factor}             *= 1;
 
1041     $pfac->{formatted_factor}    = $form->format_amount($myconfig, $pfac->{factor});
 
1045   foreach my $dept (@{ $form->{ALL_DEPARTMENTS} }) {
 
1046     next unless $dept->{id} eq $form->{department_id};
 
1047     $form->{department} = $dept->{description};
 
1051   # sort items by partsgroup
 
1052   for $i (1 .. $form->{rowcount}) {
 
1054     if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
 
1055       $partsgroup = $form->{"partsgroup_$i"};
 
1057     push @partsgroup, [$i, $partsgroup];
 
1058     push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
 
1062     $query = "SELECT id, projectnumber FROM project WHERE id IN (" .
 
1063       join(", ", map("?", @project_ids)) . ")";
 
1064     $sth = prepare_execute_query($form, $dbh, $query, @project_ids);
 
1065     while (my $ref = $sth->fetchrow_hashref()) {
 
1066       $projectnumbers{$ref->{id}} = $ref->{projectnumber};
 
1071   $form->{"globalprojectnumber"} = $projectnumbers{$form->{"globalproject_id"}};
 
1073   $form->{discount} = [];
 
1075   $form->{TEMPLATE_ARRAYS} = { };
 
1076   IC->prepare_parts_for_printing();
 
1078   my $ic_cvar_configs = CVar->get_configs(module => 'IC');
 
1081     qw(runningnumber number description longdescription qty ship unit bin
 
1082        partnotes serialnumber reqdate sellprice listprice netprice
 
1083        discount p_discount discount_sub nodiscount_sub
 
1084        linetotal  nodiscount_linetotal tax_rate projectnumber
 
1085        price_factor price_factor_name partsgroup);
 
1087   push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
 
1089   my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
 
1091   map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays);
 
1094   foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
 
1097     if ($item->[1] ne $sameitem) {
 
1098       push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
 
1099       $sameitem = $item->[1];
 
1101       map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
1104     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
 
1106     if ($form->{"id_$i"} != 0) {
 
1108       # add number, description and qty to $form->{number}, ....
 
1110       if ($form->{"subtotal_$i"} && !$subtotal_header) {
 
1111         $subtotal_header = $i;
 
1112         $position = int($position);
 
1115       } elsif ($subtotal_header) {
 
1117         $position = int($position);
 
1118         $position = $position.".".$subposition;
 
1120         $position = int($position);
 
1124       my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
 
1126       push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} },     $position;
 
1127       push @{ $form->{TEMPLATE_ARRAYS}->{number} },            $form->{"partnumber_$i"};
 
1128       push @{ $form->{TEMPLATE_ARRAYS}->{description} },       $form->{"description_$i"};
 
1129       push @{ $form->{TEMPLATE_ARRAYS}->{longdescription} },   $form->{"longdescription_$i"};
 
1130       push @{ $form->{TEMPLATE_ARRAYS}->{qty} },               $form->format_amount($myconfig, $form->{"qty_$i"});
 
1131       push @{ $form->{TEMPLATE_ARRAYS}->{ship} },              $form->format_amount($myconfig, $form->{"ship_$i"});
 
1132       push @{ $form->{TEMPLATE_ARRAYS}->{unit} },              $form->{"unit_$i"};
 
1133       push @{ $form->{TEMPLATE_ARRAYS}->{bin} },               $form->{"bin_$i"};
 
1134       push @{ $form->{TEMPLATE_ARRAYS}->{partnotes} },         $form->{"partnotes_$i"};
 
1135       push @{ $form->{TEMPLATE_ARRAYS}->{serialnumber} },      $form->{"serialnumber_$i"};
 
1136       push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} },           $form->{"reqdate_$i"};
 
1137       push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} },         $form->{"sellprice_$i"};
 
1138       push @{ $form->{TEMPLATE_ARRAYS}->{listprice} },         $form->{"listprice_$i"};
 
1139       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} },      $price_factor->{formatted_factor};
 
1140       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
 
1141       push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} },        $form->{"partsgroup_$i"};
 
1143       my $sellprice     = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
1144       my ($dec)         = ($sellprice =~ /\.(\d+)/);
 
1145       my $decimalplaces = max 2, length($dec);
 
1147       my $parsed_discount      = $form->parse_amount($myconfig, $form->{"discount_$i"});
 
1148       my $linetotal_exact      =                     $form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor};
 
1149       my $linetotal            = $form->round_amount($linetotal_exact, 2);
 
1150       my $discount             = $form->round_amount($form->{"qty_$i"} * $sellprice * $parsed_discount / 100 / $price_factor->{factor} - ($linetotal - $linetotal_exact),
 
1152       my $nodiscount_linetotal = $form->round_amount($form->{"qty_$i"} * $sellprice / $price_factor->{factor}, 2);
 
1153       $form->{"netprice_$i"}   = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, 2);
 
1155       push @{ $form->{TEMPLATE_ARRAYS}->{netprice} }, ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
 
1157       $linetotal = ($linetotal != 0) ? $linetotal : '';
 
1159       push @{ $form->{TEMPLATE_ARRAYS}->{discount} },  ($discount  != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
 
1160       push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} }, $form->{"discount_$i"};
 
1162       $form->{ordtotal}         += $linetotal;
 
1163       $form->{nodiscount_total} += $nodiscount_linetotal;
 
1164       $form->{discount_total}   += $discount;
 
1166       if ($subtotal_header) {
 
1167         $discount_subtotal   += $linetotal;
 
1168         $nodiscount_subtotal += $nodiscount_linetotal;
 
1171       if ($form->{"subtotal_$i"} && $subtotal_header && ($subtotal_header != $i)) {
 
1172         push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} },   $form->format_amount($myconfig, $discount_subtotal,   2);
 
1173         push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} }, $form->format_amount($myconfig, $nodiscount_subtotal, 2);
 
1175         $discount_subtotal   = 0;
 
1176         $nodiscount_subtotal = 0;
 
1177         $subtotal_header     = 0;
 
1180         push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} },   "";
 
1181         push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} }, "";
 
1184       if (!$form->{"discount_$i"}) {
 
1185         $nodiscount += $linetotal;
 
1188       push @{ $form->{TEMPLATE_ARRAYS}->{linetotal} }, $form->format_amount($myconfig, $linetotal, 2);
 
1189       push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} }, $form->format_amount($myconfig, $nodiscount_linetotal, 2);
 
1191       push(@{ $form->{TEMPLATE_ARRAYS}->{projectnumber} }, $projectnumbers{$form->{"project_id_$i"}});
 
1193       my ($taxamount, $taxbase);
 
1196       map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
 
1198       if ($form->{taxincluded}) {
 
1201         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
 
1202         $taxbase = $linetotal / (1 + $taxrate);
 
1204         $taxamount = $linetotal * $taxrate;
 
1205         $taxbase   = $linetotal;
 
1208       if ($taxamount != 0) {
 
1209         foreach my $accno (split / /, $form->{"taxaccounts_$i"}) {
 
1210           $taxaccounts{$accno} += $taxamount * $form->{"${accno}_rate"} / $taxrate;
 
1211           $taxbase{$accno}     += $taxbase;
 
1215       $tax_rate = $taxrate * 100;
 
1216       push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
 
1218       if ($form->{"assembly_$i"}) {
 
1221         # get parts and push them onto the stack
 
1223         if ($form->{groupitems}) {
 
1224           $sortorder = qq|ORDER BY pg.partsgroup, a.$oid{$myconfig->{dbdriver}}|;
 
1226           $sortorder = qq|ORDER BY a.$oid{$myconfig->{dbdriver}}|;
 
1229         $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty, | .
 
1230                        qq|pg.partsgroup | .
 
1231                        qq|FROM assembly a | .
 
1232                              qq|  JOIN parts p ON (a.parts_id = p.id) | .
 
1233                              qq|    LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
 
1234                              qq|    WHERE a.bom = '1' | .
 
1235                              qq|    AND a.id = ? | . $sortorder;
 
1236                     @values = ($form->{"id_$i"});
 
1237         $sth = $dbh->prepare($query);
 
1238         $sth->execute(@values) || $form->dberror($query);
 
1240         while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
 
1241           if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
 
1242             map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
1243             $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
 
1244             push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
 
1247           push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq|, $ref->{partnumber}, $ref->{description}|);
 
1248           map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
1253       map { push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} }, $form->{"ic_cvar_$_->{name}_$i"} } @{ $ic_cvar_configs };
 
1258   foreach $item (sort keys %taxaccounts) {
 
1259     $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
 
1261     push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase} },        $form->format_amount($myconfig, $taxbase{$item}, 2));
 
1262     push(@{ $form->{TEMPLATE_ARRAYS}->{tax} },            $form->format_amount($myconfig, $taxamount,      2));
 
1263     push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} },        $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
 
1264     push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $form->{"${item}_description"} . q{ } . 100 * $form->{"${item}_rate"} . q{%});
 
1265     push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} },      $form->{"${item}_taxnumber"});
 
1268   $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
 
1269   $form->{discount_total}      = $form->format_amount($myconfig, $form->{discount_total}, 2);
 
1270   $form->{nodiscount}          = $form->format_amount($myconfig, $nodiscount, 2);
 
1271   $form->{yesdiscount}         = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
 
1273   if($form->{taxincluded}) {
 
1274     $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal} - $tax, 2);
 
1276     $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
 
1279   $form->{ordtotal} = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
 
1282   $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
 
1284   if ($form->{type} =~ /_quotation/) {
 
1285     $form->set_payment_options($myconfig, $form->{quodate});
 
1287     $form->set_payment_options($myconfig, $form->{orddate});
 
1290   $form->{username} = $myconfig->{name};
 
1294   $main::lxdebug->leave_sub();
 
1297 sub project_description {
 
1298   $main::lxdebug->enter_sub();
 
1300   my ($self, $dbh, $id) = @_;
 
1302   my $query = qq|SELECT description FROM project WHERE id = ?|;
 
1303   my ($value) = selectrow_query($main::form, $dbh, $query, $id);
 
1305   $main::lxdebug->leave_sub();