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);
 
  46 OE.pm - Order entry module
 
  50 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>.
 
  59   $main::lxdebug->enter_sub();
 
  61   my ($self, $myconfig, $form) = @_;
 
  64   my $dbh = $form->dbconnect($myconfig);
 
  67   my $ordnumber = 'ordnumber';
 
  73   my $rate = ($form->{vc} eq 'customer') ? 'buy' : 'sell';
 
  75   if ($form->{type} =~ /_quotation$/) {
 
  77     $ordnumber = 'quonumber';
 
  80   my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
 
  83     qq|SELECT o.id, o.ordnumber, o.transdate, o.reqdate, | .
 
  84     qq|  o.amount, ct.name, o.netamount, o.${vc}_id, o.globalproject_id, | .
 
  85     qq|  o.closed, o.delivered, o.quonumber, o.shippingpoint, o.shipvia, | .
 
  86     qq|  o.transaction_description, | .
 
  87     qq|  o.marge_total, o.marge_percent, | .
 
  88     qq|  ex.$rate AS exchangerate, | .
 
  89     qq|  pr.projectnumber AS globalprojectnumber, | .
 
  90     qq|  e.name AS employee, s.name AS salesman, | .
 
  91     qq|  ct.country, ct.ustid  | .
 
  93     qq|JOIN $vc ct ON (o.${vc}_id = ct.id) | .
 
  94     qq|LEFT JOIN employee e ON (o.employee_id = e.id) | .
 
  95     qq|LEFT JOIN employee s ON (o.salesman_id = s.id) | .
 
  96     qq|LEFT JOIN exchangerate ex ON (ex.curr = o.curr | .
 
  97     qq|  AND ex.transdate = o.transdate) | .
 
  98     qq|LEFT JOIN project pr ON (o.globalproject_id = pr.id) | .
 
  99     qq|WHERE (o.quotation = ?) |;
 
 100   push(@values, $quotation);
 
 102   my ($null, $split_department_id) = split /--/, $form->{department};
 
 103   my $department_id = $form->{department_id} || $split_department_id;
 
 104   if ($department_id) {
 
 105     $query .= qq| AND o.department_id = ?|;
 
 106     push(@values, $department_id);
 
 109   if ($form->{"project_id"}) {
 
 111       qq|AND ((globalproject_id = ?) OR EXISTS | .
 
 112       qq|  (SELECT * FROM orderitems oi | .
 
 113       qq|   WHERE oi.project_id = ? AND oi.trans_id = o.id))|;
 
 114     push(@values, conv_i($form->{"project_id"}), conv_i($form->{"project_id"}));
 
 117   if ($form->{"projectnumber"}) {
 
 119       AND (pr.projectnumber ILIKE ?) OR EXISTS (
 
 120         SELECT * FROM orderitems oi
 
 121         LEFT JOIN project proi ON proi.id = oi.project_id
 
 122         WHERE proi.projectnumber ILIKE ? AND oi.trans_id = o.id
 
 125     push @values, "%" . $form->{"projectnumber"} . "%", "%" . $form->{"projectnumber"} . "%" ;
 
 128   if ($form->{"${vc}_id"}) {
 
 129     $query .= " AND o.${vc}_id = ?";
 
 130     push(@values, $form->{"${vc}_id"});
 
 132   } elsif ($form->{$vc}) {
 
 133     $query .= " AND ct.name ILIKE ?";
 
 134     push(@values, '%' . $form->{$vc} . '%');
 
 137   if ($form->{employee_id}) {
 
 138     $query .= " AND o.employee_id = ?";
 
 139     push @values, conv_i($form->{employee_id});
 
 142   if ($form->{salesman_id}) {
 
 143     $query .= " AND o.salesman_id = ?";
 
 144     push @values, conv_i($form->{salesman_id});
 
 147   if (!$form->{open} && !$form->{closed}) {
 
 148     $query .= " AND o.id = 0";
 
 149   } elsif (!($form->{open} && $form->{closed})) {
 
 150     $query .= ($form->{open}) ? " AND o.closed = '0'" : " AND o.closed = '1'";
 
 153   if (($form->{"notdelivered"} || $form->{"delivered"}) &&
 
 154       ($form->{"notdelivered"} ne $form->{"delivered"})) {
 
 155     $query .= $form->{"delivered"} ?
 
 156       " AND o.delivered " : " AND NOT o.delivered";
 
 159   if ($form->{$ordnumber}) {
 
 160     $query .= qq| AND o.$ordnumber ILIKE ?|;
 
 161     push(@values, '%' . $form->{$ordnumber} . '%');
 
 164   if($form->{transdatefrom}) {
 
 165     $query .= qq| AND o.transdate >= ?|;
 
 166     push(@values, conv_date($form->{transdatefrom}));
 
 169   if($form->{transdateto}) {
 
 170     $query .= qq| AND o.transdate <= ?|;
 
 171     push(@values, conv_date($form->{transdateto}));
 
 174   if($form->{reqdatefrom}) {
 
 175     $query .= qq| AND o.reqdate >= ?|;
 
 176     push(@values, conv_date($form->{reqdatefrom}));
 
 179   if($form->{reqdateto}) {
 
 180     $query .= qq| AND o.reqdate <= ?|;
 
 181     push(@values, conv_date($form->{reqdateto}));
 
 184   if ($form->{transaction_description}) {
 
 185     $query .= qq| AND o.transaction_description ILIKE ?|;
 
 186     push(@values, '%' . $form->{transaction_description} . '%');
 
 189   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
 
 190   my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name")));
 
 191   my %allowed_sort_columns = (
 
 192     "transdate"               => "o.transdate",
 
 193     "reqdate"                 => "o.reqdate",
 
 195     "ordnumber"               => "o.ordnumber",
 
 196     "quonumber"               => "o.quonumber",
 
 198     "employee"                => "e.name",
 
 199     "salesman"                => "e.name",
 
 200     "shipvia"                 => "o.shipvia",
 
 201     "transaction_description" => "o.transaction_description"
 
 203   if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
 
 204     $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}";
 
 206   $query .= qq| ORDER by | . $sortorder;
 
 208   my $sth = $dbh->prepare($query);
 
 209   $sth->execute(@values) ||
 
 210     $form->dberror($query . " (" . join(", ", @values) . ")");
 
 214   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 215     $ref->{exchangerate} = 1 unless $ref->{exchangerate};
 
 216     push @{ $form->{OE} }, $ref if $ref->{id} != $id{ $ref->{id} };
 
 217     $id{ $ref->{id} } = $ref->{id};
 
 223   $main::lxdebug->leave_sub();
 
 226 sub transactions_for_todo_list {
 
 227   $main::lxdebug->enter_sub();
 
 232   my $myconfig = \%main::myconfig;
 
 233   my $form     = $main::form;
 
 235   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
 
 237   my $query    = qq|SELECT id FROM employee WHERE login = ?|;
 
 238   my ($e_id)   = selectrow_query($form, $dbh, $query, $form->{login});
 
 241     qq|SELECT oe.id, oe.transdate, oe.reqdate, oe.quonumber, oe.transaction_description, oe.amount,
 
 242          CASE WHEN (COALESCE(oe.customer_id, 0) = 0) THEN 'vendor' ELSE 'customer' END AS vc,
 
 247        LEFT JOIN customer c ON (oe.customer_id = c.id)
 
 248        LEFT JOIN vendor v   ON (oe.vendor_id   = v.id)
 
 249        LEFT JOIN employee e ON (oe.employee_id = e.id)
 
 250        WHERE (COALESCE(quotation, FALSE) = TRUE)
 
 251          AND (COALESCE(closed,    FALSE) = FALSE)
 
 252          AND ((oe.employee_id = ?) OR (oe.salesman_id = ?))
 
 253          AND NOT (oe.reqdate ISNULL)
 
 254          AND (oe.reqdate < current_date)
 
 257   my $quotations = selectall_hashref_query($form, $dbh, $query, $e_id, $e_id);
 
 259   $main::lxdebug->leave_sub();
 
 265   $main::lxdebug->enter_sub();
 
 267   my ($self, $myconfig, $form) = @_;
 
 269   # connect to database, turn off autocommit
 
 270   my $dbh = $form->dbconnect_noauto($myconfig);
 
 272   my ($query, @values, $sth, $null);
 
 273   my $exchangerate = 0;
 
 275   my $all_units = AM->retrieve_units($myconfig, $form);
 
 276   $form->{all_units} = $all_units;
 
 278   my $ic_cvar_configs = CVar->get_configs(module => 'IC',
 
 281   $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id};
 
 282   unless ($form->{employee_id}) {
 
 283     $form->get_employee($dbh);
 
 286   my $ml = ($form->{type} eq 'sales_order') ? 1 : -1;
 
 289     $query = qq|DELETE FROM custom_variables
 
 290                 WHERE (config_id IN (SELECT id FROM custom_variable_configs WHERE module = 'IC'))
 
 291                   AND (sub_module = 'orderitems')
 
 292                   AND (trans_id IN (SELECT id FROM orderitems WHERE trans_id = ?))|;
 
 293     do_query($form, $dbh, $query, $form->{id});
 
 295     $query = qq|DELETE FROM orderitems WHERE trans_id = ?|;
 
 296     do_query($form, $dbh, $query, $form->{id});
 
 298     $query = qq|DELETE FROM shipto | .
 
 299              qq|WHERE trans_id = ? AND module = 'OE'|;
 
 300     do_query($form, $dbh, $query, $form->{id});
 
 304     $query = qq|SELECT nextval('id')|;
 
 305     ($form->{id}) = selectrow_query($form, $dbh, $query);
 
 307     $query = qq|INSERT INTO oe (id, ordnumber, employee_id) VALUES (?, '', ?)|;
 
 308     do_query($form, $dbh, $query, $form->{id}, $form->{employee_id});
 
 326   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
 
 327   my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
 
 330   for my $i (1 .. $form->{rowcount}) {
 
 332     map({ $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) } qw(qty ship));
 
 334     if ($form->{"id_$i"}) {
 
 337       $query = qq|SELECT unit FROM parts WHERE id = ?|;
 
 338       my ($item_unit) = selectrow_query($form, $dbh, $query, $form->{"id_$i"});
 
 341       if (defined($all_units->{$item_unit}->{factor}) &&
 
 342           (($all_units->{$item_unit}->{factor} * 1) != 0)) {
 
 343         $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
 
 345       my $baseqty = $form->{"qty_$i"} * $basefactor;
 
 347       $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
 
 348       $form->{"marge_total_$i"} = $form->parse_amount($myconfig, $form->{"marge_total_$i"}) * 1;
 
 349       $form->{"lastcost_$i"} = $form->{"lastcost_$i"} * 1;
 
 351       # set values to 0 if nothing entered
 
 352       $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
 
 354       $form->{"sellprice_$i"} = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
 355       $fxsellprice = $form->{"sellprice_$i"};
 
 357       my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
 359       my $decimalplaces = ($dec > 2) ? $dec : 2;
 
 361       $discount = $form->round_amount($form->{"sellprice_$i"} * $form->{"discount_$i"}, $decimalplaces);
 
 362       $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"} - $discount, $decimalplaces);
 
 364       $form->{"inventory_accno_$i"} *= 1;
 
 365       $form->{"expense_accno_$i"}   *= 1;
 
 367       $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
 
 368       $linetotal    = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
 
 370       @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
 
 374       map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
 
 376       if ($form->{taxincluded}) {
 
 377         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
 
 378         $taxbase   = $linetotal - $taxamount;
 
 380         # we are not keeping a natural price, do not round
 
 381         $form->{"sellprice_$i"} =
 
 382           $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
 
 384         $taxamount = $linetotal * $taxrate;
 
 385         $taxbase   = $linetotal;
 
 388       if ($form->round_amount($taxrate, 7) == 0) {
 
 389         if ($form->{taxincluded}) {
 
 390           foreach my $item (@taxaccounts) {
 
 391             $taxamount = $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
 
 392             $taxaccounts{$item} += $taxamount;
 
 393             $taxdiff            += $taxamount;
 
 394             $taxbase{$item}     += $taxbase;
 
 396           $taxaccounts{ $taxaccounts[0] } += $taxdiff;
 
 398           foreach my $item (@taxaccounts) {
 
 399             $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
 
 400             $taxbase{$item}     += $taxbase;
 
 404         foreach my $item (@taxaccounts) {
 
 405           $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
 
 406           $taxbase{$item} += $taxbase;
 
 410       $netamount += $form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor;
 
 412       $reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef;
 
 414       # get pricegroup_id and save ist
 
 415       ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
 
 418       # save detail record in orderitems table
 
 419       my $orderitems_id = $form->{"orderitems_id_$i"};
 
 420       ($orderitems_id)  = selectfirst_array_query($form, $dbh, qq|SELECT nextval('orderitemsid')|) if (!$orderitems_id);
 
 423       $query = qq|INSERT INTO orderitems (
 
 424                     id, trans_id, parts_id, description, longdescription, qty, base_qty,
 
 425                     sellprice, discount, unit, reqdate, project_id, serialnumber, ship,
 
 426                     pricegroup_id, ordnumber, transdate, cusordnumber, subtotal,
 
 427                     marge_percent, marge_total, lastcost, price_factor_id, price_factor, marge_price_factor)
 
 428                   VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
 
 429                           (SELECT factor FROM price_factors WHERE id = ?), ?)|;
 
 431            conv_i($orderitems_id), conv_i($form->{id}), conv_i($form->{"id_$i"}),
 
 432            $form->{"description_$i"}, $form->{"longdescription_$i"},
 
 433            $form->{"qty_$i"}, $baseqty,
 
 434            $fxsellprice, $form->{"discount_$i"},
 
 435            $form->{"unit_$i"}, conv_date($reqdate), conv_i($form->{"project_id_$i"}),
 
 436            $form->{"serialnumber_$i"}, $form->{"ship_$i"}, conv_i($pricegroup_id),
 
 437            $form->{"ordnumber_$i"}, conv_date($form->{"transdate_$i"}),
 
 438            $form->{"cusordnumber_$i"}, $form->{"subtotal_$i"} ? 't' : 'f',
 
 439            $form->{"marge_percent_$i"}, $form->{"marge_total_$i"},
 
 440            $form->{"lastcost_$i"},
 
 441            conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
 
 442            conv_i($form->{"marge_price_factor_$i"}));
 
 443       do_query($form, $dbh, $query, @values);
 
 445       $form->{"sellprice_$i"} = $fxsellprice;
 
 446       $form->{"discount_$i"} *= 100;
 
 448       CVar->save_custom_variables(module       => 'IC',
 
 449                                   sub_module   => 'orderitems',
 
 450                                   trans_id     => $orderitems_id,
 
 451                                   configs      => $ic_cvar_configs,
 
 453                                   name_prefix  => 'ic_',
 
 454                                   name_postfix => "_$i",
 
 459   $reqdate = ($form->{reqdate}) ? $form->{reqdate} : undef;
 
 463   map { $tax += $form->round_amount($taxaccounts{$_}, 2) } keys %taxaccounts;
 
 465   $amount = $form->round_amount($netamount + $tax, 2);
 
 466   $netamount = $form->round_amount($netamount, 2);
 
 468   if ($form->{currency} eq $form->{defaultcurrency}) {
 
 469     $form->{exchangerate} = 1;
 
 471     $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? 'buy' : 'sell');
 
 474   $form->{exchangerate} = $exchangerate || $form->parse_amount($myconfig, $form->{exchangerate});
 
 476   my $quotation = $form->{type} =~ /_order$/ ? 'f' : 't';
 
 478   ($null, $form->{department_id}) = split(/--/, $form->{department}) if $form->{department};
 
 483          ordnumber = ?, quonumber = ?, cusordnumber = ?, transdate = ?, vendor_id = ?,
 
 484          customer_id = ?, amount = ?, netamount = ?, reqdate = ?, taxincluded = ?,
 
 485          shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, curr = ?, closed = ?,
 
 486          delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
 
 487          taxzone_id = ?, shipto_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,
 
 488          globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
 
 491   @values = ($form->{ordnumber} || '', $form->{quonumber},
 
 492              $form->{cusordnumber}, conv_date($form->{transdate}),
 
 493              conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
 
 494              $amount, $netamount, conv_date($reqdate),
 
 495              $form->{taxincluded} ? 't' : 'f', $form->{shippingpoint},
 
 496              $form->{shipvia}, $form->{notes}, $form->{intnotes},
 
 497              substr($form->{currency}, 0, 3), $form->{closed} ? 't' : 'f',
 
 498              $form->{delivered} ? "t" : "f", $form->{proforma} ? 't' : 'f',
 
 499              $quotation, conv_i($form->{department_id}),
 
 500              conv_i($form->{language_id}), conv_i($form->{taxzone_id}),
 
 501              conv_i($form->{shipto_id}), conv_i($form->{payment_id}),
 
 502              conv_i($form->{delivery_vendor_id}),
 
 503              conv_i($form->{delivery_customer_id}),
 
 504              conv_i($form->{globalproject_id}), conv_i($form->{employee_id}),
 
 505              conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
 
 506              $form->{transaction_description},
 
 507              $form->{marge_total} * 1, $form->{marge_percent} * 1,
 
 508              conv_i($form->{id}));
 
 509   do_query($form, $dbh, $query, @values);
 
 511   $form->{ordtotal} = $amount;
 
 514   $form->{name} = $form->{ $form->{vc} };
 
 515   $form->{name} =~ s/--\Q$form->{"$form->{vc}_id"}\E//;
 
 517   if (!$form->{shipto_id}) {
 
 518     $form->add_shipto($dbh, $form->{id}, "OE");
 
 521   # save printed, emailed, queued
 
 522   $form->save_status($dbh);
 
 524   # Link this record to the records it was created from.
 
 525   $form->{convert_from_oe_ids} =~ s/^\s+//;
 
 526   $form->{convert_from_oe_ids} =~ s/\s+$//;
 
 527   my @convert_from_oe_ids      =  split m/\s+/, $form->{convert_from_oe_ids};
 
 528   delete $form->{convert_from_oe_ids};
 
 530   if (scalar @convert_from_oe_ids) {
 
 531     RecordLinks->create_links('dbh'        => $dbh,
 
 533                               'from_table' => 'oe',
 
 534                               'from_ids'   => \@convert_from_oe_ids,
 
 536                               'to_id'      => $form->{id},
 
 539     $self->_close_quotations_rfqs('dbh'     => $dbh,
 
 540                                   'from_id' => \@convert_from_oe_ids,
 
 541                                   'to_id'   => $form->{id});
 
 544   if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
 
 545     if ($form->{vc} eq 'customer') {
 
 546       $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
 
 548     if ($form->{vc} eq 'vendor') {
 
 549       $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
 
 553   $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ?
 
 554                                        "quonumber" : "ordnumber"};
 
 556   Common::webdav_folder($form) if ($main::webdav);
 
 558   my $rc = $dbh->commit;
 
 561   $main::lxdebug->leave_sub();
 
 566 sub _close_quotations_rfqs {
 
 567   $main::lxdebug->enter_sub();
 
 572   Common::check_params(\%params, qw(from_id to_id));
 
 574   my $myconfig = \%main::myconfig;
 
 575   my $form     = $main::form;
 
 577   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
 
 579   my $query    = qq|SELECT quotation FROM oe WHERE id = ?|;
 
 580   my $sth      = prepare_query($form, $dbh, $query);
 
 582   do_statement($form, $sth, $query, conv_i($params{to_id}));
 
 584   my ($quotation) = $sth->fetchrow_array();
 
 587     $main::lxdebug->leave_sub();
 
 593   foreach my $from_id (@{ $params{from_id} }) {
 
 594     $from_id = conv_i($from_id);
 
 595     do_statement($form, $sth, $query, $from_id);
 
 596     ($quotation) = $sth->fetchrow_array();
 
 597     push @close_ids, $from_id if ($quotation);
 
 602   if (scalar @close_ids) {
 
 603     $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_ids) . qq|)|;
 
 604     do_query($form, $dbh, $query, @close_ids);
 
 606     $dbh->commit() unless ($params{dbh});
 
 609   $main::lxdebug->leave_sub();
 
 613   $main::lxdebug->enter_sub();
 
 615   my ($self, $myconfig, $form, $spool) = @_;
 
 617   # connect to database
 
 618   my $dbh = $form->dbconnect_noauto($myconfig);
 
 621   my $query = qq|SELECT s.spoolfile FROM status s | .
 
 622               qq|WHERE s.trans_id = ?|;
 
 623   my @values = (conv_i($form->{id}));
 
 624   my $sth = $dbh->prepare($query);
 
 625   $sth->execute(@values) || $self->dberror($query);
 
 630   while (($spoolfile) = $sth->fetchrow_array) {
 
 631     push @spoolfiles, $spoolfile;
 
 636   @values = (conv_i($form->{id}));
 
 638   # delete status entries
 
 639   $query = qq|DELETE FROM status | .
 
 640            qq|WHERE trans_id = ?|;
 
 641   do_query($form, $dbh, $query, @values);
 
 644   $query = qq|DELETE FROM oe | .
 
 646   do_query($form, $dbh, $query, @values);
 
 648   # delete individual entries
 
 649   $query = qq|DELETE FROM orderitems | .
 
 650            qq|WHERE trans_id = ?|;
 
 651   do_query($form, $dbh, $query, @values);
 
 653   $query = qq|DELETE FROM shipto | .
 
 654            qq|WHERE trans_id = ? AND module = 'OE'|;
 
 655   do_query($form, $dbh, $query, @values);
 
 657   my $rc = $dbh->commit;
 
 661     foreach $spoolfile (@spoolfiles) {
 
 662       unlink "$spool/$spoolfile" if $spoolfile;
 
 666   $main::lxdebug->leave_sub();
 
 672   $main::lxdebug->enter_sub();
 
 674   my ($self, $myconfig, $form) = @_;
 
 676   # connect to database
 
 677   my $dbh = $form->dbconnect_noauto($myconfig);
 
 679   my ($query, $query_add, @values, @ids, $sth);
 
 681   my $ic_cvar_configs = CVar->get_configs(module => 'IC',
 
 684   # translate the ids (given by id_# and trans_id_#) into one array of ids, so we can join them later
 
 686     push @ids, $form->{"trans_id_$_"}
 
 687       if ($form->{"multi_id_$_"} and $form->{"trans_id_$_"})
 
 688   } (1 .. $form->{"rowcount"});
 
 690   if ($form->{rowcount} && scalar @ids) {
 
 691     $form->{convert_from_oe_ids} = join ' ', @ids;
 
 694   # if called in multi id mode, and still only got one id, switch back to single id
 
 695   if ($form->{"rowcount"} and $#ids == 0) {
 
 696     $form->{"id"} = $ids[0];
 
 700   # and remember for the rest of the function
 
 701   my $is_collective_order = scalar @ids;
 
 704     my $wday         = (localtime(time))[6];
 
 705     my $next_workday = $wday == 5 ? 3 : $wday == 6 ? 2 : 1;
 
 706     $query_add       = qq|, current_date AS transdate, date(current_date + interval '${next_workday} days') AS reqdate|;
 
 709   # get default accounts
 
 710   $query = qq|SELECT (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
 
 711                      (SELECT c.accno FROM chart c WHERE d.income_accno_id    = c.id) AS income_accno,
 
 712                      (SELECT c.accno FROM chart c WHERE d.expense_accno_id   = c.id) AS expense_accno,
 
 713                      (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id    = c.id) AS fxgain_accno,
 
 714                      (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id    = c.id) AS fxloss_accno,
 
 718   my $ref = selectfirst_hashref_query($form, $dbh, $query);
 
 719   map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 721   ($form->{currency}) = split(/:/, $form->{currencies}) unless ($form->{currency});
 
 723   # set reqdate if this is an invoice->order conversion. If someone knows a better check to ensure
 
 724   # we come from invoices, feel free.
 
 725   $form->{reqdate} = $form->{deliverydate}
 
 726     if (    $form->{deliverydate}
 
 727         and $form->{callback} =~ /action=ar_transactions/);
 
 729   my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
 
 731   if ($form->{id} or @ids) {
 
 733     # retrieve order for single id
 
 734     # NOTE: this query is intended to fetch all information only ONCE.
 
 735     # so if any of these infos is important (or even different) for any item,
 
 736     # it will be killed out and then has to be fetched from the item scope query further down
 
 738       qq|SELECT o.cp_id, o.ordnumber, o.transdate, o.reqdate,
 
 739            o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
 
 740            o.curr AS currency, e.name AS employee, o.employee_id, o.salesman_id,
 
 741            o.${vc}_id, cv.name AS ${vc}, o.amount AS invtotal,
 
 742            o.closed, o.reqdate, o.quonumber, o.department_id, o.cusordnumber,
 
 743            d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
 
 744            o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id,
 
 745            o.globalproject_id, o.delivered, o.transaction_description
 
 747          JOIN ${vc} cv ON (o.${vc}_id = cv.id)
 
 748          LEFT JOIN employee e ON (o.employee_id = e.id)
 
 749          LEFT JOIN department d ON (o.department_id = d.id) | .
 
 752          : "WHERE o.id IN (" . join(', ', map("? ", @ids)) . ")"
 
 754     @values = $form->{id} ? ($form->{id}) : @ids;
 
 755     $sth = prepare_execute_query($form, $dbh, $query, @values);
 
 757     $ref = $sth->fetchrow_hashref("NAME_lc");
 
 758     map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 760     $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ?
 
 761                                          "quonumber" : "ordnumber"};
 
 763     # set all entries for multiple ids blank that yield different information
 
 764     while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 765       map { $form->{$_} = '' if ($ref->{$_} ne $form->{$_}) } keys %$ref;
 
 768     # if not given, fill transdate with current_date
 
 769     $form->{transdate} = $form->current_date($myconfig)
 
 770       unless $form->{transdate};
 
 774     if ($form->{delivery_customer_id}) {
 
 775       $query = qq|SELECT name FROM customer WHERE id = ?|;
 
 776       ($form->{delivery_customer_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_customer_id});
 
 779     if ($form->{delivery_vendor_id}) {
 
 780       $query = qq|SELECT name FROM customer WHERE id = ?|;
 
 781       ($form->{delivery_vendor_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_vendor_id});
 
 784     # shipto and pinted/mailed/queued status makes only sense for single id retrieve
 
 786       $query = qq|SELECT s.* FROM shipto s WHERE s.trans_id = ? AND s.module = 'OE'|;
 
 787       $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
 
 789       $ref = $sth->fetchrow_hashref("NAME_lc");
 
 791       map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 794       # get printed, emailed and queued
 
 795       $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
 
 796       $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
 
 798       while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 799         $form->{printed} .= "$ref->{formname} " if $ref->{printed};
 
 800         $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
 
 801         $form->{queued}  .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
 
 804       map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
 
 807     my %oid = ('Pg'     => 'oid',
 
 808                'Oracle' => 'rowid');
 
 810     my $transdate = $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
 
 812     $form->{taxzone_id} = 0 unless ($form->{taxzone_id});
 
 814     # retrieve individual items
 
 815     # this query looks up all information about the items
 
 816     # stuff different from the whole will not be overwritten, but saved with a suffix.
 
 818       qq|SELECT o.id AS orderitems_id,
 
 819            c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from as inventory_valid,
 
 820            c2.accno AS income_accno,    c2.new_chart_id AS income_new_chart,    date($transdate) - c2.valid_from as income_valid,
 
 821            c3.accno AS expense_accno,   c3.new_chart_id AS expense_new_chart,   date($transdate) - c3.valid_from as expense_valid,
 
 822            oe.ordnumber AS ordnumber_oe, oe.transdate AS transdate_oe, oe.cusordnumber AS cusordnumber_oe,
 
 823            p.partnumber, p.assembly, o.description, o.qty,
 
 824            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,
 
 825            o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
 
 826            o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
 
 827            o.price_factor_id, o.price_factor, o.marge_price_factor,
 
 828            pr.projectnumber, p.formel,
 
 829            pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
 
 831          JOIN parts p ON (o.parts_id = p.id)
 
 832          JOIN oe ON (o.trans_id = oe.id)
 
 833          LEFT JOIN chart c1 ON ((SELECT inventory_accno_id                   FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c1.id)
 
 834          LEFT JOIN chart c2 ON ((SELECT income_accno_id_$form->{taxzone_id}  FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c2.id)
 
 835          LEFT JOIN chart c3 ON ((SELECT expense_accno_id_$form->{taxzone_id} FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c3.id)
 
 836          LEFT JOIN project pr ON (o.project_id = pr.id)
 
 837          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
 
 839        ? qq|WHERE o.trans_id = ?|
 
 840        : qq|WHERE o.trans_id IN (| . join(", ", map("?", @ids)) . qq|)|) .
 
 841       qq|ORDER BY o.$oid{$myconfig->{dbdriver}}|;
 
 843     @ids = $form->{id} ? ($form->{id}) : @ids;
 
 844     $sth = prepare_execute_query($form, $dbh, $query, @values);
 
 846     while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 847       # Retrieve custom variables.
 
 848       my $cvars = CVar->get_custom_variables(dbh        => $dbh,
 
 850                                              sub_module => 'orderitems',
 
 851                                              trans_id   => $ref->{orderitems_id},
 
 853       map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
 
 856       if (!$ref->{"part_inventory_accno_id"}) {
 
 857         map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid));
 
 859       delete($ref->{"part_inventory_accno_id"});
 
 861       # in collective order, copy global ordnumber, transdate, cusordnumber into item scope
 
 862       #   unless already present there
 
 863       # remove _oe entries afterwards
 
 864       map { $ref->{$_} = $ref->{"${_}_oe"} if ($ref->{$_} eq '') }
 
 865         qw|ordnumber transdate cusordnumber|
 
 867       map { delete $ref->{$_} } qw|ordnumber_oe transdate_oe cusordnumber_oe|;
 
 871       while ($ref->{inventory_new_chart} && ($ref->{inventory_valid} >= 0)) {
 
 873           qq|SELECT accno AS inventory_accno, | .
 
 874           qq|  new_chart_id AS inventory_new_chart, | .
 
 875           qq|  date($transdate) - valid_from AS inventory_valid | .
 
 876           qq|FROM chart WHERE id = $ref->{inventory_new_chart}|;
 
 877         ($ref->{inventory_accno}, $ref->{inventory_new_chart},
 
 878          $ref->{inventory_valid}) = selectrow_query($form, $dbh, $query);
 
 881       while ($ref->{income_new_chart} && ($ref->{income_valid} >= 0)) {
 
 883           qq|SELECT accno AS income_accno, | .
 
 884           qq|  new_chart_id AS income_new_chart, | .
 
 885           qq|  date($transdate) - valid_from AS income_valid | .
 
 886           qq|FROM chart WHERE id = $ref->{income_new_chart}|;
 
 887         ($ref->{income_accno}, $ref->{income_new_chart},
 
 888          $ref->{income_valid}) = selectrow_query($form, $dbh, $query);
 
 891       while ($ref->{expense_new_chart} && ($ref->{expense_valid} >= 0)) {
 
 893           qq|SELECT accno AS expense_accno, | .
 
 894           qq|  new_chart_id AS expense_new_chart, | .
 
 895           qq|  date($transdate) - valid_from AS expense_valid | .
 
 896           qq|FROM chart WHERE id = $ref->{expense_new_chart}|;
 
 897         ($ref->{expense_accno}, $ref->{expense_new_chart},
 
 898          $ref->{expense_valid}) = selectrow_query($form, $dbh, $query);
 
 901       # delete orderitems_id in collective orders, so that they get cloned no matter what
 
 902       delete $ref->{orderitems_id} if (@ids);
 
 904       # get tax rates and description
 
 905       my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
 
 907         qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber | .
 
 908         qq|FROM tax t LEFT JOIN chart c on (c.id = t.chart_id) | .
 
 909         qq|WHERE t.id IN (SELECT tk.tax_id FROM taxkeys tk | .
 
 910         qq|               WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?) | .
 
 911         qq|                 AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1) | .
 
 912         qq|ORDER BY c.accno|;
 
 913       my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
 
 914       $ref->{taxaccounts} = "";
 
 916       while (my $ptr = $stw->fetchrow_hashref("NAME_lc")) {
 
 917         if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
 
 921         $ref->{taxaccounts} .= "$ptr->{accno} ";
 
 922         if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
 
 923           $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
 
 924           $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
 
 925           $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
 
 926           $form->{taxaccounts} .= "$ptr->{accno} ";
 
 931       chop $ref->{taxaccounts};
 
 933       push @{ $form->{form_details} }, $ref;
 
 941     $form->lastname_used($dbh, $myconfig, $form->{vc})
 
 942       unless $form->{"$form->{vc}_id"};
 
 946   $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? "buy" : "sell");
 
 948   Common::webdav_folder($form) if ($main::webdav);
 
 950   my $rc = $dbh->commit;
 
 953   $main::lxdebug->leave_sub();
 
 958 =item retrieve_simple PARAMS
 
 960 simple OE retrieval by id. does not look up customer, vendor, units or any other stuff. only oe and orderitems.
 
 962   my $order = retrieve_simple(id => 2);
 
 974 sub retrieve_simple {
 
 975   $main::lxdebug->enter_sub();
 
 980   Common::check_params(\%params, qw(id));
 
 982   my $myconfig    = \%main::myconfig;
 
 983   my $form        = $main::form;
 
 985   my $dbh         = $params{dbh} || $form->get_standard_dbh($myconfig);
 
 987   my $oe_query    = qq|SELECT * FROM oe         WHERE id = ?|;
 
 988   my $oi_query    = qq|SELECT * FROM orderitems WHERE trans_id = ?|;
 
 990   my $order            = selectfirst_hashref_query($form, $dbh, $oe_query, conv_i($params{id}));
 
 991   $order->{orderitems} = selectall_hashref_query(  $form, $dbh, $oi_query, conv_i($params{id}));
 
 993   $main::lxdebug->leave_sub();
 
 999   $main::lxdebug->enter_sub();
 
1001   my ($self, $myconfig, $form) = @_;
 
1003   # connect to database
 
1004   my $dbh = $form->dbconnect($myconfig);
 
1010   my $nodiscount_subtotal = 0;
 
1011   my $discount_subtotal = 0;
 
1014   my @partsgroup = ();
 
1017   my $subtotal_header = 0;
 
1018   my $subposition = 0;
 
1025   my %oid = ('Pg'     => 'oid',
 
1026              'Oracle' => 'rowid');
 
1028   my (@project_ids, %projectnumbers);
 
1030   push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
 
1032   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
 
1033                    'departments'   => 'ALL_DEPARTMENTS');
 
1036   foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
 
1037     $price_factors{$pfac->{id}}  = $pfac;
 
1038     $pfac->{factor}             *= 1;
 
1039     $pfac->{formatted_factor}    = $form->format_amount($myconfig, $pfac->{factor});
 
1043   foreach my $dept (@{ $form->{ALL_DEPARTMENTS} }) {
 
1044     next unless $dept->{id} eq $form->{department_id};
 
1045     $form->{department} = $dept->{description};
 
1049   # sort items by partsgroup
 
1050   for $i (1 .. $form->{rowcount}) {
 
1052     if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
 
1053       $partsgroup = $form->{"partsgroup_$i"};
 
1055     push @partsgroup, [$i, $partsgroup];
 
1056     push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
 
1060     $query = "SELECT id, projectnumber FROM project WHERE id IN (" .
 
1061       join(", ", map("?", @project_ids)) . ")";
 
1062     $sth = prepare_execute_query($form, $dbh, $query, @project_ids);
 
1063     while (my $ref = $sth->fetchrow_hashref()) {
 
1064       $projectnumbers{$ref->{id}} = $ref->{projectnumber};
 
1069   $form->{"globalprojectnumber"} = $projectnumbers{$form->{"globalproject_id"}};
 
1071   $form->{discount} = [];
 
1073   $form->{TEMPLATE_ARRAYS} = { };
 
1074   IC->prepare_parts_for_printing();
 
1076   my $ic_cvar_configs = CVar->get_configs(module => 'IC');
 
1079     qw(runningnumber number description longdescription qty ship unit bin
 
1080        partnotes serialnumber reqdate sellprice listprice netprice
 
1081        discount p_discount discount_sub nodiscount_sub
 
1082        linetotal  nodiscount_linetotal tax_rate projectnumber
 
1083        price_factor price_factor_name partsgroup);
 
1085   push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
 
1087   my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
 
1089   map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays);
 
1092   foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
 
1095     if ($item->[1] ne $sameitem) {
 
1096       push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
 
1097       $sameitem = $item->[1];
 
1099       map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
1102     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
 
1104     if ($form->{"id_$i"} != 0) {
 
1106       # add number, description and qty to $form->{number}, ....
 
1108       if ($form->{"subtotal_$i"} && !$subtotal_header) {
 
1109         $subtotal_header = $i;
 
1110         $position = int($position);
 
1113       } elsif ($subtotal_header) {
 
1115         $position = int($position);
 
1116         $position = $position.".".$subposition;
 
1118         $position = int($position);
 
1122       my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
 
1124       push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} },     $position;
 
1125       push @{ $form->{TEMPLATE_ARRAYS}->{number} },            $form->{"partnumber_$i"};
 
1126       push @{ $form->{TEMPLATE_ARRAYS}->{description} },       $form->{"description_$i"};
 
1127       push @{ $form->{TEMPLATE_ARRAYS}->{longdescription} },   $form->{"longdescription_$i"};
 
1128       push @{ $form->{TEMPLATE_ARRAYS}->{qty} },               $form->format_amount($myconfig, $form->{"qty_$i"});
 
1129       push @{ $form->{TEMPLATE_ARRAYS}->{ship} },              $form->format_amount($myconfig, $form->{"ship_$i"});
 
1130       push @{ $form->{TEMPLATE_ARRAYS}->{unit} },              $form->{"unit_$i"};
 
1131       push @{ $form->{TEMPLATE_ARRAYS}->{bin} },               $form->{"bin_$i"};
 
1132       push @{ $form->{TEMPLATE_ARRAYS}->{partnotes} },         $form->{"partnotes_$i"};
 
1133       push @{ $form->{TEMPLATE_ARRAYS}->{serialnumber} },      $form->{"serialnumber_$i"};
 
1134       push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} },           $form->{"reqdate_$i"};
 
1135       push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} },         $form->{"sellprice_$i"};
 
1136       push @{ $form->{TEMPLATE_ARRAYS}->{listprice} },         $form->{"listprice_$i"};
 
1137       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} },      $price_factor->{formatted_factor};
 
1138       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
 
1139       push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} },        $form->{"partsgroup_$i"};
 
1141       my $sellprice     = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
1142       my ($dec)         = ($sellprice =~ /\.(\d+)/);
 
1143       my $decimalplaces = max 2, length($dec);
 
1145       my $parsed_discount      = $form->parse_amount($myconfig, $form->{"discount_$i"});
 
1146       my $linetotal_exact      =                     $form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor};
 
1147       my $linetotal            = $form->round_amount($linetotal_exact, 2);
 
1148       my $discount             = $form->round_amount($form->{"qty_$i"} * $sellprice * $parsed_discount / 100 / $price_factor->{factor} - ($linetotal - $linetotal_exact),
 
1150       my $nodiscount_linetotal = $form->round_amount($form->{"qty_$i"} * $sellprice / $price_factor->{factor}, 2);
 
1151       $form->{"netprice_$i"}   = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, 2);
 
1153       push @{ $form->{TEMPLATE_ARRAYS}->{netprice} }, ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
 
1155       $linetotal = ($linetotal != 0) ? $linetotal : '';
 
1157       push @{ $form->{TEMPLATE_ARRAYS}->{discount} },  ($discount  != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
 
1158       push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} }, $form->{"discount_$i"};
 
1160       $form->{ordtotal}         += $linetotal;
 
1161       $form->{nodiscount_total} += $nodiscount_linetotal;
 
1162       $form->{discount_total}   += $discount;
 
1164       if ($subtotal_header) {
 
1165         $discount_subtotal   += $linetotal;
 
1166         $nodiscount_subtotal += $nodiscount_linetotal;
 
1169       if ($form->{"subtotal_$i"} && $subtotal_header && ($subtotal_header != $i)) {
 
1170         push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} },   $form->format_amount($myconfig, $discount_subtotal,   2);
 
1171         push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} }, $form->format_amount($myconfig, $nodiscount_subtotal, 2);
 
1173         $discount_subtotal   = 0;
 
1174         $nodiscount_subtotal = 0;
 
1175         $subtotal_header     = 0;
 
1178         push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} },   "";
 
1179         push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} }, "";
 
1182       if (!$form->{"discount_$i"}) {
 
1183         $nodiscount += $linetotal;
 
1186       push @{ $form->{TEMPLATE_ARRAYS}->{linetotal} }, $form->format_amount($myconfig, $linetotal, 2);
 
1187       push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} }, $form->format_amount($myconfig, $nodiscount_linetotal, 2);
 
1189       push(@{ $form->{TEMPLATE_ARRAYS}->{projectnumber} }, $projectnumbers{$form->{"project_id_$i"}});
 
1191       my ($taxamount, $taxbase);
 
1194       map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
 
1196       if ($form->{taxincluded}) {
 
1199         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
 
1200         $taxbase = $linetotal / (1 + $taxrate);
 
1202         $taxamount = $linetotal * $taxrate;
 
1203         $taxbase   = $linetotal;
 
1206       if ($taxamount != 0) {
 
1207         foreach my $accno (split / /, $form->{"taxaccounts_$i"}) {
 
1208           $taxaccounts{$accno} += $taxamount * $form->{"${accno}_rate"} / $taxrate;
 
1209           $taxbase{$accno}     += $taxbase;
 
1213       $tax_rate = $taxrate * 100;
 
1214       push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
 
1216       if ($form->{"assembly_$i"}) {
 
1219         # get parts and push them onto the stack
 
1221         if ($form->{groupitems}) {
 
1222           $sortorder = qq|ORDER BY pg.partsgroup, a.$oid{$myconfig->{dbdriver}}|;
 
1224           $sortorder = qq|ORDER BY a.$oid{$myconfig->{dbdriver}}|;
 
1227         $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty, | .
 
1228                        qq|pg.partsgroup | .
 
1229                        qq|FROM assembly a | .
 
1230                              qq|  JOIN parts p ON (a.parts_id = p.id) | .
 
1231                              qq|    LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
 
1232                              qq|    WHERE a.bom = '1' | .
 
1233                              qq|    AND a.id = ? | . $sortorder;
 
1234                     @values = ($form->{"id_$i"});
 
1235         $sth = $dbh->prepare($query);
 
1236         $sth->execute(@values) || $form->dberror($query);
 
1238         while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
 
1239           if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
 
1240             map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
1241             $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
 
1242             push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
 
1245           push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq|, $ref->{partnumber}, $ref->{description}|);
 
1246           map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
1251       map { push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} }, $form->{"ic_cvar_$_->{name}_$i"} } @{ $ic_cvar_configs };
 
1256   foreach $item (sort keys %taxaccounts) {
 
1257     $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
 
1259     push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase} },        $form->format_amount($myconfig, $taxbase{$item}, 2));
 
1260     push(@{ $form->{TEMPLATE_ARRAYS}->{tax} },            $form->format_amount($myconfig, $taxamount,      2));
 
1261     push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} },        $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
 
1262     push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $form->{"${item}_description"} . q{ } . 100 * $form->{"${item}_rate"} . q{%});
 
1263     push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} },      $form->{"${item}_taxnumber"});
 
1266   $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
 
1267   $form->{discount_total}      = $form->format_amount($myconfig, $form->{discount_total}, 2);
 
1268   $form->{nodiscount}          = $form->format_amount($myconfig, $nodiscount, 2);
 
1269   $form->{yesdiscount}         = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
 
1271   if($form->{taxincluded}) {
 
1272     $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal} - $tax, 2);
 
1274     $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
 
1277   $form->{ordtotal} = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
 
1280   $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
 
1282   if ($form->{type} =~ /_quotation/) {
 
1283     $form->set_payment_options($myconfig, $form->{quodate});
 
1285     $form->set_payment_options($myconfig, $form->{orddate});
 
1288   $form->{username} = $myconfig->{name};
 
1292   $main::lxdebug->leave_sub();
 
1295 sub project_description {
 
1296   $main::lxdebug->enter_sub();
 
1298   my ($self, $dbh, $id) = @_;
 
1300   my $query = qq|SELECT description FROM project WHERE id = ?|;
 
1301   my ($value) = selectrow_query($main::form, $dbh, $query, $id);
 
1303   $main::lxdebug->leave_sub();