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);
 
  44 use SL::DB::PeriodicInvoicesConfig;
 
  52   $main::lxdebug->enter_sub();
 
  54   my ($self, $myconfig, $form) = @_;
 
  57   my $dbh = $form->get_standard_dbh;
 
  60   my $ordnumber = 'ordnumber';
 
  66   my ($periodic_invoices_columns, $periodic_invoices_joins);
 
  68   my $rate = ($form->{vc} eq 'customer') ? 'buy' : 'sell';
 
  70   if ($form->{type} =~ /_quotation$/) {
 
  72     $ordnumber = 'quonumber';
 
  74   } elsif ($form->{type} eq 'sales_order') {
 
  75     $periodic_invoices_columns = qq| , COALESCE(pcfg.active, 'f') AS periodic_invoices |;
 
  76     $periodic_invoices_joins   = qq| LEFT JOIN periodic_invoices_configs pcfg ON (o.id = pcfg.oe_id) |;
 
  79   my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
 
  82     qq|SELECT o.id, o.ordnumber, o.transdate, o.reqdate, | .
 
  83     qq|  o.amount, ct.${vc}number, ct.name, o.netamount, o.${vc}_id, o.globalproject_id, | .
 
  84     qq|  o.closed, o.delivered, o.quonumber, o.shippingpoint, o.shipvia, | .
 
  85     qq|  o.transaction_description, | .
 
  86     qq|  o.marge_total, o.marge_percent, | .
 
  87     qq|  ex.$rate AS exchangerate, | .
 
  88     qq|  pr.projectnumber AS globalprojectnumber, | .
 
  89     qq|  e.name AS employee, s.name AS salesman, | .
 
  90     qq|  ct.${vc}number AS vcnumber, ct.country, ct.ustid  | .
 
  91     $periodic_invoices_columns .
 
  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.currency_id = o.currency_id | .
 
  97     qq|  AND ex.transdate = o.transdate) | .
 
  98     qq|LEFT JOIN project pr ON (o.globalproject_id = pr.id) | .
 
  99     qq|$periodic_invoices_joins | .
 
 100     qq|WHERE (o.quotation = ?) |;
 
 101   push(@values, $quotation);
 
 103   my ($null, $split_department_id) = split /--/, $form->{department};
 
 104   my $department_id = $form->{department_id} || $split_department_id;
 
 105   if ($department_id) {
 
 106     $query .= qq| AND o.department_id = ?|;
 
 107     push(@values, $department_id);
 
 110   if ($form->{"project_id"}) {
 
 112       qq|AND ((globalproject_id = ?) OR EXISTS | .
 
 113       qq|  (SELECT * FROM orderitems oi | .
 
 114       qq|   WHERE oi.project_id = ? AND oi.trans_id = o.id))|;
 
 115     push(@values, conv_i($form->{"project_id"}), conv_i($form->{"project_id"}));
 
 118   if ($form->{"projectnumber"}) {
 
 120       AND ((pr.projectnumber ILIKE ?) OR EXISTS (
 
 121         SELECT * FROM orderitems oi
 
 122         LEFT JOIN project proi ON proi.id = oi.project_id
 
 123         WHERE proi.projectnumber ILIKE ? AND oi.trans_id = o.id
 
 126     push @values, "%" . $form->{"projectnumber"} . "%", "%" . $form->{"projectnumber"} . "%" ;
 
 129   if ($form->{"${vc}_id"}) {
 
 130     $query .= " AND o.${vc}_id = ?";
 
 131     push(@values, $form->{"${vc}_id"});
 
 133   } elsif ($form->{$vc}) {
 
 134     $query .= " AND ct.name ILIKE ?";
 
 135     push(@values, '%' . $form->{$vc} . '%');
 
 138   if (!$main::auth->assert('sales_all_edit', 1)) {
 
 139     $query .= " AND o.employee_id = (select id from employee where login= ?)";
 
 140     push @values, $form->{login};
 
 142   if ($form->{employee_id}) {
 
 143     $query .= " AND o.employee_id = ?";
 
 144     push @values, conv_i($form->{employee_id});
 
 147   if ($form->{salesman_id}) {
 
 148     $query .= " AND o.salesman_id = ?";
 
 149     push @values, conv_i($form->{salesman_id});
 
 152   if (!$form->{open} && !$form->{closed}) {
 
 153     $query .= " AND o.id = 0";
 
 154   } elsif (!($form->{open} && $form->{closed})) {
 
 155     $query .= ($form->{open}) ? " AND o.closed = '0'" : " AND o.closed = '1'";
 
 158   if (($form->{"notdelivered"} || $form->{"delivered"}) &&
 
 159       ($form->{"notdelivered"} ne $form->{"delivered"})) {
 
 160     $query .= $form->{"delivered"} ?
 
 161       " AND o.delivered " : " AND NOT o.delivered";
 
 164   if ($form->{$ordnumber}) {
 
 165     $query .= qq| AND o.$ordnumber ILIKE ?|;
 
 166     push(@values, '%' . $form->{$ordnumber} . '%');
 
 169   if($form->{transdatefrom}) {
 
 170     $query .= qq| AND o.transdate >= ?|;
 
 171     push(@values, conv_date($form->{transdatefrom}));
 
 174   if($form->{transdateto}) {
 
 175     $query .= qq| AND o.transdate <= ?|;
 
 176     push(@values, conv_date($form->{transdateto}));
 
 179   if($form->{reqdatefrom}) {
 
 180     $query .= qq| AND o.reqdate >= ?|;
 
 181     push(@values, conv_date($form->{reqdatefrom}));
 
 184   if($form->{reqdateto}) {
 
 185     $query .= qq| AND o.reqdate <= ?|;
 
 186     push(@values, conv_date($form->{reqdateto}));
 
 189   if ($form->{transaction_description}) {
 
 190     $query .= qq| AND o.transaction_description ILIKE ?|;
 
 191     push(@values, '%' . $form->{transaction_description} . '%');
 
 194   if ($form->{periodic_invoices_active} ne $form->{periodic_invoices_inactive}) {
 
 195     my $not  = $form->{periodic_invoices_inactive} ? 'NOT' : '';
 
 196     $query  .= qq| AND ${not} COALESCE(pcfg.active, 'f')|;
 
 199   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
 
 200   my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name")));
 
 201   my %allowed_sort_columns = (
 
 202     "transdate"               => "o.transdate",
 
 203     "reqdate"                 => "o.reqdate",
 
 205     "ordnumber"               => "o.ordnumber",
 
 206     "quonumber"               => "o.quonumber",
 
 208     "employee"                => "e.name",
 
 209     "salesman"                => "s.name",
 
 210     "shipvia"                 => "o.shipvia",
 
 211     "transaction_description" => "o.transaction_description"
 
 213   if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
 
 214     $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}";
 
 216   $query .= qq| ORDER by | . $sortorder;
 
 218   my $sth = $dbh->prepare($query);
 
 219   $sth->execute(@values) ||
 
 220     $form->dberror($query . " (" . join(", ", @values) . ")");
 
 224   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 225     $ref->{exchangerate} = 1 unless $ref->{exchangerate};
 
 226     push @{ $form->{OE} }, $ref if $ref->{id} != $id{ $ref->{id} };
 
 227     $id{ $ref->{id} } = $ref->{id};
 
 232   $main::lxdebug->leave_sub();
 
 235 sub transactions_for_todo_list {
 
 236   $main::lxdebug->enter_sub();
 
 241   my $myconfig = \%main::myconfig;
 
 242   my $form     = $main::form;
 
 244   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
 
 246   my $query    = qq|SELECT id FROM employee WHERE login = ?|;
 
 247   my ($e_id)   = selectrow_query($form, $dbh, $query, $form->{login});
 
 250     qq|SELECT oe.id, oe.transdate, oe.reqdate, oe.quonumber, oe.transaction_description, oe.amount,
 
 251          CASE WHEN (COALESCE(oe.customer_id, 0) = 0) THEN 'vendor' ELSE 'customer' END AS vc,
 
 256        LEFT JOIN customer c ON (oe.customer_id = c.id)
 
 257        LEFT JOIN vendor v   ON (oe.vendor_id   = v.id)
 
 258        LEFT JOIN employee e ON (oe.employee_id = e.id)
 
 259        WHERE (COALESCE(quotation, FALSE) = TRUE)
 
 260          AND (COALESCE(closed,    FALSE) = FALSE)
 
 261          AND ((oe.employee_id = ?) OR (oe.salesman_id = ?))
 
 262          AND NOT (oe.reqdate ISNULL)
 
 263          AND (oe.reqdate < current_date)
 
 266   my $quotations = selectall_hashref_query($form, $dbh, $query, $e_id, $e_id);
 
 268   $main::lxdebug->leave_sub();
 
 274   $main::lxdebug->enter_sub();
 
 276   my ($self, $myconfig, $form) = @_;
 
 278   # connect to database, turn off autocommit
 
 279   my $dbh = $form->get_standard_dbh;
 
 281   my ($query, @values, $sth, $null);
 
 282   my $exchangerate = 0;
 
 284   my $all_units = AM->retrieve_units($myconfig, $form);
 
 285   $form->{all_units} = $all_units;
 
 287   my $ic_cvar_configs = CVar->get_configs(module => 'IC',
 
 290   $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id};
 
 291   unless ($form->{employee_id}) {
 
 292     $form->get_employee($dbh);
 
 295   my $ml = ($form->{type} eq 'sales_order') ? 1 : -1;
 
 298     $query = qq|DELETE FROM custom_variables
 
 299                 WHERE (config_id IN (SELECT id FROM custom_variable_configs WHERE module = 'IC'))
 
 300                   AND (sub_module = 'orderitems')
 
 301                   AND (trans_id IN (SELECT id FROM orderitems WHERE trans_id = ?))|;
 
 302     do_query($form, $dbh, $query, $form->{id});
 
 304     $query = qq|DELETE FROM orderitems WHERE trans_id = ?|;
 
 305     do_query($form, $dbh, $query, $form->{id});
 
 307     $query = qq|DELETE FROM shipto | .
 
 308              qq|WHERE trans_id = ? AND module = 'OE'|;
 
 309     do_query($form, $dbh, $query, $form->{id});
 
 313     $query = qq|SELECT nextval('id')|;
 
 314     ($form->{id}) = selectrow_query($form, $dbh, $query);
 
 316     $query = qq|INSERT INTO oe (id, ordnumber, employee_id, currency_id) VALUES (?, '', ?, (SELECT currency_id FROM defaults))|;
 
 317     do_query($form, $dbh, $query, $form->{id}, $form->{employee_id});
 
 335   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
 
 336   my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
 
 339   for my $i (1 .. $form->{rowcount}) {
 
 341     map({ $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) } qw(qty ship));
 
 343     if ($form->{"id_$i"}) {
 
 346       $query = qq|SELECT unit FROM parts WHERE id = ?|;
 
 347       my ($item_unit) = selectrow_query($form, $dbh, $query, $form->{"id_$i"});
 
 350       if (defined($all_units->{$item_unit}->{factor}) &&
 
 351           (($all_units->{$item_unit}->{factor} * 1) != 0)) {
 
 352         $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
 
 354       my $baseqty = $form->{"qty_$i"} * $basefactor;
 
 356       $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
 
 357       $form->{"marge_absolut_$i"} = $form->parse_amount($myconfig, $form->{"marge_absolut_$i"}) * 1;
 
 359       $form->{"lastcost_$i"} = $form->parse_amount($myconfig, $form->{"lastcost_$i"});
 
 361       # set values to 0 if nothing entered
 
 362       $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
 
 364       $form->{"sellprice_$i"} = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
 365       $fxsellprice = $form->{"sellprice_$i"};
 
 367       my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
 369       my $decimalplaces = ($dec > 2) ? $dec : 2;
 
 371       $discount = $form->round_amount($form->{"sellprice_$i"} * $form->{"discount_$i"}, $decimalplaces);
 
 372       $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"} - $discount, $decimalplaces);
 
 374       $form->{"inventory_accno_$i"} *= 1;
 
 375       $form->{"expense_accno_$i"}   *= 1;
 
 377       $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
 
 378       $linetotal    = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
 
 380       @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
 
 384       map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
 
 386       if ($form->{taxincluded}) {
 
 387         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
 
 388         $taxbase   = $linetotal - $taxamount;
 
 390         # we are not keeping a natural price, do not round
 
 391         $form->{"sellprice_$i"} =
 
 392           $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
 
 394         $taxamount = $linetotal * $taxrate;
 
 395         $taxbase   = $linetotal;
 
 398       if ($form->round_amount($taxrate, 7) == 0) {
 
 399         if ($form->{taxincluded}) {
 
 400           foreach my $item (@taxaccounts) {
 
 401             $taxamount = $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
 
 402             $taxaccounts{$item} += $taxamount;
 
 403             $taxdiff            += $taxamount;
 
 404             $taxbase{$item}     += $taxbase;
 
 406           $taxaccounts{ $taxaccounts[0] } += $taxdiff;
 
 408           foreach my $item (@taxaccounts) {
 
 409             $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
 
 410             $taxbase{$item}     += $taxbase;
 
 414         foreach my $item (@taxaccounts) {
 
 415           $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
 
 416           $taxbase{$item} += $taxbase;
 
 420       $netamount += $form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor;
 
 422       $reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef;
 
 424       # Get pricegroup_id and save it. Unfortunately the interface
 
 425       # also uses ID "0" for signalling that none is selected, but "0"
 
 426       # must not be stored in the database. Therefore we cannot simply
 
 428       ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
 
 430       $pricegroup_id  = undef if !$pricegroup_id;
 
 432       # save detail record in orderitems table
 
 433       my $orderitems_id = $form->{"orderitems_id_$i"};
 
 434       ($orderitems_id)  = selectfirst_array_query($form, $dbh, qq|SELECT nextval('orderitemsid')|) if (!$orderitems_id);
 
 437       $query = qq|INSERT INTO orderitems (
 
 438                     id, trans_id, parts_id, description, longdescription, qty, base_qty,
 
 439                     sellprice, discount, unit, reqdate, project_id, serialnumber, ship,
 
 440                     pricegroup_id, ordnumber, transdate, cusordnumber, subtotal,
 
 441                     marge_percent, marge_total, lastcost, price_factor_id, price_factor, marge_price_factor)
 
 442                   VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
 
 443                           (SELECT factor FROM price_factors WHERE id = ?), ?)|;
 
 445            conv_i($orderitems_id), conv_i($form->{id}), conv_i($form->{"id_$i"}),
 
 446            $form->{"description_$i"}, $form->{"longdescription_$i"},
 
 447            $form->{"qty_$i"}, $baseqty,
 
 448            $fxsellprice, $form->{"discount_$i"},
 
 449            $form->{"unit_$i"}, conv_date($reqdate), conv_i($form->{"project_id_$i"}),
 
 450            $form->{"serialnumber_$i"}, $form->{"ship_$i"}, $pricegroup_id,
 
 451            $form->{"ordnumber_$i"}, conv_date($form->{"transdate_$i"}),
 
 452            $form->{"cusordnumber_$i"}, $form->{"subtotal_$i"} ? 't' : 'f',
 
 453            $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
 
 454            $form->{"lastcost_$i"},
 
 455            conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
 
 456            conv_i($form->{"marge_price_factor_$i"}));
 
 457       do_query($form, $dbh, $query, @values);
 
 459       $form->{"sellprice_$i"} = $fxsellprice;
 
 460       $form->{"discount_$i"} *= 100;
 
 462       CVar->save_custom_variables(module       => 'IC',
 
 463                                   sub_module   => 'orderitems',
 
 464                                   trans_id     => $orderitems_id,
 
 465                                   configs      => $ic_cvar_configs,
 
 467                                   name_prefix  => 'ic_',
 
 468                                   name_postfix => "_$i",
 
 473   $reqdate = ($form->{reqdate}) ? $form->{reqdate} : undef;
 
 477   map { $tax += $form->round_amount($taxaccounts{$_}, 2) } keys %taxaccounts;
 
 479   $amount = $form->round_amount($netamount + $tax, 2);
 
 480   $netamount = $form->round_amount($netamount, 2);
 
 482   if ($form->{currency} eq $form->{defaultcurrency}) {
 
 483     $form->{exchangerate} = 1;
 
 485     $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? 'buy' : 'sell');
 
 488   $form->{exchangerate} = $exchangerate || $form->parse_amount($myconfig, $form->{exchangerate});
 
 490   my $quotation = $form->{type} =~ /_order$/ ? 'f' : 't';
 
 492   ($null, $form->{department_id}) = split(/--/, $form->{department}) if $form->{department};
 
 497          ordnumber = ?, quonumber = ?, cusordnumber = ?, transdate = ?, vendor_id = ?,
 
 498          customer_id = ?, amount = ?, netamount = ?, reqdate = ?, taxincluded = ?,
 
 499          shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, currency_id = (SELECT id FROM currencies WHERE name=?), closed = ?,
 
 500          delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
 
 501          taxzone_id = ?, shipto_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,
 
 502          globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
 
 505   @values = ($form->{ordnumber} || '', $form->{quonumber},
 
 506              $form->{cusordnumber}, conv_date($form->{transdate}),
 
 507              conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
 
 508              $amount, $netamount, conv_date($reqdate),
 
 509              $form->{taxincluded} ? 't' : 'f', $form->{shippingpoint},
 
 510              $form->{shipvia}, $form->{notes}, $form->{intnotes},
 
 511              $form->{currency}, $form->{closed} ? 't' : 'f',
 
 512              $form->{delivered} ? "t" : "f", $form->{proforma} ? 't' : 'f',
 
 513              $quotation, conv_i($form->{department_id}),
 
 514              conv_i($form->{language_id}), conv_i($form->{taxzone_id}),
 
 515              conv_i($form->{shipto_id}), conv_i($form->{payment_id}),
 
 516              conv_i($form->{delivery_vendor_id}),
 
 517              conv_i($form->{delivery_customer_id}),
 
 518              conv_i($form->{globalproject_id}), conv_i($form->{employee_id}),
 
 519              conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
 
 520              $form->{transaction_description},
 
 521              $form->{marge_total} * 1, $form->{marge_percent} * 1,
 
 522              conv_i($form->{id}));
 
 523   do_query($form, $dbh, $query, @values);
 
 525   $form->{ordtotal} = $amount;
 
 528   $form->{name} = $form->{ $form->{vc} };
 
 529   $form->{name} =~ s/--\Q$form->{"$form->{vc}_id"}\E//;
 
 531   if (!$form->{shipto_id}) {
 
 532     $form->add_shipto($dbh, $form->{id}, "OE");
 
 535   # save printed, emailed, queued
 
 536   $form->save_status($dbh);
 
 538   # Link this record to the records it was created from.
 
 539   $form->{convert_from_oe_ids} =~ s/^\s+//;
 
 540   $form->{convert_from_oe_ids} =~ s/\s+$//;
 
 541   my @convert_from_oe_ids      =  split m/\s+/, $form->{convert_from_oe_ids};
 
 542   delete $form->{convert_from_oe_ids};
 
 544   if (scalar @convert_from_oe_ids) {
 
 545     RecordLinks->create_links('dbh'        => $dbh,
 
 547                               'from_table' => 'oe',
 
 548                               'from_ids'   => \@convert_from_oe_ids,
 
 550                               'to_id'      => $form->{id},
 
 553     $self->_close_quotations_rfqs('dbh'     => $dbh,
 
 554                                   'from_id' => \@convert_from_oe_ids,
 
 555                                   'to_id'   => $form->{id});
 
 558   if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
 
 559     if ($form->{vc} eq 'customer') {
 
 560       $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
 
 562     if ($form->{vc} eq 'vendor') {
 
 563       $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
 
 567   $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ?
 
 568                                        "quonumber" : "ordnumber"};
 
 570   Common::webdav_folder($form);
 
 572   my $rc = $dbh->commit;
 
 574   $self->save_periodic_invoices_config(dbh         => $dbh,
 
 575                                        oe_id       => $form->{id},
 
 576                                        config_yaml => $form->{periodic_invoices_config})
 
 577     if ($form->{type} eq 'sales_order');
 
 579   $main::lxdebug->leave_sub();
 
 584 sub save_periodic_invoices_config {
 
 585   my ($self, %params) = @_;
 
 587   return if !$params{oe_id};
 
 589   my $config = $params{config_yaml} ? YAML::Load($params{config_yaml}) : undef;
 
 590   return if 'HASH' ne ref $config;
 
 592   my $obj  = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $params{oe_id})
 
 593           || SL::DB::PeriodicInvoicesConfig->new(oe_id => $params{oe_id});
 
 594   $obj->update_attributes(%{ $config });
 
 597 sub load_periodic_invoice_config {
 
 601   delete $form->{periodic_invoices_config};
 
 604     my $config_obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $form->{id});
 
 607       my $config = { map { $_ => $config_obj->$_ } qw(active terminated periodicity start_date_as_date end_date_as_date extend_automatically_by ar_chart_id
 
 608                                                       print printer_id copies) };
 
 609       $form->{periodic_invoices_config} = YAML::Dump($config);
 
 614 sub _close_quotations_rfqs {
 
 615   $main::lxdebug->enter_sub();
 
 620   Common::check_params(\%params, qw(from_id to_id));
 
 622   my $myconfig = \%main::myconfig;
 
 623   my $form     = $main::form;
 
 625   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
 
 627   my $query    = qq|SELECT quotation FROM oe WHERE id = ?|;
 
 628   my $sth      = prepare_query($form, $dbh, $query);
 
 630   do_statement($form, $sth, $query, conv_i($params{to_id}));
 
 632   my ($quotation) = $sth->fetchrow_array();
 
 635     $main::lxdebug->leave_sub();
 
 641   foreach my $from_id (@{ $params{from_id} }) {
 
 642     $from_id = conv_i($from_id);
 
 643     do_statement($form, $sth, $query, $from_id);
 
 644     ($quotation) = $sth->fetchrow_array();
 
 645     push @close_ids, $from_id if ($quotation);
 
 650   if (scalar @close_ids) {
 
 651     $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_ids) . qq|)|;
 
 652     do_query($form, $dbh, $query, @close_ids);
 
 654     $dbh->commit() unless ($params{dbh});
 
 657   $main::lxdebug->leave_sub();
 
 661   $main::lxdebug->enter_sub();
 
 663   my ($self, $myconfig, $form) = @_;
 
 665   my $rc = SL::DB::Order->new->db->with_transaction(sub {
 
 666     my @spoolfiles = grep { $_ } map { $_->spoolfile } @{ SL::DB::Manager::Status->get_all(where => [ trans_id => $form->{id} ]) };
 
 668     SL::DB::Order->new(id => $form->{id})->delete;
 
 670     my $spool = $::lx_office_conf{paths}->{spool};
 
 671     unlink map { "$spool/$_" } @spoolfiles if $spool;
 
 676   $main::lxdebug->leave_sub();
 
 682   $main::lxdebug->enter_sub();
 
 684   my ($self, $myconfig, $form) = @_;
 
 686   # connect to database
 
 687   my $dbh = $form->get_standard_dbh;
 
 689   my ($query, $query_add, @values, @ids, $sth);
 
 691   # translate the ids (given by id_# and trans_id_#) into one array of ids, so we can join them later
 
 693     push @ids, $form->{"trans_id_$_"}
 
 694       if ($form->{"multi_id_$_"} and $form->{"trans_id_$_"})
 
 695   } (1 .. $form->{"rowcount"});
 
 697   if ($form->{rowcount} && scalar @ids) {
 
 698     $form->{convert_from_oe_ids} = join ' ', @ids;
 
 701   # if called in multi id mode, and still only got one id, switch back to single id
 
 702   if ($form->{"rowcount"} and $#ids == 0) {
 
 703     $form->{"id"} = $ids[0];
 
 707   # and remember for the rest of the function
 
 708   my $is_collective_order = scalar @ids;
 
 711     my $wday         = (localtime(time))[6];
 
 712     my $next_workday = $wday == 5 ? 3 : $wday == 6 ? 2 : 1;
 
 713     $query_add       = qq|, current_date AS transdate, date(current_date + interval '${next_workday} days') AS reqdate|;
 
 716   # get default accounts
 
 717   $query = qq|SELECT (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
 
 718                      (SELECT c.accno FROM chart c WHERE d.income_accno_id    = c.id) AS income_accno,
 
 719                      (SELECT c.accno FROM chart c WHERE d.expense_accno_id   = c.id) AS expense_accno,
 
 720                      (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id    = c.id) AS fxgain_accno,
 
 721                      (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id    = c.id) AS fxloss_accno
 
 724   my $ref = selectfirst_hashref_query($form, $dbh, $query);
 
 725   map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 727   $form->{currency} = $form->get_default_currency($myconfig);
 
 729   # set reqdate if this is an invoice->order conversion. If someone knows a better check to ensure
 
 730   # we come from invoices, feel free.
 
 731   $form->{reqdate} = $form->{deliverydate}
 
 732     if (    $form->{deliverydate}
 
 733         and $form->{callback} =~ /action=ar_transactions/);
 
 735   my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
 
 737   if ($form->{id} or @ids) {
 
 739     # retrieve order for single id
 
 740     # NOTE: this query is intended to fetch all information only ONCE.
 
 741     # so if any of these infos is important (or even different) for any item,
 
 742     # it will be killed out and then has to be fetched from the item scope query further down
 
 744       qq|SELECT o.cp_id, o.ordnumber, o.transdate, o.reqdate,
 
 745            o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
 
 746            (SELECT cu.name FROM currencies cu WHERE cu.id=o.currency_id) AS currency, e.name AS employee, o.employee_id, o.salesman_id,
 
 747            o.${vc}_id, cv.name AS ${vc}, o.amount AS invtotal,
 
 748            o.closed, o.reqdate, o.quonumber, o.department_id, o.cusordnumber,
 
 749            d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
 
 750            o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id,
 
 751            o.globalproject_id, o.delivered, o.transaction_description
 
 753          JOIN ${vc} cv ON (o.${vc}_id = cv.id)
 
 754          LEFT JOIN employee e ON (o.employee_id = e.id)
 
 755          LEFT JOIN department d ON (o.department_id = d.id) | .
 
 758          : "WHERE o.id IN (" . join(', ', map("? ", @ids)) . ")"
 
 760     @values = $form->{id} ? ($form->{id}) : @ids;
 
 761     $sth = prepare_execute_query($form, $dbh, $query, @values);
 
 763     $ref = $sth->fetchrow_hashref("NAME_lc");
 
 766       map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 768       $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ? "quonumber" : "ordnumber"};
 
 770       # set all entries for multiple ids blank that yield different information
 
 771       while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 772         map { $form->{$_} = '' if ($ref->{$_} ne $form->{$_}) } keys %$ref;
 
 776     # if not given, fill transdate with current_date
 
 777     $form->{transdate} = $form->current_date($myconfig)
 
 778       unless $form->{transdate};
 
 782     if ($form->{delivery_customer_id}) {
 
 783       $query = qq|SELECT name FROM customer WHERE id = ?|;
 
 784       ($form->{delivery_customer_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_customer_id});
 
 787     if ($form->{delivery_vendor_id}) {
 
 788       $query = qq|SELECT name FROM customer WHERE id = ?|;
 
 789       ($form->{delivery_vendor_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_vendor_id});
 
 792     # shipto and pinted/mailed/queued status makes only sense for single id retrieve
 
 794       $query = qq|SELECT s.* FROM shipto s WHERE s.trans_id = ? AND s.module = 'OE'|;
 
 795       $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
 
 797       $ref = $sth->fetchrow_hashref("NAME_lc");
 
 799       map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 802       # get printed, emailed and queued
 
 803       $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
 
 804       $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
 
 806       while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 807         $form->{printed} .= "$ref->{formname} " if $ref->{printed};
 
 808         $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
 
 809         $form->{queued}  .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
 
 812       map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
 
 815     my $transdate = $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
 
 817     $form->{taxzone_id} = 0 unless ($form->{taxzone_id});
 
 819     # retrieve individual items
 
 820     # this query looks up all information about the items
 
 821     # stuff different from the whole will not be overwritten, but saved with a suffix.
 
 823       qq|SELECT o.id AS orderitems_id,
 
 824            c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from as inventory_valid,
 
 825            c2.accno AS income_accno,    c2.new_chart_id AS income_new_chart,    date($transdate) - c2.valid_from as income_valid,
 
 826            c3.accno AS expense_accno,   c3.new_chart_id AS expense_new_chart,   date($transdate) - c3.valid_from as expense_valid,
 
 827            oe.ordnumber AS ordnumber_oe, oe.transdate AS transdate_oe, oe.cusordnumber AS cusordnumber_oe,
 
 828            p.partnumber, p.assembly, p.listprice, o.description, o.qty,
 
 829            o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id,
 
 830            o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
 
 831            o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
 
 832            o.price_factor_id, o.price_factor, o.marge_price_factor,
 
 833            pr.projectnumber, p.formel,
 
 834            pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
 
 836          JOIN parts p ON (o.parts_id = p.id)
 
 837          JOIN oe ON (o.trans_id = oe.id)
 
 838          LEFT JOIN chart c1 ON ((SELECT inventory_accno_id                   FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c1.id)
 
 839          LEFT JOIN chart c2 ON ((SELECT income_accno_id_$form->{taxzone_id}  FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c2.id)
 
 840          LEFT JOIN chart c3 ON ((SELECT expense_accno_id_$form->{taxzone_id} FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c3.id)
 
 841          LEFT JOIN project pr ON (o.project_id = pr.id)
 
 842          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
 
 844        ? qq|WHERE o.trans_id = ?|
 
 845        : qq|WHERE o.trans_id IN (| . join(", ", map("?", @ids)) . qq|)|) .
 
 848     @ids = $form->{id} ? ($form->{id}) : @ids;
 
 849     $sth = prepare_execute_query($form, $dbh, $query, @values);
 
 851     while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 852       # Retrieve custom variables.
 
 853       my $cvars = CVar->get_custom_variables(dbh        => $dbh,
 
 855                                              sub_module => 'orderitems',
 
 856                                              trans_id   => $ref->{orderitems_id},
 
 858       map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
 
 861       if (!$ref->{"part_inventory_accno_id"}) {
 
 862         map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid));
 
 864       delete($ref->{"part_inventory_accno_id"});
 
 866       # in collective order, copy global ordnumber, transdate, cusordnumber into item scope
 
 867       #   unless already present there
 
 868       # remove _oe entries afterwards
 
 869       map { $ref->{$_} = $ref->{"${_}_oe"} if ($ref->{$_} eq '') }
 
 870         qw|ordnumber transdate cusordnumber|
 
 872       map { delete $ref->{$_} } qw|ordnumber_oe transdate_oe cusordnumber_oe|;
 
 876       while ($ref->{inventory_new_chart} && ($ref->{inventory_valid} >= 0)) {
 
 878           qq|SELECT accno AS inventory_accno, | .
 
 879           qq|  new_chart_id AS inventory_new_chart, | .
 
 880           qq|  date($transdate) - valid_from AS inventory_valid | .
 
 881           qq|FROM chart WHERE id = $ref->{inventory_new_chart}|;
 
 882         ($ref->{inventory_accno}, $ref->{inventory_new_chart},
 
 883          $ref->{inventory_valid}) = selectrow_query($form, $dbh, $query);
 
 886       while ($ref->{income_new_chart} && ($ref->{income_valid} >= 0)) {
 
 888           qq|SELECT accno AS income_accno, | .
 
 889           qq|  new_chart_id AS income_new_chart, | .
 
 890           qq|  date($transdate) - valid_from AS income_valid | .
 
 891           qq|FROM chart WHERE id = $ref->{income_new_chart}|;
 
 892         ($ref->{income_accno}, $ref->{income_new_chart},
 
 893          $ref->{income_valid}) = selectrow_query($form, $dbh, $query);
 
 896       while ($ref->{expense_new_chart} && ($ref->{expense_valid} >= 0)) {
 
 898           qq|SELECT accno AS expense_accno, | .
 
 899           qq|  new_chart_id AS expense_new_chart, | .
 
 900           qq|  date($transdate) - valid_from AS expense_valid | .
 
 901           qq|FROM chart WHERE id = $ref->{expense_new_chart}|;
 
 902         ($ref->{expense_accno}, $ref->{expense_new_chart},
 
 903          $ref->{expense_valid}) = selectrow_query($form, $dbh, $query);
 
 906       # delete orderitems_id in collective orders, so that they get cloned no matter what
 
 907       delete $ref->{orderitems_id} if (@ids);
 
 909       # get tax rates and description
 
 910       my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
 
 912         qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber | .
 
 913         qq|FROM tax t LEFT JOIN chart c on (c.id = t.chart_id) | .
 
 914         qq|WHERE t.id IN (SELECT tk.tax_id FROM taxkeys tk | .
 
 915         qq|               WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?) | .
 
 916         qq|                 AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1) | .
 
 917         qq|ORDER BY c.accno|;
 
 918       my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
 
 919       $ref->{taxaccounts} = "";
 
 921       while (my $ptr = $stw->fetchrow_hashref("NAME_lc")) {
 
 922         if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
 
 926         $ref->{taxaccounts} .= "$ptr->{accno} ";
 
 927         if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
 
 928           $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
 
 929           $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
 
 930           $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
 
 931           $form->{taxaccounts} .= "$ptr->{accno} ";
 
 936       chop $ref->{taxaccounts};
 
 938       push @{ $form->{form_details} }, $ref;
 
 946     $form->lastname_used($dbh, $myconfig, $form->{vc})
 
 947       unless $form->{"$form->{vc}_id"};
 
 951   $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? "buy" : "sell");
 
 953   Common::webdav_folder($form);
 
 955   $self->load_periodic_invoice_config($form);
 
 957   my $rc = $dbh->commit;
 
 959   $main::lxdebug->leave_sub();
 
 964 sub retrieve_simple {
 
 965   $main::lxdebug->enter_sub();
 
 970   Common::check_params(\%params, qw(id));
 
 972   my $myconfig    = \%main::myconfig;
 
 973   my $form        = $main::form;
 
 975   my $dbh         = $params{dbh} || $form->get_standard_dbh($myconfig);
 
 977   my $oe_query    = qq|SELECT * FROM oe         WHERE id = ?|;
 
 978   my $oi_query    = qq|SELECT * FROM orderitems WHERE trans_id = ?|;
 
 980   my $order            = selectfirst_hashref_query($form, $dbh, $oe_query, conv_i($params{id}));
 
 981   $order->{orderitems} = selectall_hashref_query(  $form, $dbh, $oi_query, conv_i($params{id}));
 
 983   $main::lxdebug->leave_sub();
 
 989   $main::lxdebug->enter_sub();
 
 991   my ($self, $myconfig, $form) = @_;
 
 993   # connect to database
 
 994   my $dbh = $form->get_standard_dbh;
 
1000   my $nodiscount_subtotal = 0;
 
1001   my $discount_subtotal = 0;
 
1004   my @partsgroup = ();
 
1007   my $subtotal_header = 0;
 
1008   my $subposition = 0;
 
1014   my (@project_ids, %projectnumbers, %projectdescriptions);
 
1016   push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
 
1018   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
 
1019                    'departments'   => 'ALL_DEPARTMENTS');
 
1022   foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
 
1023     $price_factors{$pfac->{id}}  = $pfac;
 
1024     $pfac->{factor}             *= 1;
 
1025     $pfac->{formatted_factor}    = $form->format_amount($myconfig, $pfac->{factor});
 
1029   foreach my $dept (@{ $form->{ALL_DEPARTMENTS} }) {
 
1030     next unless $dept->{id} eq $form->{department_id};
 
1031     $form->{department} = $dept->{description};
 
1035   # sort items by partsgroup
 
1036   for $i (1 .. $form->{rowcount}) {
 
1038     if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
 
1039       $partsgroup = $form->{"partsgroup_$i"};
 
1041     push @partsgroup, [$i, $partsgroup];
 
1042     push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
 
1046     $query = "SELECT id, projectnumber, description FROM project WHERE id IN (" .
 
1047       join(", ", map("?", @project_ids)) . ")";
 
1048     $sth = prepare_execute_query($form, $dbh, $query, @project_ids);
 
1049     while (my $ref = $sth->fetchrow_hashref()) {
 
1050       $projectnumbers{$ref->{id}} = $ref->{projectnumber};
 
1051       $projectdescriptions{$ref->{id}} = $ref->{description};
 
1056   $form->{"globalprojectnumber"} = $projectnumbers{$form->{"globalproject_id"}};
 
1057   $form->{"globalprojectdescription"} = $projectdescriptions{$form->{"globalproject_id"}};
 
1059   $form->{discount} = [];
 
1061   $form->{TEMPLATE_ARRAYS} = { };
 
1062   IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
 
1064   my $ic_cvar_configs = CVar->get_configs(module => 'IC');
 
1067     qw(runningnumber number description longdescription qty ship unit bin
 
1068        partnotes serialnumber reqdate sellprice listprice netprice
 
1069        discount p_discount discount_sub nodiscount_sub
 
1070        linetotal  nodiscount_linetotal tax_rate projectnumber projectdescription
 
1071        price_factor price_factor_name partsgroup weight lineweight);
 
1073   push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
 
1075   my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
 
1077   map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays);
 
1079   my $totalweight = 0;
 
1081   foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
 
1084     if ($item->[1] ne $sameitem) {
 
1085       push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
 
1086       $sameitem = $item->[1];
 
1088       map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
1091     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
 
1093     if ($form->{"id_$i"} != 0) {
 
1095       # add number, description and qty to $form->{number}, ....
 
1097       if ($form->{"subtotal_$i"} && !$subtotal_header) {
 
1098         $subtotal_header = $i;
 
1099         $position = int($position);
 
1102       } elsif ($subtotal_header) {
 
1104         $position = int($position);
 
1105         $position = $position.".".$subposition;
 
1107         $position = int($position);
 
1111       my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
 
1113       push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} },     $position;
 
1114       push @{ $form->{TEMPLATE_ARRAYS}->{number} },            $form->{"partnumber_$i"};
 
1115       push @{ $form->{TEMPLATE_ARRAYS}->{description} },       $form->{"description_$i"};
 
1116       push @{ $form->{TEMPLATE_ARRAYS}->{longdescription} },   $form->{"longdescription_$i"};
 
1117       push @{ $form->{TEMPLATE_ARRAYS}->{qty} },               $form->format_amount($myconfig, $form->{"qty_$i"});
 
1118       push @{ $form->{TEMPLATE_ARRAYS}->{qty_nofmt} },         $form->{"qty_$i"};
 
1119       push @{ $form->{TEMPLATE_ARRAYS}->{ship} },              $form->format_amount($myconfig, $form->{"ship_$i"});
 
1120       push @{ $form->{TEMPLATE_ARRAYS}->{ship_nofmt} },        $form->{"ship_$i"};
 
1121       push @{ $form->{TEMPLATE_ARRAYS}->{unit} },              $form->{"unit_$i"};
 
1122       push @{ $form->{TEMPLATE_ARRAYS}->{bin} },               $form->{"bin_$i"};
 
1123       push @{ $form->{TEMPLATE_ARRAYS}->{partnotes} },         $form->{"partnotes_$i"};
 
1124       push @{ $form->{TEMPLATE_ARRAYS}->{serialnumber} },      $form->{"serialnumber_$i"};
 
1125       push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} },           $form->{"reqdate_$i"};
 
1126       push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} },         $form->{"sellprice_$i"};
 
1127       push @{ $form->{TEMPLATE_ARRAYS}->{sellprice_nofmt} },   $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
1128       push @{ $form->{TEMPLATE_ARRAYS}->{listprice} },         $form->{"listprice_$i"};
 
1129       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} },      $price_factor->{formatted_factor};
 
1130       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
 
1131       push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} },        $form->{"partsgroup_$i"};
 
1133       my $sellprice     = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
1134       my ($dec)         = ($sellprice =~ /\.(\d+)/);
 
1135       my $decimalplaces = max 2, length($dec);
 
1137       my $parsed_discount            = $form->parse_amount($myconfig, $form->{"discount_$i"});
 
1139       my $linetotal_exact            = $form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor};
 
1140       my $linetotal                  = $form->round_amount($linetotal_exact, 2);
 
1142       my $nodiscount_exact_linetotal = $form->{"qty_$i"} * $sellprice                                  / $price_factor->{factor};
 
1143       my $nodiscount_linetotal       = $form->round_amount($nodiscount_exact_linetotal,2);
 
1145       my $discount                   = $nodiscount_linetotal - $linetotal; # is always rounded because $nodiscount_linetotal and $linetotal are rounded
 
1147       my $discount_round_error       = $discount + ($linetotal_exact - $nodiscount_exact_linetotal); # not used
 
1149       $form->{"netprice_$i"}   = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, 2);
 
1151       push @{ $form->{TEMPLATE_ARRAYS}->{netprice} },       ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
 
1152       push @{ $form->{TEMPLATE_ARRAYS}->{netprice_nofmt} }, ($form->{"netprice_$i"} != 0) ? $form->{"netprice_$i"} : '';
 
1154       $linetotal = ($linetotal != 0) ? $linetotal : '';
 
1156       push @{ $form->{TEMPLATE_ARRAYS}->{discount} },       ($discount != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
 
1157       push @{ $form->{TEMPLATE_ARRAYS}->{discount_nofmt} }, ($discount != 0) ? $discount * -1 : '';
 
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}->{discount_sub_nofmt} },   $discount_subtotal;
 
1172         push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} },       $form->format_amount($myconfig, $nodiscount_subtotal, 2);
 
1173         push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub_nofmt} }, $nodiscount_subtotal;
 
1175         $discount_subtotal   = 0;
 
1176         $nodiscount_subtotal = 0;
 
1177         $subtotal_header     = 0;
 
1180         push @{ $form->{TEMPLATE_ARRAYS}->{$_} }, "" for qw(discount_sub nodiscount_sub discount_sub_nofmt nodiscount_sub_nofmt);
 
1183       if (!$form->{"discount_$i"}) {
 
1184         $nodiscount += $linetotal;
 
1187       push @{ $form->{TEMPLATE_ARRAYS}->{linetotal} },                  $form->format_amount($myconfig, $linetotal, 2);
 
1188       push @{ $form->{TEMPLATE_ARRAYS}->{linetotal_nofmt} },            $linetotal_exact;
 
1189       push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} },       $form->format_amount($myconfig, $nodiscount_linetotal, 2);
 
1190       push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal_nofmt} }, $nodiscount_linetotal;
 
1191       push(@{ $form->{TEMPLATE_ARRAYS}->{projectnumber} },              $projectnumbers{$form->{"project_id_$i"}});
 
1192       push(@{ $form->{TEMPLATE_ARRAYS}->{projectdescription} },         $projectdescriptions{$form->{"project_id_$i"}});
 
1194       my $lineweight = $form->{"qty_$i"} * $form->{"weight_$i"};
 
1195       $totalweight += $lineweight;
 
1196       push @{ $form->{TEMPLATE_ARRAYS}->{weight} },            $form->format_amount($myconfig, $form->{"weight_$i"}, 3);
 
1197       push @{ $form->{TEMPLATE_ARRAYS}->{weight_nofmt} },      $form->{"weight_$i"};
 
1198       push @{ $form->{TEMPLATE_ARRAYS}->{lineweight} },        $form->format_amount($myconfig, $lineweight, 3);
 
1199       push @{ $form->{TEMPLATE_ARRAYS}->{lineweight_nofmt} },  $lineweight;
 
1201       my ($taxamount, $taxbase);
 
1204       map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
 
1206       if ($form->{taxincluded}) {
 
1209         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
 
1210         $taxbase = $linetotal / (1 + $taxrate);
 
1212         $taxamount = $linetotal * $taxrate;
 
1213         $taxbase   = $linetotal;
 
1216       if ($taxamount != 0) {
 
1217         foreach my $accno (split / /, $form->{"taxaccounts_$i"}) {
 
1218           $taxaccounts{$accno} += $taxamount * $form->{"${accno}_rate"} / $taxrate;
 
1219           $taxbase{$accno}     += $taxbase;
 
1223       $tax_rate = $taxrate * 100;
 
1224       push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
 
1226       if ($form->{"assembly_$i"}) {
 
1229         # get parts and push them onto the stack
 
1231         if ($form->{groupitems}) {
 
1232           $sortorder = qq|ORDER BY pg.partsgroup, a.oid|;
 
1234           $sortorder = qq|ORDER BY a.oid|;
 
1237         $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty, | .
 
1238                  qq|pg.partsgroup | .
 
1239                  qq|FROM assembly a | .
 
1240                  qq|  JOIN parts p ON (a.parts_id = p.id) | .
 
1241                  qq|    LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
 
1242                  qq|    WHERE a.bom = '1' | .
 
1243                  qq|    AND a.id = ? | . $sortorder;
 
1244         @values = ($form->{"id_$i"});
 
1245         $sth = $dbh->prepare($query);
 
1246         $sth->execute(@values) || $form->dberror($query);
 
1248         while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
 
1249           if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
 
1250             map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
1251             $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
 
1252             push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
 
1255           push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq|, $ref->{partnumber}, $ref->{description}|);
 
1256           map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
 
1261       push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} },
 
1262         CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)
 
1263           for @{ $ic_cvar_configs };
 
1267   $form->{totalweight}       = $form->format_amount($myconfig, $totalweight, 3);
 
1268   $form->{totalweight_nofmt} = $totalweight;
 
1269   my $defaults = AM->get_defaults();
 
1270   $form->{weightunit}        = $defaults->{weightunit};
 
1273   foreach $item (sort keys %taxaccounts) {
 
1274     $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
 
1276     push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase} },        $form->format_amount($myconfig, $taxbase{$item}, 2));
 
1277     push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase_nofmt} },  $taxbase{$item});
 
1278     push(@{ $form->{TEMPLATE_ARRAYS}->{tax} },            $form->format_amount($myconfig, $taxamount,      2));
 
1279     push(@{ $form->{TEMPLATE_ARRAYS}->{tax_nofmt} },      $taxamount);
 
1280     push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} },        $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
 
1281     push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate_nofmt} },  $form->{"${item}_rate"} * 100);
 
1282     push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $form->{"${item}_description"} . q{ } . 100 * $form->{"${item}_rate"} . q{%});
 
1283     push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} },      $form->{"${item}_taxnumber"});
 
1286   $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
 
1287   $form->{discount_total}      = $form->format_amount($myconfig, $form->{discount_total}, 2);
 
1288   $form->{nodiscount}          = $form->format_amount($myconfig, $nodiscount, 2);
 
1289   $form->{yesdiscount}         = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
 
1291   if($form->{taxincluded}) {
 
1292     $form->{subtotal}       = $form->format_amount($myconfig, $form->{ordtotal} - $tax, 2);
 
1293     $form->{subtotal_nofmt} = $form->{ordtotal} - $tax;
 
1295     $form->{subtotal}       = $form->format_amount($myconfig, $form->{ordtotal}, 2);
 
1296     $form->{subtotal_nofmt} = $form->{ordtotal};
 
1299   $form->{ordtotal} = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
 
1302   $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
 
1304   if ($form->{type} =~ /_quotation/) {
 
1305     $form->set_payment_options($myconfig, $form->{quodate});
 
1307     $form->set_payment_options($myconfig, $form->{orddate});
 
1310   $form->{username} = $myconfig->{name};
 
1314   $main::lxdebug->leave_sub();
 
1317 sub project_description {
 
1318   $main::lxdebug->enter_sub();
 
1320   my ($self, $dbh, $id) = @_;
 
1322   my $query = qq|SELECT description FROM project WHERE id = ?|;
 
1323   my ($value) = selectrow_query($main::form, $dbh, $query, $id);
 
1325   $main::lxdebug->leave_sub();
 
1336 OE.pm - Order entry module
 
1340 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>.
 
1346 =item retrieve_simple PARAMS
 
1348 simple OE retrieval by id. does not look up customer, vendor, units or any other stuff. only oe and orderitems.
 
1350   my $order = retrieve_simple(id => 2);