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., 51 Franklin Street, Fifth Floor, Boston,
 
  30 #======================================================================
 
  34 #======================================================================
 
  38 use List::Util qw(max first);
 
  44 use SL::DB::PeriodicInvoicesConfig;
 
  46 use SL::DB::ProjectType;
 
  47 use SL::DB::RequirementSpecOrder;
 
  51 use SL::HTML::Restrict;
 
  54 use SL::Util qw(trim);
 
  62   $main::lxdebug->enter_sub();
 
  64   my ($self, $myconfig, $form) = @_;
 
  67   my $dbh = $form->get_standard_dbh;
 
  70   my $ordnumber = 'ordnumber';
 
  76   my ($periodic_invoices_columns, $periodic_invoices_joins);
 
  78   my $rate = ($form->{vc} eq 'customer') ? 'buy' : 'sell';
 
  80   if ($form->{type} =~ /_quotation$/) {
 
  82     $ordnumber = 'quonumber';
 
  84   } elsif ($form->{type} eq 'sales_order') {
 
  85     $periodic_invoices_columns = qq| , COALESCE(pcfg.active, 'f') AS periodic_invoices |;
 
  86     $periodic_invoices_joins   = qq| LEFT JOIN periodic_invoices_configs pcfg ON (o.id = pcfg.oe_id) |;
 
  89   my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
 
  93   if ($form->{l_remaining_amount} || $form->{l_remaining_netamount}) {
 
  95       SELECT from_id, ar.amount, ar.netamount FROM (
 
  98         WHERE from_table = 'oe' AND to_table = 'ar'
 
 100         SELECT rl1.from_id, rl2.to_id
 
 101         FROM record_links rl1
 
 102         LEFT JOIN record_links rl2 ON (rl1.to_table = rl2.from_table AND rl1.to_id = rl2.from_id)
 
 103         WHERE rl1.from_table = 'oe' AND rl2.to_table = 'ar'
 
 105         SELECT rl1.from_id, rl3.to_id
 
 106         FROM record_links rl1
 
 107         JOIN record_links rl2 ON (rl1.to_table = rl2.from_table AND rl1.to_id = rl2.from_id)
 
 108         JOIN record_links rl3 ON (rl2.to_table = rl3.from_table AND rl2.to_id = rl3.from_id)
 
 109         WHERE rl1.from_table = 'oe' AND rl2.to_table = 'ar' AND rl3.to_table = 'ar'
 
 111       LEFT JOIN ar ON ar.id = rl.to_id
 
 113     for my $ref (@{ selectall_hashref_query($form, $dbh, $query) }) {
 
 114       $billed_amount{   $ref->{from_id}} += $ref->{amount};
 
 115       $billed_netamount{$ref->{from_id}} += $ref->{netamount};
 
 120     qq|SELECT o.id, o.ordnumber, o.transdate, o.reqdate, | .
 
 121     qq|  o.amount, ct.${vc}number, ct.name, o.netamount, o.${vc}_id, o.globalproject_id, | .
 
 122     qq|  o.closed, o.delivered, o.quonumber, o.cusordnumber, o.shippingpoint, o.shipvia, | .
 
 123     qq|  o.transaction_description, | .
 
 124     qq|  o.marge_total, o.marge_percent, | .
 
 125     qq|  o.exchangerate, | .
 
 126     qq|  o.itime::DATE AS insertdate, | .
 
 128     qq|  department.description as department, | .
 
 129     qq|  ex.$rate AS daily_exchangerate, | .
 
 130     qq|  pt.description AS payment_terms, | .
 
 131     qq|  pr.projectnumber AS globalprojectnumber, | .
 
 132     qq|  e.name AS employee, s.name AS salesman, | .
 
 133     qq|  ct.${vc}number AS vcnumber, ct.country, ct.ustid, ct.business_id,  | .
 
 134     qq|  tz.description AS taxzone | .
 
 135     $periodic_invoices_columns .
 
 136     qq|  , o.order_probability, o.expected_billing_date, (o.netamount * o.order_probability / 100) AS expected_netamount | .
 
 138     qq|JOIN $vc ct ON (o.${vc}_id = ct.id) | .
 
 139     qq|LEFT JOIN contacts cp ON (o.cp_id = cp.cp_id) | .
 
 140     qq|LEFT JOIN employee e ON (o.employee_id = e.id) | .
 
 141     qq|LEFT JOIN employee s ON (o.salesman_id = s.id) | .
 
 142     qq|LEFT JOIN exchangerate ex ON (ex.currency_id = o.currency_id | .
 
 143     qq|  AND ex.transdate = o.transdate) | .
 
 144     qq|LEFT JOIN project pr ON (o.globalproject_id = pr.id) | .
 
 145     qq|LEFT JOIN payment_terms pt ON (pt.id = o.payment_id)| .
 
 146     qq|LEFT JOIN tax_zones tz ON (o.taxzone_id = tz.id) | .
 
 147     qq|LEFT JOIN department   ON (o.department_id = department.id) | .
 
 148     qq|$periodic_invoices_joins | .
 
 149     qq|WHERE (o.quotation = ?) |;
 
 150   push(@values, $quotation);
 
 152   if ($form->{department_id}) {
 
 153     $query .= qq| AND o.department_id = ?|;
 
 154     push(@values, $form->{department_id});
 
 157   if ($form->{"project_id"}) {
 
 159       qq|AND ((globalproject_id = ?) OR EXISTS | .
 
 160       qq|  (SELECT * FROM orderitems oi | .
 
 161       qq|   WHERE oi.project_id = ? AND oi.trans_id = o.id))|;
 
 162     push(@values, conv_i($form->{"project_id"}), conv_i($form->{"project_id"}));
 
 165   if ($form->{"projectnumber"}) {
 
 167       AND ((pr.projectnumber ILIKE ?) OR EXISTS (
 
 168         SELECT * FROM orderitems oi
 
 169         LEFT JOIN project proi ON proi.id = oi.project_id
 
 170         WHERE proi.projectnumber ILIKE ? AND oi.trans_id = o.id
 
 173     push @values, like($form->{"projectnumber"}), like($form->{"projectnumber"});
 
 176   if ($form->{"business_id"}) {
 
 177     $query .= " AND ct.business_id = ?";
 
 178     push(@values, $form->{"business_id"});
 
 181   if ($form->{"${vc}_id"}) {
 
 182     $query .= " AND o.${vc}_id = ?";
 
 183     push(@values, $form->{"${vc}_id"});
 
 185   } elsif ($form->{$vc}) {
 
 186     $query .= " AND ct.name ILIKE ?";
 
 187     push(@values, like($form->{$vc}));
 
 190   if ($form->{"cp_name"}) {
 
 191     $query .= " AND (cp.cp_name ILIKE ? OR cp.cp_givenname ILIKE ?)";
 
 192     push(@values, (like($form->{"cp_name"}))x2);
 
 195   if ( !(    ($vc eq 'customer' && ($main::auth->assert('sales_all_edit',    1) || $main::auth->assert('sales_order_view',    1)))
 
 196           || ($vc eq 'vendor'   && ($main::auth->assert('purchase_all_edit', 1) || $main::auth->assert('purchase_order_view', 1))) ) ) {
 
 197     $query .= " AND o.employee_id = (select id from employee where login= ?)";
 
 198     push @values, $::myconfig{login};
 
 200   if ($form->{employee_id}) {
 
 201     $query .= " AND o.employee_id = ?";
 
 202     push @values, conv_i($form->{employee_id});
 
 205   if ($form->{salesman_id}) {
 
 206     $query .= " AND o.salesman_id = ?";
 
 207     push @values, conv_i($form->{salesman_id});
 
 210   if (!$form->{open} && !$form->{closed}) {
 
 211     $query .= " AND o.id = 0";
 
 212   } elsif (!($form->{open} && $form->{closed})) {
 
 213     $query .= ($form->{open}) ? " AND o.closed = '0'" : " AND o.closed = '1'";
 
 216   if (($form->{"notdelivered"} || $form->{"delivered"}) &&
 
 217       ($form->{"notdelivered"} ne $form->{"delivered"})) {
 
 218     $query .= $form->{"delivered"} ?
 
 219       " AND o.delivered " : " AND NOT o.delivered";
 
 222   if ($form->{$ordnumber}) {
 
 223     $query .= qq| AND o.$ordnumber ILIKE ?|;
 
 224     push(@values, like($form->{$ordnumber}));
 
 227   if ($form->{cusordnumber}) {
 
 228     $query .= qq| AND o.cusordnumber ILIKE ?|;
 
 229     push(@values, like($form->{cusordnumber}));
 
 232   if($form->{transdatefrom}) {
 
 233     $query .= qq| AND o.transdate >= ?|;
 
 234     push(@values, conv_date($form->{transdatefrom}));
 
 237   if($form->{transdateto}) {
 
 238     $query .= qq| AND o.transdate <= ?|;
 
 239     push(@values, conv_date($form->{transdateto}));
 
 242   if($form->{reqdatefrom}) {
 
 243     $query .= qq| AND o.reqdate >= ?|;
 
 244     push(@values, conv_date($form->{reqdatefrom}));
 
 247   if($form->{reqdateto}) {
 
 248     $query .= qq| AND o.reqdate <= ?|;
 
 249     push(@values, conv_date($form->{reqdateto}));
 
 252   if($form->{insertdatefrom}) {
 
 253     $query .= qq| AND o.itime::DATE >= ?|;
 
 254     push(@values, conv_date($form->{insertdatefrom}));
 
 257   if($form->{insertdateto}) {
 
 258     $query .= qq| AND o.itime::DATE <= ?|;
 
 259     push(@values, conv_date($form->{insertdateto}));
 
 262   if ($form->{shippingpoint}) {
 
 263     $query .= qq| AND o.shippingpoint ILIKE ?|;
 
 264     push(@values, like($form->{shippingpoint}));
 
 267   if ($form->{taxzone_id} ne '') { # taxzone_id could be 0
 
 268     $query .= qq| AND tz.id = ?|;
 
 269     push(@values, $form->{taxzone_id});
 
 272   if ($form->{transaction_description}) {
 
 273     $query .= qq| AND o.transaction_description ILIKE ?|;
 
 274     push(@values, like($form->{transaction_description}));
 
 277   if ($form->{periodic_invoices_active} ne $form->{periodic_invoices_inactive}) {
 
 278     my $not  = $form->{periodic_invoices_inactive} ? 'NOT' : '';
 
 279     $query  .= qq| AND ${not} COALESCE(pcfg.active, 'f')|;
 
 282   if ($form->{reqdate_unset_or_old}) {
 
 283     $query .= qq| AND ((o.reqdate IS NULL) OR (o.reqdate < date_trunc('month', current_date)))|;
 
 286   if (($form->{order_probability_value} || '') ne '') {
 
 287     my $op  = $form->{order_probability_value} eq 'le' ? '<=' : '>=';
 
 288     $query .= qq| AND (o.order_probability ${op} ?)|;
 
 289     push @values, trim($form->{order_probability_value});
 
 292   if ($form->{expected_billing_date_from}) {
 
 293     $query .= qq| AND (o.expected_billing_date >= ?)|;
 
 294     push @values, conv_date($form->{expected_billing_date_from});
 
 297   if ($form->{expected_billing_date_to}) {
 
 298     $query .= qq| AND (o.expected_billing_date <= ?)|;
 
 299     push @values, conv_date($form->{expected_billing_date_to});
 
 302   if ($form->{intnotes}) {
 
 303     $query .= qq| AND o.intnotes ILIKE ?|;
 
 304     push(@values, like($form->{intnotes}));
 
 307   if ($form->{parts_partnumber}) {
 
 310         SELECT orderitems.trans_id
 
 312         LEFT JOIN parts ON (orderitems.parts_id = parts.id)
 
 313         WHERE (orderitems.trans_id = o.id)
 
 314           AND (parts.partnumber ILIKE ?)
 
 318     push @values, like($form->{parts_partnumber});
 
 321   if ($form->{parts_description}) {
 
 324         SELECT orderitems.trans_id
 
 326         WHERE (orderitems.trans_id = o.id)
 
 327           AND (orderitems.description ILIKE ?)
 
 331     push @values, like($form->{parts_description});
 
 335     my @tokens = parse_line('\s+', 0, $form->{all});
 
 336     # ordnumber quonumber customer.name vendor.name transaction_description
 
 338       o.ordnumber ILIKE ? OR
 
 339       o.quonumber ILIKE ? OR
 
 341       o.transaction_description ILIKE ?
 
 343     push @values, (like($_))x4 for @tokens;
 
 346   my ($cvar_where, @cvar_values) = CVar->build_filter_query('module'         => 'CT',
 
 347                                                             'trans_id_field' => 'ct.id',
 
 351     $query .= qq| AND ($cvar_where)|;
 
 352     push @values, @cvar_values;
 
 355   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
 
 356   my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name"), "o.itime"));
 
 357   my %allowed_sort_columns = (
 
 358     "transdate"               => "o.transdate",
 
 359     "reqdate"                 => "o.reqdate",
 
 361     "ordnumber"               => "o.ordnumber",
 
 362     "cusordnumber"            => "o.cusordnumber",
 
 363     "quonumber"               => "o.quonumber",
 
 365     "employee"                => "e.name",
 
 366     "salesman"                => "s.name",
 
 367     "shipvia"                 => "o.shipvia",
 
 368     "transaction_description" => "o.transaction_description",
 
 369     "shippingpoint"           => "o.shippingpoint",
 
 370     "insertdate"              => "o.itime",
 
 371     "taxzone"                 => "tz.description",
 
 372     "payment_terms"           => "pt.description",
 
 373     "department"              => "department.description",
 
 374     "intnotes"                => "o.intnotes",
 
 376   if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
 
 377     $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}"  . ", o.itime ${sortdir}";
 
 379   $query .= qq| ORDER by | . $sortorder;
 
 381   my $sth = $dbh->prepare($query);
 
 382   $sth->execute(@values) ||
 
 383     $form->dberror($query . " (" . join(", ", @values) . ")");
 
 387   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 388     $ref->{billed_amount}    = $billed_amount{$ref->{id}};
 
 389     $ref->{billed_netamount} = $billed_netamount{$ref->{id}};
 
 390     if ($ref->{billed_amount} < 0) { # case: credit note(s) higher than invoices
 
 391       $ref->{remaining_amount} = $ref->{amount} + $ref->{billed_amount};
 
 392       $ref->{remaining_netamount} = $ref->{netamount} + $ref->{billed_netamount};
 
 394       $ref->{remaining_amount} = $ref->{amount} - $ref->{billed_amount};
 
 395       $ref->{remaining_netamount} = $ref->{netamount} - $ref->{billed_netamount};
 
 397     $ref->{exchangerate} ||= $ref->{daily_exchangerate};
 
 398     $ref->{exchangerate} ||= 1;
 
 399     push @{ $form->{OE} }, $ref if $ref->{id} != $id{ $ref->{id} };
 
 400     $id{ $ref->{id} } = $ref->{id};
 
 405   $main::lxdebug->leave_sub();
 
 408 sub transactions_for_todo_list {
 
 409   $main::lxdebug->enter_sub();
 
 414   my $myconfig = \%main::myconfig;
 
 415   my $form     = $main::form;
 
 417   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
 
 419   my $query    = qq|SELECT id FROM employee WHERE login = ?|;
 
 420   my ($e_id)   = selectrow_query($form, $dbh, $query, $::myconfig{login});
 
 423     qq|SELECT oe.id, oe.transdate, oe.reqdate, oe.quonumber, oe.transaction_description, oe.amount,
 
 424          CASE WHEN (COALESCE(oe.customer_id, 0) = 0) THEN 'vendor' ELSE 'customer' END AS vc,
 
 429        LEFT JOIN customer c ON (oe.customer_id = c.id)
 
 430        LEFT JOIN vendor v   ON (oe.vendor_id   = v.id)
 
 431        LEFT JOIN employee e ON (oe.employee_id = e.id)
 
 432        WHERE (COALESCE(quotation, FALSE) = TRUE)
 
 433          AND (COALESCE(closed,    FALSE) = FALSE)
 
 434          AND ((oe.employee_id = ?) OR (oe.salesman_id = ?))
 
 435          AND NOT (oe.reqdate ISNULL)
 
 436          AND (oe.reqdate < current_date)
 
 439   my $quotations = selectall_hashref_query($form, $dbh, $query, $e_id, $e_id);
 
 441   $main::lxdebug->leave_sub();
 
 447   my ($self, $myconfig, $form) = @_;
 
 448   $main::lxdebug->enter_sub();
 
 450   my $rc = SL::DB->client->with_transaction(\&_save, $self, $myconfig, $form);
 
 452   $::lxdebug->leave_sub;
 
 458   $main::lxdebug->enter_sub();
 
 460   my ($self, $myconfig, $form) = @_;
 
 462   my $dbh = SL::DB->client->dbh;
 
 463   my $restricter = SL::HTML::Restrict->create;
 
 465   my ($query, @values, $sth, $null);
 
 466   my $exchangerate = 0;
 
 468   my $all_units = AM->retrieve_units($myconfig, $form);
 
 469   $form->{all_units} = $all_units;
 
 471   my $ic_cvar_configs = CVar->get_configs(module => 'IC',
 
 474   $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id};
 
 475   unless ($form->{employee_id}) {
 
 476     $form->get_employee($dbh);
 
 479   my $ml = ($form->{type} eq 'sales_order') ? 1 : -1;
 
 481   my $number_field         = $form->{type} =~ m{order} ? 'ordnumber' : 'quonumber';
 
 482   my $trans_number         = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{$number_field}, id => $form->{id});
 
 483   $form->{$number_field} ||= $trans_number->create_unique; # set $form->{ordnumber} or $form->{quonumber}
 
 484   my $is_new               = !$form->{id};
 
 487     $query = qq|DELETE FROM custom_variables
 
 488                 WHERE (config_id IN (SELECT id        FROM custom_variable_configs WHERE (module = 'ShipTo')))
 
 489                   AND (trans_id  IN (SELECT shipto_id FROM shipto                  WHERE (module = 'OE') AND (trans_id = ?)))|;
 
 490     do_query($form, $dbh, $query, $form->{id});
 
 492     $query = qq|DELETE FROM shipto | .
 
 493              qq|WHERE trans_id = ? AND module = 'OE'|;
 
 494     do_query($form, $dbh, $query, $form->{id});
 
 498     $query = qq|SELECT nextval('id')|;
 
 499     ($form->{id}) = selectrow_query($form, $dbh, $query);
 
 501     $query = qq|INSERT INTO oe (id, ordnumber, employee_id, currency_id, taxzone_id) VALUES (?, '', ?, (SELECT currency_id FROM defaults), ?)|;
 
 502     do_query($form, $dbh, $query, $form->{id}, $form->{employee_id}, $form->{taxzone_id});
 
 519   my @processed_orderitems;
 
 521   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
 
 522   my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
 
 525   for my $i (1 .. $form->{rowcount}) {
 
 527     map({ $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) } qw(qty ship));
 
 529     if ($form->{"id_$i"}) {
 
 532       $query = qq|SELECT unit FROM parts WHERE id = ?|;
 
 533       my ($item_unit) = selectrow_query($form, $dbh, $query, $form->{"id_$i"});
 
 536       if (defined($all_units->{$item_unit}->{factor}) &&
 
 537           (($all_units->{$item_unit}->{factor} * 1) != 0)) {
 
 538         $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
 
 540       my $baseqty = $form->{"qty_$i"} * $basefactor;
 
 542       $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
 
 543       $form->{"marge_absolut_$i"} = $form->parse_amount($myconfig, $form->{"marge_absolut_$i"}) * 1;
 
 545       $form->{"lastcost_$i"} = $form->parse_amount($myconfig, $form->{"lastcost_$i"});
 
 547       # keep entered selling price
 
 549         $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
 551       my ($dec) = ($fxsellprice =~ /\.(\d+)/);
 
 553       my $decimalplaces = ($dec > 2) ? $dec : 2;
 
 555       # undo discount formatting
 
 556       $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
 
 559       $form->{"sellprice_$i"} = $fxsellprice * (1 - $form->{"discount_$i"});
 
 561       # round linetotal at least to 2 decimal places
 
 562       $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
 
 563       $linetotal    = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
 
 565       $form->{"inventory_accno_$i"} *= 1;
 
 566       $form->{"expense_accno_$i"}   *= 1;
 
 568       @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
 
 572       map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
 
 574       if ($form->{taxincluded}) {
 
 575         $taxamount = $linetotal * $taxrate / (1 + $taxrate);
 
 576         $taxbase   = $linetotal - $taxamount;
 
 578         # we are not keeping a natural price, do not round
 
 579         $form->{"sellprice_$i"} =
 
 580           $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
 
 582         $taxamount = $linetotal * $taxrate;
 
 583         $taxbase   = $linetotal;
 
 586       if ($form->round_amount($taxrate, 7) == 0) {
 
 587         if ($form->{taxincluded}) {
 
 588           foreach my $item (@taxaccounts) {
 
 589             $taxamount = $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
 
 590             $taxaccounts{$item} += $taxamount;
 
 591             $taxdiff            += $taxamount;
 
 592             $taxbase{$item}     += $taxbase;
 
 594           $taxaccounts{ $taxaccounts[0] } += $taxdiff;
 
 596           foreach my $item (@taxaccounts) {
 
 597             $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
 
 598             $taxbase{$item}     += $taxbase;
 
 602         foreach my $item (@taxaccounts) {
 
 603           $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
 
 604           $taxbase{$item} += $taxbase;
 
 608       $netamount += $form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor;
 
 610       $reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef;
 
 612       # Get pricegroup_id and save it. Unfortunately the interface
 
 613       # also uses ID "0" for signalling that none is selected, but "0"
 
 614       # must not be stored in the database. Therefore we cannot simply
 
 616       ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
 
 618       $pricegroup_id  = undef if !$pricegroup_id;
 
 620       # force new project, if not set yet
 
 621       if ($::instance_conf->get_order_always_project && !$form->{"globalproject_id"} && ($form->{type} eq 'sales_order')) {
 
 622         require SL::DB::Customer;
 
 623         my $customer = SL::DB::Manager::Customer->find_by(id => $form->{customer_id});
 
 624         die "Can't find customer" unless $customer;
 
 625         die $main::locale->text("Error while creating project with project number of new order number, project number #1 already exists!", $form->{ordnumber})
 
 626           if SL::DB::Manager::Project->find_by(projectnumber => $form->{ordnumber});
 
 628         my $new_project = SL::DB::Project->new(
 
 629           projectnumber     => $form->{ordnumber},
 
 630           description       => $customer->name,
 
 631           customer_id       => $customer->id,
 
 633           project_type_id   => $::instance_conf->get_project_type_id,
 
 634           project_status_id => $::instance_conf->get_project_status_id,
 
 637         $form->{"globalproject_id"} = $new_project->id;
 
 640       CVar->get_non_editable_ic_cvars(form               => $form,
 
 643                                       sub_module         => 'orderitems',
 
 644                                       may_converted_from => ['orderitems', 'invoice']);
 
 648       # save detail record in orderitems table
 
 649       if (! $form->{"orderitems_id_$i"}) {
 
 650         $query = qq|SELECT nextval('orderitemsid')|;
 
 651         ($form->{"orderitems_id_$i"}) = selectrow_query($form, $dbh, $query);
 
 653         $query = qq|INSERT INTO orderitems (id, position) VALUES (?, ?)|;
 
 654         do_query($form, $dbh, $query, $form->{"orderitems_id_$i"}, conv_i($position));
 
 657       my $orderitems_id = $form->{"orderitems_id_$i"};
 
 658       push @processed_orderitems, $orderitems_id;
 
 661          UPDATE orderitems SET
 
 662           trans_id = ?, position = ?, parts_id = ?, description = ?, longdescription = ?, qty = ?, base_qty = ?,
 
 663           sellprice = ?, discount = ?, unit = ?, reqdate = ?, project_id = ?, serialnumber = ?, ship = ?,
 
 664           pricegroup_id = ?, subtotal = ?,
 
 665           marge_percent = ?, marge_total = ?, lastcost = ?, price_factor_id = ?,
 
 666           active_price_source = ?, active_discount_source = ?,
 
 667           price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?
 
 671            conv_i($form->{id}), conv_i($position), conv_i($form->{"id_$i"}),
 
 672            $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}),
 
 673            $form->{"qty_$i"}, $baseqty,
 
 674            $fxsellprice, $form->{"discount_$i"},
 
 675            $form->{"unit_$i"}, conv_date($reqdate), conv_i($form->{"project_id_$i"}),
 
 676            $form->{"serialnumber_$i"}, $form->{"ship_$i"},
 
 677            $pricegroup_id, $form->{"subtotal_$i"} ? 't' : 'f',
 
 678            $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
 
 679            $form->{"lastcost_$i"}, conv_i($form->{"price_factor_id_$i"}),
 
 680            $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"},
 
 681            conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}),
 
 682            conv_i($orderitems_id),
 
 685       do_query($form, $dbh, $query, @values);
 
 687       $form->{"sellprice_$i"} = $fxsellprice;
 
 688       $form->{"discount_$i"} *= 100;
 
 690       CVar->save_custom_variables(module       => 'IC',
 
 691                                   sub_module   => 'orderitems',
 
 692                                   trans_id     => $orderitems_id,
 
 693                                   configs      => $ic_cvar_configs,
 
 695                                   name_prefix  => 'ic_',
 
 696                                   name_postfix => "_$i",
 
 699       # link previous items with orderitems
 
 700       # assume we have a new workflow if we link from invoice or order to quotation
 
 701       # unluckily orderitems are used for quotation and orders - therefore one more
 
 702       # check to be sure NOT to link from order to quotation
 
 703       foreach (qw(orderitems)) {
 
 704         if (!$form->{saveasnew} && !$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}
 
 705               && $form->{type} !~ 'quotation') {
 
 706           RecordLinks->create_links('dbh'        => $dbh,
 
 709                                     'from_ids'   => $form->{"converted_from_${_}_id_$i"},
 
 710                                     'to_table'   => 'orderitems',
 
 711                                     'to_id'      => $orderitems_id,
 
 714         delete $form->{"converted_from_${_}_id_$i"};
 
 719   # search for orphaned ids
 
 720   $query  = sprintf 'SELECT id FROM orderitems WHERE trans_id = ? AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_orderitems;
 
 721   @values = (conv_i($form->{id}), map { conv_i($_) } @processed_orderitems);
 
 722   my @orphaned_ids = map { $_->{id} } selectall_hashref_query($form, $dbh, $query, @values);
 
 724   if (scalar @orphaned_ids) {
 
 725     # clean up orderitems
 
 726     $query  = sprintf 'DELETE FROM orderitems WHERE id IN (%s)', join ', ', ("?") x scalar @orphaned_ids;
 
 727     do_query($form, $dbh, $query, @orphaned_ids);
 
 730   $reqdate = ($form->{reqdate}) ? $form->{reqdate} : undef;
 
 734   map { $tax += $form->round_amount($taxaccounts{$_}, 2) } keys %taxaccounts;
 
 736   $amount = $form->round_amount($netamount + $tax, 2, 1);
 
 737   $netamount = $form->round_amount($netamount, 2);
 
 739   if ($form->{currency} eq $form->{defaultcurrency}) {
 
 740     $form->{exchangerate} = 1;
 
 742     $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? 'buy' : 'sell');
 
 745   # from inputfield (exchangerate) or hidden (forex)
 
 746   my $exchangerate_from_form = $form->{forex} || $form->parse_amount($myconfig, $form->{exchangerate});
 
 748   $form->{exchangerate} = $exchangerate || $exchangerate_from_form;
 
 750   my $quotation = $form->{type} =~ /_order$/ ? 'f' : 't';
 
 755          ordnumber = ?, quonumber = ?, cusordnumber = ?, transdate = ?, vendor_id = ?,
 
 756          customer_id = ?, amount = ?, netamount = ?, reqdate = ?, tax_point = ?, taxincluded = ?,
 
 757          shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, currency_id = (SELECT id FROM currencies WHERE name=?), closed = ?,
 
 758          delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
 
 759          taxzone_id = ?, shipto_id = ?, billing_address_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,delivery_term_id = ?,
 
 760          globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
 
 761          , order_probability = ?, expected_billing_date = ?
 
 764   @values = ($form->{ordnumber} || '', $form->{quonumber},
 
 765              $form->{cusordnumber}, conv_date($form->{transdate}),
 
 766              conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
 
 767              $amount, $netamount, conv_date($reqdate), conv_date($form->{tax_point}),
 
 768              $form->{taxincluded} ? 't' : 'f', $form->{shippingpoint},
 
 769              $form->{shipvia}, $restricter->process($form->{notes}), $form->{intnotes},
 
 770              $form->{currency}, $form->{closed} ? 't' : 'f',
 
 771              $form->{delivered} ? "t" : "f", $form->{proforma} ? 't' : 'f',
 
 772              $quotation, conv_i($form->{department_id}),
 
 773              conv_i($form->{language_id}), conv_i($form->{taxzone_id}),
 
 774              conv_i($form->{shipto_id}), conv_i($form->{billing_address_id}), conv_i($form->{payment_id}),
 
 775              conv_i($form->{delivery_vendor_id}),
 
 776              conv_i($form->{delivery_customer_id}),
 
 777              conv_i($form->{delivery_term_id}),
 
 778              conv_i($form->{globalproject_id}), conv_i($form->{employee_id}),
 
 779              conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
 
 780              $form->{transaction_description},
 
 781              $form->{marge_total} * 1, $form->{marge_percent} * 1,
 
 782              $form->{order_probability} * 1, conv_date($form->{expected_billing_date}),
 
 783              conv_i($form->{id}));
 
 784   do_query($form, $dbh, $query, @values);
 
 786   $form->new_lastmtime('oe');
 
 788   $form->{ordtotal} = $amount;
 
 790   $form->{name} = $form->{ $form->{vc} };
 
 791   $form->{name} =~ s/--\Q$form->{"$form->{vc}_id"}\E//;
 
 794   if (!$form->{shipto_id}) {
 
 795     $form->add_shipto($dbh, $form->{id}, "OE");
 
 798   # save printed, emailed, queued
 
 799   $form->save_status($dbh);
 
 801   # Link this record to the records it was created from.
 
 802   $form->{convert_from_oe_ids} =~ s/^\s+//;
 
 803   $form->{convert_from_oe_ids} =~ s/\s+$//;
 
 804   my @convert_from_oe_ids      =  split m/\s+/, $form->{convert_from_oe_ids};
 
 805   delete $form->{convert_from_oe_ids};
 
 806   if (!$form->{useasnew} && scalar @convert_from_oe_ids) {
 
 807     RecordLinks->create_links('dbh'        => $dbh,
 
 809                               'from_table' => 'oe',
 
 810                               'from_ids'   => \@convert_from_oe_ids,
 
 812                               'to_id'      => $form->{id},
 
 814     $self->_close_quotations_rfqs('dbh'     => $dbh,
 
 815                                   'from_id' => \@convert_from_oe_ids,
 
 816                                   'to_id'   => $form->{id});
 
 819   if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
 
 820     if ($form->{vc} eq 'customer') {
 
 821       $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
 
 823     if ($form->{vc} eq 'vendor') {
 
 824       $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
 
 828   $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ?
 
 829                                        "quonumber" : "ordnumber"};
 
 831   Common::webdav_folder($form);
 
 833   $self->save_periodic_invoices_config(dbh         => $dbh,
 
 834                                        oe_id       => $form->{id},
 
 835                                        config_yaml => $form->{periodic_invoices_config})
 
 836     if ($form->{type} eq 'sales_order');
 
 838   $self->_link_created_sales_order_to_requirement_specs_for_sales_quotations(
 
 839     type               => $form->{type},
 
 840     converted_from_ids => \@convert_from_oe_ids,
 
 841     sales_order_id     => $form->{id},
 
 845   $self->_set_project_in_linked_requirement_spec(
 
 846     type           => $form->{type},
 
 847     project_id     => $form->{globalproject_id},
 
 848     sales_order_id => $form->{id},
 
 851   $main::lxdebug->leave_sub();
 
 856 sub _link_created_sales_order_to_requirement_specs_for_sales_quotations {
 
 857   my ($self, %params) = @_;
 
 859   # If this is a sales order created from a sales quotation and if
 
 860   # that sales quotation was created from a requirement spec document
 
 861   # then link the newly created sales order to the requirement spec
 
 864   return if !$params{is_new};
 
 865   return if  $params{type} ne 'sales_order';
 
 866   return if !@{ $params{converted_from_ids} };
 
 868   my $oe_objects       = SL::DB::Manager::Order->get_all(where => [ id => $params{converted_from_ids} ]);
 
 869   my @sales_quotations = grep { $_->is_type('sales_quotation') } @{ $oe_objects };
 
 871   return if !@sales_quotations;
 
 873   my $rs_orders = SL::DB::Manager::RequirementSpecOrder->get_all(where => [ order_id => [ map { $_->id } @sales_quotations ] ]);
 
 875   return if !@{ $rs_orders };
 
 877   $rs_orders->[0]->db->with_transaction(sub {
 
 878     foreach my $rs_order (@{ $rs_orders }) {
 
 879       SL::DB::RequirementSpecOrder->new(
 
 880         order_id            => $params{sales_order_id},
 
 881         requirement_spec_id => $rs_order->requirement_spec_id,
 
 882         version_id          => $rs_order->version_id,
 
 890 sub _set_project_in_linked_requirement_spec {
 
 891   my ($self, %params) = @_;
 
 893   return if  $params{type} ne 'sales_order';
 
 894   return if !$params{project_id} || !$params{sales_order_id};
 
 897     UPDATE requirement_specs
 
 900       SELECT so.requirement_spec_id
 
 901       FROM requirement_spec_orders so
 
 902       WHERE so.order_id = ?
 
 906   do_query($::form, $::form->get_standard_dbh, $query, $params{project_id}, $params{sales_order_id});
 
 909 sub save_periodic_invoices_config {
 
 910   my ($self, %params) = @_;
 
 912   return if !$params{oe_id};
 
 914   my $config = $params{config_yaml} ? SL::YAML::Load($params{config_yaml}) : undef;
 
 915   return if 'HASH' ne ref $config;
 
 917   my $obj  = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $params{oe_id})
 
 918           || SL::DB::PeriodicInvoicesConfig->new(oe_id => $params{oe_id});
 
 919   $obj->update_attributes(%{ $config });
 
 922 sub load_periodic_invoice_config {
 
 926   delete $form->{periodic_invoices_config};
 
 929     my $config_obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $form->{id});
 
 932       my $config = { map { $_ => $config_obj->$_ } qw(active terminated periodicity order_value_periodicity start_date_as_date end_date_as_date first_billing_date_as_date extend_automatically_by ar_chart_id
 
 933                                                       print printer_id copies direct_debit send_email email_recipient_contact_id email_recipient_address email_sender email_subject email_body) };
 
 934       $form->{periodic_invoices_config} = SL::YAML::Dump($config);
 
 939 sub _close_quotations_rfqs {
 
 940   $main::lxdebug->enter_sub();
 
 945   Common::check_params(\%params, qw(from_id to_id));
 
 947   my $myconfig = \%main::myconfig;
 
 948   my $form     = $main::form;
 
 950   my $dbh      = $params{dbh} || SL::DB->client->dbh;
 
 952   SL::DB->client->with_transaction(sub {
 
 954     my $query    = qq|SELECT quotation FROM oe WHERE id = ?|;
 
 955     my $sth      = prepare_query($form, $dbh, $query);
 
 957     do_statement($form, $sth, $query, conv_i($params{to_id}));
 
 959     my ($quotation) = $sth->fetchrow_array();
 
 967     foreach my $from_id (@{ $params{from_id} }) {
 
 968       $from_id = conv_i($from_id);
 
 969       do_statement($form, $sth, $query, $from_id);
 
 970       ($quotation) = $sth->fetchrow_array();
 
 971       push @close_ids, $from_id if ($quotation);
 
 976     if (scalar @close_ids) {
 
 977       $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_ids) . qq|)|;
 
 978       do_query($form, $dbh, $query, @close_ids);
 
 981   }) or do { die SL::DB->client->error };
 
 983   $main::lxdebug->leave_sub();
 
 987   $main::lxdebug->enter_sub();
 
 989   my ($self, $myconfig, $form) = @_;
 
 991   my $rc = SL::DB::Order->new->db->with_transaction(sub {
 
 992     my @spoolfiles = grep { $_ } map { $_->spoolfile } @{ SL::DB::Manager::Status->get_all(where => [ trans_id => $form->{id} ]) };
 
 994     SL::DB::Order->new(id => $form->{id})->delete;
 
 996     my $spool = $::lx_office_conf{paths}->{spool};
 
 997     unlink map { "$spool/$_" } @spoolfiles if $spool;
 
1000   }) or do { die SL::DB->client->error };
 
1002   $main::lxdebug->leave_sub();
 
1008   my ($self, $myconfig, $form) = @_;
 
1009   $main::lxdebug->enter_sub();
 
1011   my $rc = SL::DB->client->with_transaction(\&_retrieve, $self, $myconfig, $form);
 
1013   $::lxdebug->leave_sub;
 
1018   my ($self, $myconfig, $form) = @_;
 
1020   # connect to database
 
1021   my $dbh = SL::DB->client->dbh;
 
1023   my ($query, $query_add, @values, @ids, $sth);
 
1025   # translate the ids (given by id_# and trans_id_#) into one array of ids, so we can join them later
 
1027     push @ids, $form->{"trans_id_$_"}
 
1028       if ($form->{"multi_id_$_"} and $form->{"trans_id_$_"})
 
1029   } (1 .. $form->{"rowcount"});
 
1031   if ($form->{rowcount} && scalar @ids) {
 
1032     $form->{convert_from_oe_ids} = join ' ', @ids;
 
1035   # if called in multi id mode, and still only got one id, switch back to single id
 
1036   if ($form->{"rowcount"} and $#ids == 0) {
 
1037     $form->{"id"} = $ids[0];
 
1039     delete $form->{convert_from_oe_ids};
 
1042   # and remember for the rest of the function
 
1043   my $is_collective_order = scalar @ids;
 
1045   # If collective order was created from exactly 1 order, we assume the same
 
1046   # behaviour as a "save as new" from within an order is actually desired, i.e.
 
1047   # the original order isn't part of a workflow where we want to remember
 
1048   # record_links, but simply a quick way of generating a new order from an old
 
1049   # one without having to enter everything again.
 
1050   # Setting useasnew will prevent the creation of record_links for the items
 
1051   # when saving the new order.
 
1052   # This form variable is probably not necessary, could just set saveasnew instead
 
1053   $form->{useasnew} = 1 if $is_collective_order == 1;
 
1056     my $extra_days = $form->{type} eq 'sales_quotation' ? $::instance_conf->get_reqdate_interval       :
 
1057                      $form->{type} eq 'sales_order'     ? $::instance_conf->get_delivery_date_interval : 1;
 
1058     if (   ($form->{type} eq 'sales_order'     &&  !$::instance_conf->get_deliverydate_on)
 
1059         || ($form->{type} eq 'sales_quotation' &&  !$::instance_conf->get_reqdate_on)) {
 
1060       $form->{reqdate}   = '';
 
1062       $form->{reqdate}   = DateTime->today_local->next_workday(extra_days => $extra_days)->to_kivitendo;
 
1064     $form->{transdate} = DateTime->today_local->to_kivitendo;
 
1067   # get default accounts
 
1068   $query = qq|SELECT (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
 
1069                      (SELECT c.accno FROM chart c WHERE d.income_accno_id    = c.id) AS income_accno,
 
1070                      (SELECT c.accno FROM chart c WHERE d.expense_accno_id   = c.id) AS expense_accno,
 
1071                      (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id    = c.id) AS fxgain_accno,
 
1072                      (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id    = c.id) AS fxloss_accno,
 
1073                      (SELECT c.accno FROM chart c WHERE d.rndgain_accno_id   = c.id) AS rndgain_accno,
 
1074                      (SELECT c.accno FROM chart c WHERE d.rndloss_accno_id   = c.id) AS rndloss_accno
 
1077   my $ref = selectfirst_hashref_query($form, $dbh, $query);
 
1078   map { $form->{$_} = $ref->{$_} } keys %$ref;
 
1080   $form->{currency} = $form->get_default_currency($myconfig);
 
1082   # set reqdate if this is an invoice->order conversion. If someone knows a better check to ensure
 
1083   # we come from invoices, feel free.
 
1084   $form->{reqdate} = $form->{deliverydate}
 
1085     if (    $form->{deliverydate}
 
1086         and $form->{callback} =~ /action=ar_transactions/);
 
1088   my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
 
1090   if ($form->{id} or @ids) {
 
1092     # retrieve order for single id
 
1093     # NOTE: this query is intended to fetch all information only ONCE.
 
1094     # so if any of these infos is important (or even different) for any item,
 
1095     # it will be killed out and then has to be fetched from the item scope query further down
 
1097       qq|SELECT o.cp_id, o.ordnumber, o.transdate, o.reqdate,
 
1098            o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
 
1099            (SELECT cu.name FROM currencies cu WHERE cu.id=o.currency_id) AS currency, e.name AS employee, o.employee_id, o.salesman_id,
 
1100            o.${vc}_id, cv.name AS ${vc}, o.amount AS invtotal,
 
1101            o.closed, o.reqdate, o.tax_point, o.quonumber, o.department_id, o.cusordnumber,
 
1103            d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
 
1104            o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id, o.billing_address_id,
 
1105            o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id,
 
1106            o.itime::DATE AS insertdate, o.order_probability, o.expected_billing_date
 
1108          JOIN ${vc} cv ON (o.${vc}_id = cv.id)
 
1109          LEFT JOIN employee e ON (o.employee_id = e.id)
 
1110          LEFT JOIN department d ON (o.department_id = d.id) | .
 
1113          : "WHERE o.id IN (" . join(', ', map("? ", @ids)) . ")"
 
1115     @values = $form->{id} ? ($form->{id}) : @ids;
 
1116     $sth = prepare_execute_query($form, $dbh, $query, @values);
 
1118     $ref = $sth->fetchrow_hashref("NAME_lc");
 
1121       map { $form->{$_} = $ref->{$_} } keys %$ref;
 
1123       $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ? "quonumber" : "ordnumber"};
 
1125       # set all entries for multiple ids blank that yield different information
 
1126       while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
1127         map { $form->{$_} = '' if ($ref->{$_} ne $form->{$_}) } keys %$ref;
 
1130     $form->{mtime}   ||= $form->{itime};
 
1131     $form->{lastmtime} = $form->{mtime};
 
1133     # if not given, fill transdate with current_date
 
1134     $form->{transdate} = $form->current_date($myconfig)
 
1135       unless $form->{transdate};
 
1139     if ($form->{delivery_customer_id}) {
 
1140       $query = qq|SELECT name FROM customer WHERE id = ?|;
 
1141       ($form->{delivery_customer_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_customer_id});
 
1144     if ($form->{delivery_vendor_id}) {
 
1145       $query = qq|SELECT name FROM customer WHERE id = ?|;
 
1146       ($form->{delivery_vendor_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_vendor_id});
 
1149     # shipto and pinted/mailed/queued status makes only sense for single id retrieve
 
1151       $query = qq|SELECT s.* FROM shipto s WHERE s.trans_id = ? AND s.module = 'OE'|;
 
1152       $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
 
1154       $ref = $sth->fetchrow_hashref("NAME_lc");
 
1155       $form->{$_} = $ref->{$_} for grep { m{^shipto(?!_id$)} } keys %$ref;
 
1158       if ($ref->{shipto_id}) {
 
1159         my $cvars = CVar->get_custom_variables(
 
1162           trans_id => $ref->{shipto_id},
 
1164         $form->{"shiptocvar_$_->{name}"} = $_->{value} for @{ $cvars };
 
1167       # get printed, emailed and queued
 
1168       $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
 
1169       $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
 
1171       while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
1172         $form->{printed} .= "$ref->{formname} " if $ref->{printed};
 
1173         $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
 
1174         $form->{queued}  .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
 
1177       map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
 
1180     my $transdate = $form->{tax_point} ? $dbh->quote($form->{tax_point}) : $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
 
1182     $form->{taxzone_id} = 0 unless ($form->{taxzone_id});
 
1183     unshift @values, ($form->{taxzone_id}) x 2;
 
1185     # retrieve individual items
 
1186     # this query looks up all information about the items
 
1187     # stuff different from the whole will not be overwritten, but saved with a suffix.
 
1189       qq|SELECT o.id AS orderitems_id,
 
1190            c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from as inventory_valid,
 
1191            c2.accno AS income_accno,    c2.new_chart_id AS income_new_chart,    date($transdate) - c2.valid_from as income_valid,
 
1192            c3.accno AS expense_accno,   c3.new_chart_id AS expense_new_chart,   date($transdate) - c3.valid_from as expense_valid,
 
1193            oe.ordnumber AS ordnumber_oe, oe.transdate AS transdate_oe, oe.cusordnumber AS cusordnumber_oe,
 
1194            p.partnumber, p.part_type, p.listprice, o.description, o.qty,
 
1195            p.classification_id,
 
1196            o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.part_type,
 
1197            o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
 
1198            o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
 
1199            o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source, o.active_discount_source,
 
1200            pr.projectnumber, p.formel,
 
1201            pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
 
1203          JOIN parts p ON (o.parts_id = p.id)
 
1204          JOIN oe ON (o.trans_id = oe.id)
 
1205          LEFT JOIN chart c1 ON ((SELECT inventory_accno_id                   FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c1.id)
 
1206          LEFT JOIN chart c2 ON ((SELECT tc.income_accno_id  FROM taxzone_charts tc WHERE tc.taxzone_id = ? and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c2.id)
 
1207          LEFT JOIN chart c3 ON ((SELECT tc.expense_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = ? and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c3.id)
 
1208          LEFT JOIN project pr ON (o.project_id = pr.id)
 
1209          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
 
1211        ? qq|WHERE o.trans_id = ?|
 
1212        : qq|WHERE o.trans_id IN (| . join(", ", map("?", @ids)) . qq|)|) .
 
1213       qq|ORDER BY o.trans_id, o.position|;
 
1215     @ids = $form->{id} ? ($form->{id}) : @ids;
 
1216     $sth = prepare_execute_query($form, $dbh, $query, @values);
 
1218     while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
 
1219       # Retrieve custom variables.
 
1220       my $cvars = CVar->get_custom_variables(dbh        => $dbh,
 
1222                                              sub_module => 'orderitems',
 
1223                                              trans_id   => $ref->{orderitems_id},
 
1225       map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
 
1228       if (!$ref->{"part_type"} eq 'part') {
 
1229         map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid));
 
1231       # delete($ref->{"part_inventory_accno_id"});
 
1233       # in collective order, copy global ordnumber, transdate, cusordnumber into item scope
 
1234       #   unless already present there
 
1235       # remove _oe entries afterwards
 
1236       map { $ref->{$_} = $ref->{"${_}_oe"} if ($ref->{$_} eq '') }
 
1237         qw|ordnumber transdate cusordnumber|
 
1239       map { delete $ref->{$_} } qw|ordnumber_oe transdate_oe cusordnumber_oe|;
 
1243       while ($ref->{inventory_new_chart} && ($ref->{inventory_valid} >= 0)) {
 
1245           qq|SELECT accno AS inventory_accno, | .
 
1246           qq|  new_chart_id AS inventory_new_chart, | .
 
1247           qq|  date($transdate) - valid_from AS inventory_valid | .
 
1248           qq|FROM chart WHERE id = $ref->{inventory_new_chart}|;
 
1249         ($ref->{inventory_accno}, $ref->{inventory_new_chart},
 
1250          $ref->{inventory_valid}) = selectrow_query($form, $dbh, $query);
 
1253       while ($ref->{income_new_chart} && ($ref->{income_valid} >= 0)) {
 
1255           qq|SELECT accno AS income_accno, | .
 
1256           qq|  new_chart_id AS income_new_chart, | .
 
1257           qq|  date($transdate) - valid_from AS income_valid | .
 
1258           qq|FROM chart WHERE id = $ref->{income_new_chart}|;
 
1259         ($ref->{income_accno}, $ref->{income_new_chart},
 
1260          $ref->{income_valid}) = selectrow_query($form, $dbh, $query);
 
1263       while ($ref->{expense_new_chart} && ($ref->{expense_valid} >= 0)) {
 
1265           qq|SELECT accno AS expense_accno, | .
 
1266           qq|  new_chart_id AS expense_new_chart, | .
 
1267           qq|  date($transdate) - valid_from AS expense_valid | .
 
1268           qq|FROM chart WHERE id = $ref->{expense_new_chart}|;
 
1269         ($ref->{expense_accno}, $ref->{expense_new_chart},
 
1270          $ref->{expense_valid}) = selectrow_query($form, $dbh, $query);
 
1273       # delete orderitems_id in collective orders, so that they get cloned no matter what
 
1274       # is this correct? or is the following meant?
 
1275       # remember orderitems_ids in converted_from_orderitems_ids, so that they may be linked
 
1276       $ref->{converted_from_orderitems_id} = delete $ref->{orderitems_id} if $is_collective_order;
 
1278       # get tax rates and description
 
1279       my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
 
1281         qq|SELECT c.accno, t.taxdescription, t.rate, t.id as tax_id, c.accno as taxnumber | .
 
1283         qq|LEFT JOIN chart c on (c.id = t.chart_id) | .
 
1284         qq|WHERE t.id IN (SELECT tk.tax_id FROM taxkeys tk | .
 
1285         qq|               WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?) | .
 
1286         qq|                 AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1) | .
 
1287         qq|ORDER BY c.accno|;
 
1288       my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
 
1289       $ref->{taxaccounts} = "";
 
1291       while (my $ptr = $stw->fetchrow_hashref("NAME_lc")) {
 
1292         if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
 
1296         $ref->{taxaccounts} .= "$ptr->{accno} ";
 
1297         if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
 
1298           $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
 
1299           $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
 
1300           $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
 
1301           $form->{"$ptr->{accno}_tax_id"}      = $ptr->{tax_id};
 
1302           $form->{taxaccounts} .= "$ptr->{accno} ";
 
1307       chop $ref->{taxaccounts};
 
1309       push @{ $form->{form_details} }, $ref;
 
1316     # get last name used
 
1317     $form->lastname_used($dbh, $myconfig, $form->{vc})
 
1318       unless $form->{"$form->{vc}_id"};
 
1322   $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? "buy" : "sell");
 
1324   Common::webdav_folder($form);
 
1326   $self->load_periodic_invoice_config($form);
 
1331 sub retrieve_simple {
 
1332   $main::lxdebug->enter_sub();
 
1337   Common::check_params(\%params, qw(id));
 
1339   my $myconfig    = \%main::myconfig;
 
1340   my $form        = $main::form;
 
1342   my $dbh         = $params{dbh} || $form->get_standard_dbh($myconfig);
 
1344   my $oe_query    = qq|SELECT * FROM oe         WHERE id = ?|;
 
1345   my $oi_query    = qq|SELECT * FROM orderitems WHERE trans_id = ? ORDER BY position|;
 
1347   my $order            = selectfirst_hashref_query($form, $dbh, $oe_query, conv_i($params{id}));
 
1348   $order->{orderitems} = selectall_hashref_query(  $form, $dbh, $oi_query, conv_i($params{id}));
 
1350   $main::lxdebug->leave_sub();
 
1356   $main::lxdebug->enter_sub();
 
1358   my ($self, $myconfig, $form) = @_;
 
1360   # connect to database
 
1361   my $dbh = SL::DB->client->dbh;
 
1367   my $nodiscount_subtotal = 0;
 
1368   my $discount_subtotal = 0;
 
1371   my @partsgroup = ();
 
1374   my $subtotal_header = 0;
 
1375   my $subposition = 0;
 
1383   push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
 
1385   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
 
1388   foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
 
1389     $price_factors{$pfac->{id}}  = $pfac;
 
1390     $pfac->{factor}             *= 1;
 
1391     $pfac->{formatted_factor}    = $form->format_amount($myconfig, $pfac->{factor});
 
1394   # sort items by partsgroup
 
1395   for $i (1 .. $form->{rowcount}) {
 
1397     if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
 
1398       $partsgroup = $form->{"partsgroup_$i"};
 
1400     push @partsgroup, [$i, $partsgroup];
 
1401     push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
 
1407     $projects = SL::DB::Manager::Project->get_all(query => [ id => \@project_ids ]);
 
1408     %projects_by_id = map { $_->id => $_ } @$projects;
 
1411   if ($projects_by_id{$form->{"globalproject_id"}}) {
 
1412     $form->{globalprojectnumber} = $projects_by_id{$form->{"globalproject_id"}}->projectnumber;
 
1413     $form->{globalprojectdescription} = $projects_by_id{$form->{"globalproject_id"}}->description;
 
1415     for (@{ $projects_by_id{$form->{"globalproject_id"}}->cvars_by_config }) {
 
1416       $form->{"project_cvar_" . $_->config->name} = $_->value_as_text;
 
1420   $form->{discount} = [];
 
1422   # get some values of parts from db on store them in extra array,
 
1423   # so that they can be sorted in later
 
1424   my %prepared_template_arrays = IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
 
1425   my @prepared_arrays          = keys %prepared_template_arrays;
 
1426   my @separate_totals          = qw(non_separate_subtotal);
 
1428   $form->{TEMPLATE_ARRAYS} = { };
 
1430   my $ic_cvar_configs = CVar->get_configs(module => 'IC');
 
1431   my $project_cvar_configs = CVar->get_configs(module => 'Projects');
 
1434     qw(runningnumber number description longdescription qty qty_nofmt ship ship_nofmt unit bin
 
1435        partnotes serialnumber reqdate sellprice sellprice_nofmt listprice listprice_nofmt netprice netprice_nofmt
 
1436        discount discount_nofmt p_discount discount_sub discount_sub_nofmt nodiscount_sub nodiscount_sub_nofmt
 
1437        linetotal linetotal_nofmt nodiscount_linetotal nodiscount_linetotal_nofmt tax_rate projectnumber projectdescription
 
1438        price_factor price_factor_name partsgroup weight weight_nofmt lineweight lineweight_nofmt optional);
 
1440   push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
 
1441   push @arrays, map { "project_cvar_$_->{name}" } @{ $project_cvar_configs };
 
1443   my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
 
1445   map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @prepared_arrays);
 
1447   my $totalweight = 0;
 
1449   foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
 
1452     if ($item->[1] ne $sameitem) {
 
1453       push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type}  }, 'partsgroup');
 
1454       push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
 
1455       $sameitem = $item->[1];
 
1457       map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
 
1460     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
 
1462     if ($form->{"id_$i"} != 0) {
 
1464       # add number, description and qty to $form->{number}, ....
 
1466       if ($form->{"subtotal_$i"} && !$subtotal_header) {
 
1467         $subtotal_header = $i;
 
1468         $position = int($position);
 
1471       } elsif ($subtotal_header) {
 
1473         $position = int($position);
 
1474         $position = $position.".".$subposition;
 
1476         $position = int($position);
 
1480       my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
 
1482       push(@{ $form->{TEMPLATE_ARRAYS}->{$_} },                $prepared_template_arrays{$_}[$i - 1]) for @prepared_arrays;
 
1484       push @{ $form->{TEMPLATE_ARRAYS}->{entry_type} },        'normal';
 
1485       push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} },     $position;
 
1486       push @{ $form->{TEMPLATE_ARRAYS}->{number} },            $form->{"partnumber_$i"};
 
1487       push @{ $form->{TEMPLATE_ARRAYS}->{description} },       $form->{"description_$i"};
 
1488       push @{ $form->{TEMPLATE_ARRAYS}->{longdescription} },   $form->{"longdescription_$i"};
 
1489       push @{ $form->{TEMPLATE_ARRAYS}->{qty} },               $form->format_amount($myconfig, $form->{"qty_$i"});
 
1490       push @{ $form->{TEMPLATE_ARRAYS}->{qty_nofmt} },         $form->{"qty_$i"};
 
1491       push @{ $form->{TEMPLATE_ARRAYS}->{ship} },              $form->format_amount($myconfig, $form->{"ship_$i"});
 
1492       push @{ $form->{TEMPLATE_ARRAYS}->{ship_nofmt} },        $form->{"ship_$i"};
 
1493       push @{ $form->{TEMPLATE_ARRAYS}->{unit} },              $form->{"unit_$i"};
 
1494       push @{ $form->{TEMPLATE_ARRAYS}->{bin} },               $form->{"bin_$i"};
 
1495       push @{ $form->{TEMPLATE_ARRAYS}->{partnotes} },         $form->{"partnotes_$i"};
 
1496       push @{ $form->{TEMPLATE_ARRAYS}->{serialnumber} },      $form->{"serialnumber_$i"};
 
1497       push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} },           $form->{"reqdate_$i"};
 
1498       push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} },         $form->{"sellprice_$i"};
 
1499       push @{ $form->{TEMPLATE_ARRAYS}->{sellprice_nofmt} },   $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
1500       push @{ $form->{TEMPLATE_ARRAYS}->{listprice} },         $form->format_amount($myconfig, $form->{"listprice_$i"}, 2);
 
1501       push @{ $form->{TEMPLATE_ARRAYS}->{listprice_nofmt} },   $form->{"listprice_$i"};
 
1502       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} },      $price_factor->{formatted_factor};
 
1503       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
 
1504       push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} },        $form->{"partsgroup_$i"};
 
1505       push @{ $form->{TEMPLATE_ARRAYS}->{optional} },          $form->{"optional_$i"};
 
1507       my $sellprice     = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
 
1508       my ($dec)         = ($sellprice =~ /\.(\d+)/);
 
1509       my $decimalplaces = max 2, length($dec);
 
1511       my $parsed_discount            = $form->parse_amount($myconfig, $form->{"discount_$i"});
 
1513       my $linetotal_exact            = $form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor};
 
1514       my $linetotal                  = $form->round_amount($linetotal_exact, 2);
 
1516       my $nodiscount_exact_linetotal = $form->{"qty_$i"} * $sellprice                                  / $price_factor->{factor};
 
1517       my $nodiscount_linetotal       = $form->round_amount($nodiscount_exact_linetotal,2);
 
1519       my $discount                   = $nodiscount_linetotal - $linetotal; # is always rounded because $nodiscount_linetotal and $linetotal are rounded
 
1521       my $discount_round_error       = $discount + ($linetotal_exact - $nodiscount_exact_linetotal); # not used
 
1523       $form->{"netprice_$i"}   = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, $decimalplaces);
 
1525       push @{ $form->{TEMPLATE_ARRAYS}->{netprice} },       ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
 
1526       push @{ $form->{TEMPLATE_ARRAYS}->{netprice_nofmt} }, ($form->{"netprice_$i"} != 0) ? $form->{"netprice_$i"} : '';
 
1528       $linetotal = ($linetotal != 0) ? $linetotal : '';
 
1530       push @{ $form->{TEMPLATE_ARRAYS}->{discount} },       ($discount != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
 
1531       push @{ $form->{TEMPLATE_ARRAYS}->{discount_nofmt} }, ($discount != 0) ? $discount * -1 : '';
 
1532       push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} },     $form->{"discount_$i"};
 
1534       if ( $prepared_template_arrays{separate}[$i - 1]  ) {
 
1535         my $pabbr = $prepared_template_arrays{separate}[$i - 1];
 
1536         if ( ! $form->{"separate_${pabbr}_subtotal"} ) {
 
1537             push @separate_totals , "separate_${pabbr}_subtotal";
 
1538             $form->{"separate_${pabbr}_subtotal"} = 0;
 
1540         $form->{"separate_${pabbr}_subtotal"} += $linetotal;
 
1542         $form->{non_separate_subtotal} += $linetotal;
 
1545       $form->{ordtotal}         += $linetotal unless $form->{"optional_$i"};
 
1546       $form->{nodiscount_total} += $nodiscount_linetotal;
 
1547       $form->{discount_total}   += $discount;
 
1549       if ($subtotal_header) {
 
1550         $discount_subtotal   += $linetotal;
 
1551         $nodiscount_subtotal += $nodiscount_linetotal;
 
1554       if ($form->{"subtotal_$i"} && $subtotal_header && ($subtotal_header != $i)) {
 
1555         push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} },         $form->format_amount($myconfig, $discount_subtotal,   2);
 
1556         push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub_nofmt} },   $discount_subtotal;
 
1557         push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} },       $form->format_amount($myconfig, $nodiscount_subtotal, 2);
 
1558         push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub_nofmt} }, $nodiscount_subtotal;
 
1560         $discount_subtotal   = 0;
 
1561         $nodiscount_subtotal = 0;
 
1562         $subtotal_header     = 0;
 
1565         push @{ $form->{TEMPLATE_ARRAYS}->{$_} }, "" for qw(discount_sub nodiscount_sub discount_sub_nofmt nodiscount_sub_nofmt);
 
1568       if (!$form->{"discount_$i"}) {
 
1569         $nodiscount += $linetotal;
 
1572       my $project = $projects_by_id{$form->{"project_id_$i"}} || SL::DB::Project->new;
 
1574       push @{ $form->{TEMPLATE_ARRAYS}->{linetotal} },                  $form->format_amount($myconfig, $linetotal, 2);
 
1575       push @{ $form->{TEMPLATE_ARRAYS}->{linetotal_nofmt} },            $linetotal_exact;
 
1576       push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} },       $form->format_amount($myconfig, $nodiscount_linetotal, 2);
 
1577       push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal_nofmt} }, $nodiscount_linetotal;
 
1578       push @{ $form->{TEMPLATE_ARRAYS}->{projectnumber} },              $project->projectnumber;
 
1579       push @{ $form->{TEMPLATE_ARRAYS}->{projectdescription} },         $project->description;
 
1581       my $lineweight = $form->{"qty_$i"} * $form->{"weight_$i"};
 
1582       $totalweight += $lineweight;
 
1583       push @{ $form->{TEMPLATE_ARRAYS}->{weight} },            $form->format_amount($myconfig, $form->{"weight_$i"}, 3);
 
1584       push @{ $form->{TEMPLATE_ARRAYS}->{weight_nofmt} },      $form->{"weight_$i"};
 
1585       push @{ $form->{TEMPLATE_ARRAYS}->{lineweight} },        $form->format_amount($myconfig, $lineweight, 3);
 
1586       push @{ $form->{TEMPLATE_ARRAYS}->{lineweight_nofmt} },  $lineweight;
 
1588       my ($taxamount, $taxbase);
 
1591       map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
 
1593       unless ($form->{"optional_$i"}) {
 
1594         if ($form->{taxincluded}) {
 
1597           $taxamount = $linetotal * $taxrate / (1 + $taxrate);
 
1598           $taxbase = $linetotal / (1 + $taxrate);
 
1600           $taxamount = $linetotal * $taxrate;
 
1601           $taxbase   = $linetotal;
 
1605       if ($taxamount != 0) {
 
1606         foreach my $accno (split / /, $form->{"taxaccounts_$i"}) {
 
1607           $taxaccounts{$accno} += $taxamount * $form->{"${accno}_rate"} / $taxrate;
 
1608           $taxbase{$accno}     += $taxbase;
 
1612       $tax_rate = $taxrate * 100;
 
1613       push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
 
1615       if ($form->{"part_type_$i"} eq 'assembly') {
 
1618         # get parts and push them onto the stack
 
1620         if ($form->{groupitems}) {
 
1621           $sortorder = qq|ORDER BY pg.partsgroup, a.position|;
 
1623           $sortorder = qq|ORDER BY a.position|;
 
1626         $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty, | .
 
1627                  qq|pg.partsgroup | .
 
1628                  qq|FROM assembly a | .
 
1629                  qq|  JOIN parts p ON (a.parts_id = p.id) | .
 
1630                  qq|    LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
 
1631                  qq|    WHERE a.bom = '1' | .
 
1632                  qq|    AND a.id = ? | . $sortorder;
 
1633         @values = ($form->{"id_$i"});
 
1634         $sth = $dbh->prepare($query);
 
1635         $sth->execute(@values) || $form->dberror($query);
 
1637         while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
 
1638           if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
 
1639             map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
 
1640             $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
 
1641             push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type}  }, 'assembly-item-partsgroup');
 
1642             push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
 
1645           push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type}  }, 'assembly-item');
 
1646           push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq|, $ref->{partnumber}, $ref->{description}|);
 
1647           map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
 
1652       CVar->get_non_editable_ic_cvars(form               => $form,
 
1655                                       sub_module         => 'orderitems',
 
1656                                       may_converted_from => ['orderitems', 'invoice']);
 
1658       push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} },
 
1659         CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)
 
1660           for @{ $ic_cvar_configs };
 
1662       push @{ $form->{TEMPLATE_ARRAYS}->{"project_cvar_" . $_->config->name} }, $_->value_as_text for @{ $project->cvars_by_config };
 
1666   $form->{totalweight}       = $form->format_amount($myconfig, $totalweight, 3);
 
1667   $form->{totalweight_nofmt} = $totalweight;
 
1668   my $defaults = AM->get_defaults();
 
1669   $form->{weightunit}        = $defaults->{weightunit};
 
1672   foreach $item (sort keys %taxaccounts) {
 
1673     $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
 
1675     push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase} },        $form->format_amount($myconfig, $taxbase{$item}, 2));
 
1676     push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase_nofmt} },  $taxbase{$item});
 
1677     push(@{ $form->{TEMPLATE_ARRAYS}->{tax} },            $form->format_amount($myconfig, $taxamount,      2));
 
1678     push(@{ $form->{TEMPLATE_ARRAYS}->{tax_nofmt} },      $taxamount);
 
1679     push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} },        $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
 
1680     push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate_nofmt} },  $form->{"${item}_rate"} * 100);
 
1681     push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} },      $form->{"${item}_taxnumber"});
 
1682     push(@{ $form->{TEMPLATE_ARRAYS}->{tax_id} },         $form->{"${item}_tax_id"});
 
1684     if ( $form->{"${item}_tax_id"} ) {
 
1685       my $tax_obj = SL::DB::Manager::Tax->find_by(id => $form->{"${item}_tax_id"}) or die "Can't find tax with id " . $form->{"${item}_tax_id"};
 
1686       my $description = $tax_obj ? $tax_obj->translated_attribute('taxdescription',  $form->{language_id}, 0) : '';
 
1687       push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $description . q{ } . 100 * $form->{"${item}_rate"} . q{%});
 
1691   $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
 
1692   $form->{discount_total}      = $form->format_amount($myconfig, $form->{discount_total}, 2);
 
1693   $form->{nodiscount}          = $form->format_amount($myconfig, $nodiscount, 2);
 
1694   $form->{yesdiscount}         = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
 
1696   if($form->{taxincluded}) {
 
1697     $form->{subtotal}       = $form->format_amount($myconfig, $form->{ordtotal} - $tax, 2);
 
1698     $form->{subtotal_nofmt} = $form->{ordtotal} - $tax;
 
1700     $form->{subtotal}       = $form->format_amount($myconfig, $form->{ordtotal}, 2);
 
1701     $form->{subtotal_nofmt} = $form->{ordtotal};
 
1704   my $grossamount = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
 
1705   $form->{ordtotal} = $form->round_amount( $grossamount, 2, 1);
 
1706   $form->{rounding} = $form->round_amount(
 
1707     $form->{ordtotal} - $form->round_amount($grossamount, 2),
 
1712   $form->{rounding} = $form->format_amount($myconfig, $form->{rounding}, 2);
 
1713   $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
 
1715   $form->set_payment_options($myconfig, $form->{$form->{type} =~ /_quotation/ ? 'quodate' : 'orddate'}, $form->{type});
 
1717   $form->{username} = $myconfig->{name};
 
1719   $form->{department}    = SL::DB::Manager::Department->find_by(id => $form->{department_id})->description if $form->{department_id};
 
1720   $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
 
1721   $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
 
1723   $form->{order} = SL::DB::Manager::Order->find_by(id => $form->{id}) if $form->{id};
 
1724   $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) for @separate_totals;
 
1726   $main::lxdebug->leave_sub();
 
1735 OE.pm - Order entry module
 
1739 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>.
 
1745 =item retrieve_simple PARAMS
 
1747 simple OE retrieval by id. does not look up customer, vendor, units or any other stuff. only oe and orderitems.
 
1749   my $order = retrieve_simple(id => 2);