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)) || ($vc eq 'vendor' && $main::auth->assert('purchase_all_edit', 1))) ) {
196 $query .= " AND o.employee_id = (select id from employee where login= ?)";
197 push @values, $::myconfig{login};
199 if ($form->{employee_id}) {
200 $query .= " AND o.employee_id = ?";
201 push @values, conv_i($form->{employee_id});
204 if ($form->{salesman_id}) {
205 $query .= " AND o.salesman_id = ?";
206 push @values, conv_i($form->{salesman_id});
209 if (!$form->{open} && !$form->{closed}) {
210 $query .= " AND o.id = 0";
211 } elsif (!($form->{open} && $form->{closed})) {
212 $query .= ($form->{open}) ? " AND o.closed = '0'" : " AND o.closed = '1'";
215 if (($form->{"notdelivered"} || $form->{"delivered"}) &&
216 ($form->{"notdelivered"} ne $form->{"delivered"})) {
217 $query .= $form->{"delivered"} ?
218 " AND o.delivered " : " AND NOT o.delivered";
221 if ($form->{$ordnumber}) {
222 $query .= qq| AND o.$ordnumber ILIKE ?|;
223 push(@values, like($form->{$ordnumber}));
226 if ($form->{cusordnumber}) {
227 $query .= qq| AND o.cusordnumber ILIKE ?|;
228 push(@values, like($form->{cusordnumber}));
231 if($form->{transdatefrom}) {
232 $query .= qq| AND o.transdate >= ?|;
233 push(@values, conv_date($form->{transdatefrom}));
236 if($form->{transdateto}) {
237 $query .= qq| AND o.transdate <= ?|;
238 push(@values, conv_date($form->{transdateto}));
241 if($form->{reqdatefrom}) {
242 $query .= qq| AND o.reqdate >= ?|;
243 push(@values, conv_date($form->{reqdatefrom}));
246 if($form->{reqdateto}) {
247 $query .= qq| AND o.reqdate <= ?|;
248 push(@values, conv_date($form->{reqdateto}));
251 if($form->{insertdatefrom}) {
252 $query .= qq| AND o.itime::DATE >= ?|;
253 push(@values, conv_date($form->{insertdatefrom}));
256 if($form->{insertdateto}) {
257 $query .= qq| AND o.itime::DATE <= ?|;
258 push(@values, conv_date($form->{insertdateto}));
261 if ($form->{shippingpoint}) {
262 $query .= qq| AND o.shippingpoint ILIKE ?|;
263 push(@values, like($form->{shippingpoint}));
266 if ($form->{taxzone_id} ne '') { # taxzone_id could be 0
267 $query .= qq| AND tz.id = ?|;
268 push(@values, $form->{taxzone_id});
271 if ($form->{transaction_description}) {
272 $query .= qq| AND o.transaction_description ILIKE ?|;
273 push(@values, like($form->{transaction_description}));
276 if ($form->{periodic_invoices_active} ne $form->{periodic_invoices_inactive}) {
277 my $not = $form->{periodic_invoices_inactive} ? 'NOT' : '';
278 $query .= qq| AND ${not} COALESCE(pcfg.active, 'f')|;
281 if ($form->{reqdate_unset_or_old}) {
282 $query .= qq| AND ((o.reqdate IS NULL) OR (o.reqdate < date_trunc('month', current_date)))|;
285 if (($form->{order_probability_value} || '') ne '') {
286 my $op = $form->{order_probability_value} eq 'le' ? '<=' : '>=';
287 $query .= qq| AND (o.order_probability ${op} ?)|;
288 push @values, trim($form->{order_probability_value});
291 if ($form->{expected_billing_date_from}) {
292 $query .= qq| AND (o.expected_billing_date >= ?)|;
293 push @values, conv_date($form->{expected_billing_date_from});
296 if ($form->{expected_billing_date_to}) {
297 $query .= qq| AND (o.expected_billing_date <= ?)|;
298 push @values, conv_date($form->{expected_billing_date_to});
301 if ($form->{intnotes}) {
302 $query .= qq| AND o.intnotes ILIKE ?|;
303 push(@values, like($form->{intnotes}));
306 if ($form->{parts_partnumber}) {
309 SELECT orderitems.trans_id
311 LEFT JOIN parts ON (orderitems.parts_id = parts.id)
312 WHERE (orderitems.trans_id = o.id)
313 AND (parts.partnumber ILIKE ?)
317 push @values, like($form->{parts_partnumber});
320 if ($form->{parts_description}) {
323 SELECT orderitems.trans_id
325 WHERE (orderitems.trans_id = o.id)
326 AND (orderitems.description ILIKE ?)
330 push @values, like($form->{parts_description});
334 my @tokens = parse_line('\s+', 0, $form->{all});
335 # ordnumber quonumber customer.name vendor.name transaction_description
337 o.ordnumber ILIKE ? OR
338 o.quonumber ILIKE ? OR
340 o.transaction_description ILIKE ?
342 push @values, (like($_))x4 for @tokens;
345 my ($cvar_where, @cvar_values) = CVar->build_filter_query('module' => 'CT',
346 'trans_id_field' => 'ct.id',
350 $query .= qq| AND ($cvar_where)|;
351 push @values, @cvar_values;
354 my $sortdir = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
355 my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name"), "o.itime"));
356 my %allowed_sort_columns = (
357 "transdate" => "o.transdate",
358 "reqdate" => "o.reqdate",
360 "ordnumber" => "o.ordnumber",
361 "cusordnumber" => "o.cusordnumber",
362 "quonumber" => "o.quonumber",
364 "employee" => "e.name",
365 "salesman" => "s.name",
366 "shipvia" => "o.shipvia",
367 "transaction_description" => "o.transaction_description",
368 "shippingpoint" => "o.shippingpoint",
369 "insertdate" => "o.itime",
370 "taxzone" => "tz.description",
371 "payment_terms" => "pt.description",
372 "department" => "department.description",
373 "intnotes" => "o.intnotes",
375 if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
376 $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}" . ", o.itime ${sortdir}";
378 $query .= qq| ORDER by | . $sortorder;
380 my $sth = $dbh->prepare($query);
381 $sth->execute(@values) ||
382 $form->dberror($query . " (" . join(", ", @values) . ")");
386 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
387 $ref->{billed_amount} = $billed_amount{$ref->{id}};
388 $ref->{billed_netamount} = $billed_netamount{$ref->{id}};
389 if ($ref->{billed_amount} < 0) { # case: credit note(s) higher than invoices
390 $ref->{remaining_amount} = $ref->{amount} + $ref->{billed_amount};
391 $ref->{remaining_netamount} = $ref->{netamount} + $ref->{billed_netamount};
393 $ref->{remaining_amount} = $ref->{amount} - $ref->{billed_amount};
394 $ref->{remaining_netamount} = $ref->{netamount} - $ref->{billed_netamount};
396 $ref->{exchangerate} ||= $ref->{daily_exchangerate};
397 $ref->{exchangerate} ||= 1;
398 push @{ $form->{OE} }, $ref if $ref->{id} != $id{ $ref->{id} };
399 $id{ $ref->{id} } = $ref->{id};
404 $main::lxdebug->leave_sub();
407 sub transactions_for_todo_list {
408 $main::lxdebug->enter_sub();
413 my $myconfig = \%main::myconfig;
414 my $form = $main::form;
416 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
418 my $query = qq|SELECT id FROM employee WHERE login = ?|;
419 my ($e_id) = selectrow_query($form, $dbh, $query, $::myconfig{login});
422 qq|SELECT oe.id, oe.transdate, oe.reqdate, oe.quonumber, oe.transaction_description, oe.amount,
423 CASE WHEN (COALESCE(oe.customer_id, 0) = 0) THEN 'vendor' ELSE 'customer' END AS vc,
428 LEFT JOIN customer c ON (oe.customer_id = c.id)
429 LEFT JOIN vendor v ON (oe.vendor_id = v.id)
430 LEFT JOIN employee e ON (oe.employee_id = e.id)
431 WHERE (COALESCE(quotation, FALSE) = TRUE)
432 AND (COALESCE(closed, FALSE) = FALSE)
433 AND ((oe.employee_id = ?) OR (oe.salesman_id = ?))
434 AND NOT (oe.reqdate ISNULL)
435 AND (oe.reqdate < current_date)
438 my $quotations = selectall_hashref_query($form, $dbh, $query, $e_id, $e_id);
440 $main::lxdebug->leave_sub();
446 my ($self, $myconfig, $form) = @_;
447 $main::lxdebug->enter_sub();
449 my $rc = SL::DB->client->with_transaction(\&_save, $self, $myconfig, $form);
451 $::lxdebug->leave_sub;
457 $main::lxdebug->enter_sub();
459 my ($self, $myconfig, $form) = @_;
461 my $dbh = SL::DB->client->dbh;
462 my $restricter = SL::HTML::Restrict->create;
464 my ($query, @values, $sth, $null);
465 my $exchangerate = 0;
467 my $all_units = AM->retrieve_units($myconfig, $form);
468 $form->{all_units} = $all_units;
470 my $ic_cvar_configs = CVar->get_configs(module => 'IC',
473 $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id};
474 unless ($form->{employee_id}) {
475 $form->get_employee($dbh);
478 my $ml = ($form->{type} eq 'sales_order') ? 1 : -1;
480 my $number_field = $form->{type} =~ m{order} ? 'ordnumber' : 'quonumber';
481 my $trans_number = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{$number_field}, id => $form->{id});
482 $form->{$number_field} ||= $trans_number->create_unique; # set $form->{ordnumber} or $form->{quonumber}
483 my $is_new = !$form->{id};
486 $query = qq|DELETE FROM custom_variables
487 WHERE (config_id IN (SELECT id FROM custom_variable_configs WHERE (module = 'ShipTo')))
488 AND (trans_id IN (SELECT shipto_id FROM shipto WHERE (module = 'OE') AND (trans_id = ?)))|;
489 do_query($form, $dbh, $query, $form->{id});
491 $query = qq|DELETE FROM shipto | .
492 qq|WHERE trans_id = ? AND module = 'OE'|;
493 do_query($form, $dbh, $query, $form->{id});
497 $query = qq|SELECT nextval('id')|;
498 ($form->{id}) = selectrow_query($form, $dbh, $query);
500 $query = qq|INSERT INTO oe (id, ordnumber, employee_id, currency_id, taxzone_id) VALUES (?, '', ?, (SELECT currency_id FROM defaults), ?)|;
501 do_query($form, $dbh, $query, $form->{id}, $form->{employee_id}, $form->{taxzone_id});
518 my @processed_orderitems;
520 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
521 my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
524 for my $i (1 .. $form->{rowcount}) {
526 map({ $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) } qw(qty ship));
528 if ($form->{"id_$i"}) {
531 $query = qq|SELECT unit FROM parts WHERE id = ?|;
532 my ($item_unit) = selectrow_query($form, $dbh, $query, $form->{"id_$i"});
535 if (defined($all_units->{$item_unit}->{factor}) &&
536 (($all_units->{$item_unit}->{factor} * 1) != 0)) {
537 $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
539 my $baseqty = $form->{"qty_$i"} * $basefactor;
541 $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
542 $form->{"marge_absolut_$i"} = $form->parse_amount($myconfig, $form->{"marge_absolut_$i"}) * 1;
544 $form->{"lastcost_$i"} = $form->parse_amount($myconfig, $form->{"lastcost_$i"});
546 # keep entered selling price
548 $form->parse_amount($myconfig, $form->{"sellprice_$i"});
550 my ($dec) = ($fxsellprice =~ /\.(\d+)/);
552 my $decimalplaces = ($dec > 2) ? $dec : 2;
554 # undo discount formatting
555 $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
558 $form->{"sellprice_$i"} = $fxsellprice * (1 - $form->{"discount_$i"});
560 # round linetotal at least to 2 decimal places
561 $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
562 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
564 $form->{"inventory_accno_$i"} *= 1;
565 $form->{"expense_accno_$i"} *= 1;
567 @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
571 map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
573 if ($form->{taxincluded}) {
574 $taxamount = $linetotal * $taxrate / (1 + $taxrate);
575 $taxbase = $linetotal - $taxamount;
577 # we are not keeping a natural price, do not round
578 $form->{"sellprice_$i"} =
579 $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
581 $taxamount = $linetotal * $taxrate;
582 $taxbase = $linetotal;
585 if ($form->round_amount($taxrate, 7) == 0) {
586 if ($form->{taxincluded}) {
587 foreach my $item (@taxaccounts) {
588 $taxamount = $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
589 $taxaccounts{$item} += $taxamount;
590 $taxdiff += $taxamount;
591 $taxbase{$item} += $taxbase;
593 $taxaccounts{ $taxaccounts[0] } += $taxdiff;
595 foreach my $item (@taxaccounts) {
596 $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
597 $taxbase{$item} += $taxbase;
601 foreach my $item (@taxaccounts) {
602 $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
603 $taxbase{$item} += $taxbase;
607 $netamount += $form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor;
609 $reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef;
611 # Get pricegroup_id and save it. Unfortunately the interface
612 # also uses ID "0" for signalling that none is selected, but "0"
613 # must not be stored in the database. Therefore we cannot simply
615 ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
617 $pricegroup_id = undef if !$pricegroup_id;
619 # force new project, if not set yet
620 if ($::instance_conf->get_order_always_project && !$form->{"globalproject_id"} && ($form->{type} eq 'sales_order')) {
621 require SL::DB::Customer;
622 my $customer = SL::DB::Manager::Customer->find_by(id => $form->{customer_id});
623 die "Can't find customer" unless $customer;
624 die $main::locale->text("Error while creating project with project number of new order number, project number #1 already exists!", $form->{ordnumber})
625 if SL::DB::Manager::Project->find_by(projectnumber => $form->{ordnumber});
627 my $new_project = SL::DB::Project->new(
628 projectnumber => $form->{ordnumber},
629 description => $customer->name,
630 customer_id => $customer->id,
632 project_type_id => $::instance_conf->get_project_type_id,
633 project_status_id => $::instance_conf->get_project_status_id,
636 $form->{"globalproject_id"} = $new_project->id;
639 CVar->get_non_editable_ic_cvars(form => $form,
642 sub_module => 'orderitems',
643 may_converted_from => ['orderitems', 'invoice']);
647 # save detail record in orderitems table
648 if (! $form->{"orderitems_id_$i"}) {
649 $query = qq|SELECT nextval('orderitemsid')|;
650 ($form->{"orderitems_id_$i"}) = selectrow_query($form, $dbh, $query);
652 $query = qq|INSERT INTO orderitems (id, position) VALUES (?, ?)|;
653 do_query($form, $dbh, $query, $form->{"orderitems_id_$i"}, conv_i($position));
656 my $orderitems_id = $form->{"orderitems_id_$i"};
657 push @processed_orderitems, $orderitems_id;
660 UPDATE orderitems SET
661 trans_id = ?, position = ?, parts_id = ?, description = ?, longdescription = ?, qty = ?, base_qty = ?,
662 sellprice = ?, discount = ?, unit = ?, reqdate = ?, project_id = ?, serialnumber = ?, ship = ?,
663 pricegroup_id = ?, subtotal = ?,
664 marge_percent = ?, marge_total = ?, lastcost = ?, price_factor_id = ?,
665 active_price_source = ?, active_discount_source = ?,
666 price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?
670 conv_i($form->{id}), conv_i($position), conv_i($form->{"id_$i"}),
671 $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}),
672 $form->{"qty_$i"}, $baseqty,
673 $fxsellprice, $form->{"discount_$i"},
674 $form->{"unit_$i"}, conv_date($reqdate), conv_i($form->{"project_id_$i"}),
675 $form->{"serialnumber_$i"}, $form->{"ship_$i"},
676 $pricegroup_id, $form->{"subtotal_$i"} ? 't' : 'f',
677 $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
678 $form->{"lastcost_$i"}, conv_i($form->{"price_factor_id_$i"}),
679 $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"},
680 conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}),
681 conv_i($orderitems_id),
684 do_query($form, $dbh, $query, @values);
686 $form->{"sellprice_$i"} = $fxsellprice;
687 $form->{"discount_$i"} *= 100;
689 CVar->save_custom_variables(module => 'IC',
690 sub_module => 'orderitems',
691 trans_id => $orderitems_id,
692 configs => $ic_cvar_configs,
694 name_prefix => 'ic_',
695 name_postfix => "_$i",
698 # link previous items with orderitems
699 # assume we have a new workflow if we link from invoice or order to quotation
700 # unluckily orderitems are used for quotation and orders - therefore one more
701 # check to be sure NOT to link from order to quotation
702 foreach (qw(orderitems)) {
703 if (!$form->{saveasnew} && !$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}
704 && $form->{type} !~ 'quotation') {
705 RecordLinks->create_links('dbh' => $dbh,
708 'from_ids' => $form->{"converted_from_${_}_id_$i"},
709 'to_table' => 'orderitems',
710 'to_id' => $orderitems_id,
713 delete $form->{"converted_from_${_}_id_$i"};
718 # search for orphaned ids
719 $query = sprintf 'SELECT id FROM orderitems WHERE trans_id = ? AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_orderitems;
720 @values = (conv_i($form->{id}), map { conv_i($_) } @processed_orderitems);
721 my @orphaned_ids = map { $_->{id} } selectall_hashref_query($form, $dbh, $query, @values);
723 if (scalar @orphaned_ids) {
724 # clean up orderitems
725 $query = sprintf 'DELETE FROM orderitems WHERE id IN (%s)', join ', ', ("?") x scalar @orphaned_ids;
726 do_query($form, $dbh, $query, @orphaned_ids);
729 $reqdate = ($form->{reqdate}) ? $form->{reqdate} : undef;
733 map { $tax += $form->round_amount($taxaccounts{$_}, 2) } keys %taxaccounts;
735 $amount = $form->round_amount($netamount + $tax, 2, 1);
736 $netamount = $form->round_amount($netamount, 2);
738 if ($form->{currency} eq $form->{defaultcurrency}) {
739 $form->{exchangerate} = 1;
741 $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? 'buy' : 'sell');
744 # from inputfield (exchangerate) or hidden (forex)
745 my $exchangerate_from_form = $form->{forex} || $form->parse_amount($myconfig, $form->{exchangerate});
747 $form->{exchangerate} = $exchangerate || $exchangerate_from_form;
749 my $quotation = $form->{type} =~ /_order$/ ? 'f' : 't';
754 ordnumber = ?, quonumber = ?, cusordnumber = ?, transdate = ?, vendor_id = ?,
755 customer_id = ?, amount = ?, netamount = ?, reqdate = ?, tax_point = ?, taxincluded = ?,
756 shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, currency_id = (SELECT id FROM currencies WHERE name=?), closed = ?,
757 delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
758 taxzone_id = ?, shipto_id = ?, billing_address_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,delivery_term_id = ?,
759 globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
760 , order_probability = ?, expected_billing_date = ?
763 @values = ($form->{ordnumber} || '', $form->{quonumber},
764 $form->{cusordnumber}, conv_date($form->{transdate}),
765 conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
766 $amount, $netamount, conv_date($reqdate), conv_date($form->{tax_point}),
767 $form->{taxincluded} ? 't' : 'f', $form->{shippingpoint},
768 $form->{shipvia}, $restricter->process($form->{notes}), $form->{intnotes},
769 $form->{currency}, $form->{closed} ? 't' : 'f',
770 $form->{delivered} ? "t" : "f", $form->{proforma} ? 't' : 'f',
771 $quotation, conv_i($form->{department_id}),
772 conv_i($form->{language_id}), conv_i($form->{taxzone_id}),
773 conv_i($form->{shipto_id}), conv_i($form->{billing_address_id}), conv_i($form->{payment_id}),
774 conv_i($form->{delivery_vendor_id}),
775 conv_i($form->{delivery_customer_id}),
776 conv_i($form->{delivery_term_id}),
777 conv_i($form->{globalproject_id}), conv_i($form->{employee_id}),
778 conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
779 $form->{transaction_description},
780 $form->{marge_total} * 1, $form->{marge_percent} * 1,
781 $form->{order_probability} * 1, conv_date($form->{expected_billing_date}),
782 conv_i($form->{id}));
783 do_query($form, $dbh, $query, @values);
785 $form->new_lastmtime('oe');
787 $form->{ordtotal} = $amount;
789 $form->{name} = $form->{ $form->{vc} };
790 $form->{name} =~ s/--\Q$form->{"$form->{vc}_id"}\E//;
793 if (!$form->{shipto_id}) {
794 $form->add_shipto($dbh, $form->{id}, "OE");
797 # save printed, emailed, queued
798 $form->save_status($dbh);
800 # Link this record to the records it was created from.
801 $form->{convert_from_oe_ids} =~ s/^\s+//;
802 $form->{convert_from_oe_ids} =~ s/\s+$//;
803 my @convert_from_oe_ids = split m/\s+/, $form->{convert_from_oe_ids};
804 delete $form->{convert_from_oe_ids};
805 if (!$form->{useasnew} && scalar @convert_from_oe_ids) {
806 RecordLinks->create_links('dbh' => $dbh,
808 'from_table' => 'oe',
809 'from_ids' => \@convert_from_oe_ids,
811 'to_id' => $form->{id},
813 $self->_close_quotations_rfqs('dbh' => $dbh,
814 'from_id' => \@convert_from_oe_ids,
815 'to_id' => $form->{id});
818 if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
819 if ($form->{vc} eq 'customer') {
820 $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
822 if ($form->{vc} eq 'vendor') {
823 $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
827 $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ?
828 "quonumber" : "ordnumber"};
830 Common::webdav_folder($form);
832 $self->save_periodic_invoices_config(dbh => $dbh,
833 oe_id => $form->{id},
834 config_yaml => $form->{periodic_invoices_config})
835 if ($form->{type} eq 'sales_order');
837 $self->_link_created_sales_order_to_requirement_specs_for_sales_quotations(
838 type => $form->{type},
839 converted_from_ids => \@convert_from_oe_ids,
840 sales_order_id => $form->{id},
844 $self->_set_project_in_linked_requirement_spec(
845 type => $form->{type},
846 project_id => $form->{globalproject_id},
847 sales_order_id => $form->{id},
850 $main::lxdebug->leave_sub();
855 sub _link_created_sales_order_to_requirement_specs_for_sales_quotations {
856 my ($self, %params) = @_;
858 # If this is a sales order created from a sales quotation and if
859 # that sales quotation was created from a requirement spec document
860 # then link the newly created sales order to the requirement spec
863 return if !$params{is_new};
864 return if $params{type} ne 'sales_order';
865 return if !@{ $params{converted_from_ids} };
867 my $oe_objects = SL::DB::Manager::Order->get_all(where => [ id => $params{converted_from_ids} ]);
868 my @sales_quotations = grep { $_->is_type('sales_quotation') } @{ $oe_objects };
870 return if !@sales_quotations;
872 my $rs_orders = SL::DB::Manager::RequirementSpecOrder->get_all(where => [ order_id => [ map { $_->id } @sales_quotations ] ]);
874 return if !@{ $rs_orders };
876 $rs_orders->[0]->db->with_transaction(sub {
877 foreach my $rs_order (@{ $rs_orders }) {
878 SL::DB::RequirementSpecOrder->new(
879 order_id => $params{sales_order_id},
880 requirement_spec_id => $rs_order->requirement_spec_id,
881 version_id => $rs_order->version_id,
889 sub _set_project_in_linked_requirement_spec {
890 my ($self, %params) = @_;
892 return if $params{type} ne 'sales_order';
893 return if !$params{project_id} || !$params{sales_order_id};
896 UPDATE requirement_specs
899 SELECT so.requirement_spec_id
900 FROM requirement_spec_orders so
901 WHERE so.order_id = ?
905 do_query($::form, $::form->get_standard_dbh, $query, $params{project_id}, $params{sales_order_id});
908 sub save_periodic_invoices_config {
909 my ($self, %params) = @_;
911 return if !$params{oe_id};
913 my $config = $params{config_yaml} ? SL::YAML::Load($params{config_yaml}) : undef;
914 return if 'HASH' ne ref $config;
916 my $obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $params{oe_id})
917 || SL::DB::PeriodicInvoicesConfig->new(oe_id => $params{oe_id});
918 $obj->update_attributes(%{ $config });
921 sub load_periodic_invoice_config {
925 delete $form->{periodic_invoices_config};
928 my $config_obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $form->{id});
931 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
932 print printer_id copies direct_debit send_email email_recipient_contact_id email_recipient_address email_sender email_subject email_body) };
933 $form->{periodic_invoices_config} = SL::YAML::Dump($config);
938 sub _close_quotations_rfqs {
939 $main::lxdebug->enter_sub();
944 Common::check_params(\%params, qw(from_id to_id));
946 my $myconfig = \%main::myconfig;
947 my $form = $main::form;
949 my $dbh = $params{dbh} || SL::DB->client->dbh;
951 SL::DB->client->with_transaction(sub {
953 my $query = qq|SELECT quotation FROM oe WHERE id = ?|;
954 my $sth = prepare_query($form, $dbh, $query);
956 do_statement($form, $sth, $query, conv_i($params{to_id}));
958 my ($quotation) = $sth->fetchrow_array();
966 foreach my $from_id (@{ $params{from_id} }) {
967 $from_id = conv_i($from_id);
968 do_statement($form, $sth, $query, $from_id);
969 ($quotation) = $sth->fetchrow_array();
970 push @close_ids, $from_id if ($quotation);
975 if (scalar @close_ids) {
976 $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_ids) . qq|)|;
977 do_query($form, $dbh, $query, @close_ids);
980 }) or do { die SL::DB->client->error };
982 $main::lxdebug->leave_sub();
986 $main::lxdebug->enter_sub();
988 my ($self, $myconfig, $form) = @_;
990 my $rc = SL::DB::Order->new->db->with_transaction(sub {
991 my @spoolfiles = grep { $_ } map { $_->spoolfile } @{ SL::DB::Manager::Status->get_all(where => [ trans_id => $form->{id} ]) };
993 SL::DB::Order->new(id => $form->{id})->delete;
995 my $spool = $::lx_office_conf{paths}->{spool};
996 unlink map { "$spool/$_" } @spoolfiles if $spool;
999 }) or do { die SL::DB->client->error };
1001 $main::lxdebug->leave_sub();
1007 my ($self, $myconfig, $form) = @_;
1008 $main::lxdebug->enter_sub();
1010 my $rc = SL::DB->client->with_transaction(\&_retrieve, $self, $myconfig, $form);
1012 $::lxdebug->leave_sub;
1017 my ($self, $myconfig, $form) = @_;
1019 # connect to database
1020 my $dbh = SL::DB->client->dbh;
1022 my ($query, $query_add, @values, @ids, $sth);
1024 # translate the ids (given by id_# and trans_id_#) into one array of ids, so we can join them later
1026 push @ids, $form->{"trans_id_$_"}
1027 if ($form->{"multi_id_$_"} and $form->{"trans_id_$_"})
1028 } (1 .. $form->{"rowcount"});
1030 if ($form->{rowcount} && scalar @ids) {
1031 $form->{convert_from_oe_ids} = join ' ', @ids;
1034 # if called in multi id mode, and still only got one id, switch back to single id
1035 if ($form->{"rowcount"} and $#ids == 0) {
1036 $form->{"id"} = $ids[0];
1038 delete $form->{convert_from_oe_ids};
1041 # and remember for the rest of the function
1042 my $is_collective_order = scalar @ids;
1044 # If collective order was created from exactly 1 order, we assume the same
1045 # behaviour as a "save as new" from within an order is actually desired, i.e.
1046 # the original order isn't part of a workflow where we want to remember
1047 # record_links, but simply a quick way of generating a new order from an old
1048 # one without having to enter everything again.
1049 # Setting useasnew will prevent the creation of record_links for the items
1050 # when saving the new order.
1051 # This form variable is probably not necessary, could just set saveasnew instead
1052 $form->{useasnew} = 1 if $is_collective_order == 1;
1055 my $extra_days = $form->{type} eq 'sales_quotation' ? $::instance_conf->get_reqdate_interval :
1056 $form->{type} eq 'sales_order' ? $::instance_conf->get_delivery_date_interval : 1;
1057 if ( ($form->{type} eq 'sales_order' && !$::instance_conf->get_deliverydate_on)
1058 || ($form->{type} eq 'sales_quotation' && !$::instance_conf->get_reqdate_on)) {
1059 $form->{reqdate} = '';
1061 $form->{reqdate} = DateTime->today_local->next_workday(extra_days => $extra_days)->to_kivitendo;
1063 $form->{transdate} = DateTime->today_local->to_kivitendo;
1066 # get default accounts
1067 $query = qq|SELECT (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
1068 (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id) AS income_accno,
1069 (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id) AS expense_accno,
1070 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
1071 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
1072 (SELECT c.accno FROM chart c WHERE d.rndgain_accno_id = c.id) AS rndgain_accno,
1073 (SELECT c.accno FROM chart c WHERE d.rndloss_accno_id = c.id) AS rndloss_accno
1076 my $ref = selectfirst_hashref_query($form, $dbh, $query);
1077 map { $form->{$_} = $ref->{$_} } keys %$ref;
1079 $form->{currency} = $form->get_default_currency($myconfig);
1081 # set reqdate if this is an invoice->order conversion. If someone knows a better check to ensure
1082 # we come from invoices, feel free.
1083 $form->{reqdate} = $form->{deliverydate}
1084 if ( $form->{deliverydate}
1085 and $form->{callback} =~ /action=ar_transactions/);
1087 my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
1089 if ($form->{id} or @ids) {
1091 # retrieve order for single id
1092 # NOTE: this query is intended to fetch all information only ONCE.
1093 # so if any of these infos is important (or even different) for any item,
1094 # it will be killed out and then has to be fetched from the item scope query further down
1096 qq|SELECT o.cp_id, o.ordnumber, o.transdate, o.reqdate,
1097 o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
1098 (SELECT cu.name FROM currencies cu WHERE cu.id=o.currency_id) AS currency, e.name AS employee, o.employee_id, o.salesman_id,
1099 o.${vc}_id, cv.name AS ${vc}, o.amount AS invtotal,
1100 o.closed, o.reqdate, o.tax_point, o.quonumber, o.department_id, o.cusordnumber,
1102 d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
1103 o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id, o.billing_address_id,
1104 o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id,
1105 o.itime::DATE AS insertdate, o.order_probability, o.expected_billing_date
1107 JOIN ${vc} cv ON (o.${vc}_id = cv.id)
1108 LEFT JOIN employee e ON (o.employee_id = e.id)
1109 LEFT JOIN department d ON (o.department_id = d.id) | .
1112 : "WHERE o.id IN (" . join(', ', map("? ", @ids)) . ")"
1114 @values = $form->{id} ? ($form->{id}) : @ids;
1115 $sth = prepare_execute_query($form, $dbh, $query, @values);
1117 $ref = $sth->fetchrow_hashref("NAME_lc");
1120 map { $form->{$_} = $ref->{$_} } keys %$ref;
1122 $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ? "quonumber" : "ordnumber"};
1124 # set all entries for multiple ids blank that yield different information
1125 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1126 map { $form->{$_} = '' if ($ref->{$_} ne $form->{$_}) } keys %$ref;
1129 $form->{mtime} ||= $form->{itime};
1130 $form->{lastmtime} = $form->{mtime};
1132 # if not given, fill transdate with current_date
1133 $form->{transdate} = $form->current_date($myconfig)
1134 unless $form->{transdate};
1138 if ($form->{delivery_customer_id}) {
1139 $query = qq|SELECT name FROM customer WHERE id = ?|;
1140 ($form->{delivery_customer_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_customer_id});
1143 if ($form->{delivery_vendor_id}) {
1144 $query = qq|SELECT name FROM customer WHERE id = ?|;
1145 ($form->{delivery_vendor_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_vendor_id});
1148 # shipto and pinted/mailed/queued status makes only sense for single id retrieve
1150 $query = qq|SELECT s.* FROM shipto s WHERE s.trans_id = ? AND s.module = 'OE'|;
1151 $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
1153 $ref = $sth->fetchrow_hashref("NAME_lc");
1154 $form->{$_} = $ref->{$_} for grep { m{^shipto(?!_id$)} } keys %$ref;
1157 if ($ref->{shipto_id}) {
1158 my $cvars = CVar->get_custom_variables(
1161 trans_id => $ref->{shipto_id},
1163 $form->{"shiptocvar_$_->{name}"} = $_->{value} for @{ $cvars };
1166 # get printed, emailed and queued
1167 $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
1168 $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
1170 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1171 $form->{printed} .= "$ref->{formname} " if $ref->{printed};
1172 $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
1173 $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
1176 map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
1179 my $transdate = $form->{tax_point} ? $dbh->quote($form->{tax_point}) : $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
1181 $form->{taxzone_id} = 0 unless ($form->{taxzone_id});
1182 unshift @values, ($form->{taxzone_id}) x 2;
1184 # retrieve individual items
1185 # this query looks up all information about the items
1186 # stuff different from the whole will not be overwritten, but saved with a suffix.
1188 qq|SELECT o.id AS orderitems_id,
1189 c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from as inventory_valid,
1190 c2.accno AS income_accno, c2.new_chart_id AS income_new_chart, date($transdate) - c2.valid_from as income_valid,
1191 c3.accno AS expense_accno, c3.new_chart_id AS expense_new_chart, date($transdate) - c3.valid_from as expense_valid,
1192 oe.ordnumber AS ordnumber_oe, oe.transdate AS transdate_oe, oe.cusordnumber AS cusordnumber_oe,
1193 p.partnumber, p.part_type, p.listprice, o.description, o.qty,
1194 p.classification_id,
1195 o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.part_type,
1196 o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
1197 o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
1198 o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source, o.active_discount_source,
1199 pr.projectnumber, p.formel,
1200 pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
1202 JOIN parts p ON (o.parts_id = p.id)
1203 JOIN oe ON (o.trans_id = oe.id)
1204 LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c1.id)
1205 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)
1206 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)
1207 LEFT JOIN project pr ON (o.project_id = pr.id)
1208 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
1210 ? qq|WHERE o.trans_id = ?|
1211 : qq|WHERE o.trans_id IN (| . join(", ", map("?", @ids)) . qq|)|) .
1212 qq|ORDER BY o.trans_id, o.position|;
1214 @ids = $form->{id} ? ($form->{id}) : @ids;
1215 $sth = prepare_execute_query($form, $dbh, $query, @values);
1217 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1218 # Retrieve custom variables.
1219 my $cvars = CVar->get_custom_variables(dbh => $dbh,
1221 sub_module => 'orderitems',
1222 trans_id => $ref->{orderitems_id},
1224 map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
1227 if (!$ref->{"part_type"} eq 'part') {
1228 map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid));
1230 # delete($ref->{"part_inventory_accno_id"});
1232 # in collective order, copy global ordnumber, transdate, cusordnumber into item scope
1233 # unless already present there
1234 # remove _oe entries afterwards
1235 map { $ref->{$_} = $ref->{"${_}_oe"} if ($ref->{$_} eq '') }
1236 qw|ordnumber transdate cusordnumber|
1238 map { delete $ref->{$_} } qw|ordnumber_oe transdate_oe cusordnumber_oe|;
1242 while ($ref->{inventory_new_chart} && ($ref->{inventory_valid} >= 0)) {
1244 qq|SELECT accno AS inventory_accno, | .
1245 qq| new_chart_id AS inventory_new_chart, | .
1246 qq| date($transdate) - valid_from AS inventory_valid | .
1247 qq|FROM chart WHERE id = $ref->{inventory_new_chart}|;
1248 ($ref->{inventory_accno}, $ref->{inventory_new_chart},
1249 $ref->{inventory_valid}) = selectrow_query($form, $dbh, $query);
1252 while ($ref->{income_new_chart} && ($ref->{income_valid} >= 0)) {
1254 qq|SELECT accno AS income_accno, | .
1255 qq| new_chart_id AS income_new_chart, | .
1256 qq| date($transdate) - valid_from AS income_valid | .
1257 qq|FROM chart WHERE id = $ref->{income_new_chart}|;
1258 ($ref->{income_accno}, $ref->{income_new_chart},
1259 $ref->{income_valid}) = selectrow_query($form, $dbh, $query);
1262 while ($ref->{expense_new_chart} && ($ref->{expense_valid} >= 0)) {
1264 qq|SELECT accno AS expense_accno, | .
1265 qq| new_chart_id AS expense_new_chart, | .
1266 qq| date($transdate) - valid_from AS expense_valid | .
1267 qq|FROM chart WHERE id = $ref->{expense_new_chart}|;
1268 ($ref->{expense_accno}, $ref->{expense_new_chart},
1269 $ref->{expense_valid}) = selectrow_query($form, $dbh, $query);
1272 # delete orderitems_id in collective orders, so that they get cloned no matter what
1273 # is this correct? or is the following meant?
1274 # remember orderitems_ids in converted_from_orderitems_ids, so that they may be linked
1275 $ref->{converted_from_orderitems_id} = delete $ref->{orderitems_id} if $is_collective_order;
1277 # get tax rates and description
1278 my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1280 qq|SELECT c.accno, t.taxdescription, t.rate, t.id as tax_id, c.accno as taxnumber | .
1282 qq|LEFT JOIN chart c on (c.id = t.chart_id) | .
1283 qq|WHERE t.id IN (SELECT tk.tax_id FROM taxkeys tk | .
1284 qq| WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?) | .
1285 qq| AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1) | .
1286 qq|ORDER BY c.accno|;
1287 my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
1288 $ref->{taxaccounts} = "";
1290 while (my $ptr = $stw->fetchrow_hashref("NAME_lc")) {
1291 if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1295 $ref->{taxaccounts} .= "$ptr->{accno} ";
1296 if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
1297 $form->{"$ptr->{accno}_rate"} = $ptr->{rate};
1298 $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1299 $form->{"$ptr->{accno}_taxnumber"} = $ptr->{taxnumber};
1300 $form->{"$ptr->{accno}_tax_id"} = $ptr->{tax_id};
1301 $form->{taxaccounts} .= "$ptr->{accno} ";
1306 chop $ref->{taxaccounts};
1308 push @{ $form->{form_details} }, $ref;
1315 # get last name used
1316 $form->lastname_used($dbh, $myconfig, $form->{vc})
1317 unless $form->{"$form->{vc}_id"};
1321 $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? "buy" : "sell");
1323 Common::webdav_folder($form);
1325 $self->load_periodic_invoice_config($form);
1330 sub retrieve_simple {
1331 $main::lxdebug->enter_sub();
1336 Common::check_params(\%params, qw(id));
1338 my $myconfig = \%main::myconfig;
1339 my $form = $main::form;
1341 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
1343 my $oe_query = qq|SELECT * FROM oe WHERE id = ?|;
1344 my $oi_query = qq|SELECT * FROM orderitems WHERE trans_id = ? ORDER BY position|;
1346 my $order = selectfirst_hashref_query($form, $dbh, $oe_query, conv_i($params{id}));
1347 $order->{orderitems} = selectall_hashref_query( $form, $dbh, $oi_query, conv_i($params{id}));
1349 $main::lxdebug->leave_sub();
1355 $main::lxdebug->enter_sub();
1357 my ($self, $myconfig, $form) = @_;
1359 # connect to database
1360 my $dbh = SL::DB->client->dbh;
1366 my $nodiscount_subtotal = 0;
1367 my $discount_subtotal = 0;
1370 my @partsgroup = ();
1373 my $subtotal_header = 0;
1374 my $subposition = 0;
1382 push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
1384 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
1387 foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
1388 $price_factors{$pfac->{id}} = $pfac;
1389 $pfac->{factor} *= 1;
1390 $pfac->{formatted_factor} = $form->format_amount($myconfig, $pfac->{factor});
1393 # sort items by partsgroup
1394 for $i (1 .. $form->{rowcount}) {
1396 if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
1397 $partsgroup = $form->{"partsgroup_$i"};
1399 push @partsgroup, [$i, $partsgroup];
1400 push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
1406 $projects = SL::DB::Manager::Project->get_all(query => [ id => \@project_ids ]);
1407 %projects_by_id = map { $_->id => $_ } @$projects;
1410 if ($projects_by_id{$form->{"globalproject_id"}}) {
1411 $form->{globalprojectnumber} = $projects_by_id{$form->{"globalproject_id"}}->projectnumber;
1412 $form->{globalprojectdescription} = $projects_by_id{$form->{"globalproject_id"}}->description;
1414 for (@{ $projects_by_id{$form->{"globalproject_id"}}->cvars_by_config }) {
1415 $form->{"project_cvar_" . $_->config->name} = $_->value_as_text;
1419 $form->{discount} = [];
1421 # get some values of parts from db on store them in extra array,
1422 # so that they can be sorted in later
1423 my %prepared_template_arrays = IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
1424 my @prepared_arrays = keys %prepared_template_arrays;
1425 my @separate_totals = qw(non_separate_subtotal);
1427 $form->{TEMPLATE_ARRAYS} = { };
1429 my $ic_cvar_configs = CVar->get_configs(module => 'IC');
1430 my $project_cvar_configs = CVar->get_configs(module => 'Projects');
1433 qw(runningnumber number description longdescription qty qty_nofmt ship ship_nofmt unit bin
1434 partnotes serialnumber reqdate sellprice sellprice_nofmt listprice listprice_nofmt netprice netprice_nofmt
1435 discount discount_nofmt p_discount discount_sub discount_sub_nofmt nodiscount_sub nodiscount_sub_nofmt
1436 linetotal linetotal_nofmt nodiscount_linetotal nodiscount_linetotal_nofmt tax_rate projectnumber projectdescription
1437 price_factor price_factor_name partsgroup weight weight_nofmt lineweight lineweight_nofmt optional);
1439 push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
1440 push @arrays, map { "project_cvar_$_->{name}" } @{ $project_cvar_configs };
1442 my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
1444 map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @prepared_arrays);
1446 my $totalweight = 0;
1448 foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
1451 if ($item->[1] ne $sameitem) {
1452 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'partsgroup');
1453 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
1454 $sameitem = $item->[1];
1456 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1459 $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
1461 if ($form->{"id_$i"} != 0) {
1463 # add number, description and qty to $form->{number}, ....
1465 if ($form->{"subtotal_$i"} && !$subtotal_header) {
1466 $subtotal_header = $i;
1467 $position = int($position);
1470 } elsif ($subtotal_header) {
1472 $position = int($position);
1473 $position = $position.".".$subposition;
1475 $position = int($position);
1479 my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
1481 push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, $prepared_template_arrays{$_}[$i - 1]) for @prepared_arrays;
1483 push @{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'normal';
1484 push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} }, $position;
1485 push @{ $form->{TEMPLATE_ARRAYS}->{number} }, $form->{"partnumber_$i"};
1486 push @{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->{"description_$i"};
1487 push @{ $form->{TEMPLATE_ARRAYS}->{longdescription} }, $form->{"longdescription_$i"};
1488 push @{ $form->{TEMPLATE_ARRAYS}->{qty} }, $form->format_amount($myconfig, $form->{"qty_$i"});
1489 push @{ $form->{TEMPLATE_ARRAYS}->{qty_nofmt} }, $form->{"qty_$i"};
1490 push @{ $form->{TEMPLATE_ARRAYS}->{ship} }, $form->format_amount($myconfig, $form->{"ship_$i"});
1491 push @{ $form->{TEMPLATE_ARRAYS}->{ship_nofmt} }, $form->{"ship_$i"};
1492 push @{ $form->{TEMPLATE_ARRAYS}->{unit} }, $form->{"unit_$i"};
1493 push @{ $form->{TEMPLATE_ARRAYS}->{bin} }, $form->{"bin_$i"};
1494 push @{ $form->{TEMPLATE_ARRAYS}->{partnotes} }, $form->{"partnotes_$i"};
1495 push @{ $form->{TEMPLATE_ARRAYS}->{serialnumber} }, $form->{"serialnumber_$i"};
1496 push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} }, $form->{"reqdate_$i"};
1497 push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} }, $form->{"sellprice_$i"};
1498 push @{ $form->{TEMPLATE_ARRAYS}->{sellprice_nofmt} }, $form->parse_amount($myconfig, $form->{"sellprice_$i"});
1499 push @{ $form->{TEMPLATE_ARRAYS}->{listprice} }, $form->format_amount($myconfig, $form->{"listprice_$i"}, 2);
1500 push @{ $form->{TEMPLATE_ARRAYS}->{listprice_nofmt} }, $form->{"listprice_$i"};
1501 push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} }, $price_factor->{formatted_factor};
1502 push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
1503 push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} }, $form->{"partsgroup_$i"};
1504 push @{ $form->{TEMPLATE_ARRAYS}->{optional} }, $form->{"optional_$i"};
1506 my $sellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
1507 my ($dec) = ($sellprice =~ /\.(\d+)/);
1508 my $decimalplaces = max 2, length($dec);
1510 my $parsed_discount = $form->parse_amount($myconfig, $form->{"discount_$i"});
1512 my $linetotal_exact = $form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor};
1513 my $linetotal = $form->round_amount($linetotal_exact, 2);
1515 my $nodiscount_exact_linetotal = $form->{"qty_$i"} * $sellprice / $price_factor->{factor};
1516 my $nodiscount_linetotal = $form->round_amount($nodiscount_exact_linetotal,2);
1518 my $discount = $nodiscount_linetotal - $linetotal; # is always rounded because $nodiscount_linetotal and $linetotal are rounded
1520 my $discount_round_error = $discount + ($linetotal_exact - $nodiscount_exact_linetotal); # not used
1522 $form->{"netprice_$i"} = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, $decimalplaces);
1524 push @{ $form->{TEMPLATE_ARRAYS}->{netprice} }, ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
1525 push @{ $form->{TEMPLATE_ARRAYS}->{netprice_nofmt} }, ($form->{"netprice_$i"} != 0) ? $form->{"netprice_$i"} : '';
1527 $linetotal = ($linetotal != 0) ? $linetotal : '';
1529 push @{ $form->{TEMPLATE_ARRAYS}->{discount} }, ($discount != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
1530 push @{ $form->{TEMPLATE_ARRAYS}->{discount_nofmt} }, ($discount != 0) ? $discount * -1 : '';
1531 push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} }, $form->{"discount_$i"};
1533 if ( $prepared_template_arrays{separate}[$i - 1] ) {
1534 my $pabbr = $prepared_template_arrays{separate}[$i - 1];
1535 if ( ! $form->{"separate_${pabbr}_subtotal"} ) {
1536 push @separate_totals , "separate_${pabbr}_subtotal";
1537 $form->{"separate_${pabbr}_subtotal"} = 0;
1539 $form->{"separate_${pabbr}_subtotal"} += $linetotal;
1541 $form->{non_separate_subtotal} += $linetotal;
1544 $form->{ordtotal} += $linetotal unless $form->{"optional_$i"};
1545 $form->{nodiscount_total} += $nodiscount_linetotal;
1546 $form->{discount_total} += $discount;
1548 if ($subtotal_header) {
1549 $discount_subtotal += $linetotal;
1550 $nodiscount_subtotal += $nodiscount_linetotal;
1553 if ($form->{"subtotal_$i"} && $subtotal_header && ($subtotal_header != $i)) {
1554 push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} }, $form->format_amount($myconfig, $discount_subtotal, 2);
1555 push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub_nofmt} }, $discount_subtotal;
1556 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} }, $form->format_amount($myconfig, $nodiscount_subtotal, 2);
1557 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub_nofmt} }, $nodiscount_subtotal;
1559 $discount_subtotal = 0;
1560 $nodiscount_subtotal = 0;
1561 $subtotal_header = 0;
1564 push @{ $form->{TEMPLATE_ARRAYS}->{$_} }, "" for qw(discount_sub nodiscount_sub discount_sub_nofmt nodiscount_sub_nofmt);
1567 if (!$form->{"discount_$i"}) {
1568 $nodiscount += $linetotal;
1571 my $project = $projects_by_id{$form->{"project_id_$i"}} || SL::DB::Project->new;
1573 push @{ $form->{TEMPLATE_ARRAYS}->{linetotal} }, $form->format_amount($myconfig, $linetotal, 2);
1574 push @{ $form->{TEMPLATE_ARRAYS}->{linetotal_nofmt} }, $linetotal_exact;
1575 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} }, $form->format_amount($myconfig, $nodiscount_linetotal, 2);
1576 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal_nofmt} }, $nodiscount_linetotal;
1577 push @{ $form->{TEMPLATE_ARRAYS}->{projectnumber} }, $project->projectnumber;
1578 push @{ $form->{TEMPLATE_ARRAYS}->{projectdescription} }, $project->description;
1580 my $lineweight = $form->{"qty_$i"} * $form->{"weight_$i"};
1581 $totalweight += $lineweight;
1582 push @{ $form->{TEMPLATE_ARRAYS}->{weight} }, $form->format_amount($myconfig, $form->{"weight_$i"}, 3);
1583 push @{ $form->{TEMPLATE_ARRAYS}->{weight_nofmt} }, $form->{"weight_$i"};
1584 push @{ $form->{TEMPLATE_ARRAYS}->{lineweight} }, $form->format_amount($myconfig, $lineweight, 3);
1585 push @{ $form->{TEMPLATE_ARRAYS}->{lineweight_nofmt} }, $lineweight;
1587 my ($taxamount, $taxbase);
1590 map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
1592 unless ($form->{"optional_$i"}) {
1593 if ($form->{taxincluded}) {
1596 $taxamount = $linetotal * $taxrate / (1 + $taxrate);
1597 $taxbase = $linetotal / (1 + $taxrate);
1599 $taxamount = $linetotal * $taxrate;
1600 $taxbase = $linetotal;
1604 if ($taxamount != 0) {
1605 foreach my $accno (split / /, $form->{"taxaccounts_$i"}) {
1606 $taxaccounts{$accno} += $taxamount * $form->{"${accno}_rate"} / $taxrate;
1607 $taxbase{$accno} += $taxbase;
1611 $tax_rate = $taxrate * 100;
1612 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
1614 if ($form->{"part_type_$i"} eq 'assembly') {
1617 # get parts and push them onto the stack
1619 if ($form->{groupitems}) {
1620 $sortorder = qq|ORDER BY pg.partsgroup, a.position|;
1622 $sortorder = qq|ORDER BY a.position|;
1625 $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty, | .
1626 qq|pg.partsgroup | .
1627 qq|FROM assembly a | .
1628 qq| JOIN parts p ON (a.parts_id = p.id) | .
1629 qq| LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
1630 qq| WHERE a.bom = '1' | .
1631 qq| AND a.id = ? | . $sortorder;
1632 @values = ($form->{"id_$i"});
1633 $sth = $dbh->prepare($query);
1634 $sth->execute(@values) || $form->dberror($query);
1636 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
1637 if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
1638 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1639 $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
1640 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'assembly-item-partsgroup');
1641 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
1644 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'assembly-item');
1645 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq|, $ref->{partnumber}, $ref->{description}|);
1646 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1651 CVar->get_non_editable_ic_cvars(form => $form,
1654 sub_module => 'orderitems',
1655 may_converted_from => ['orderitems', 'invoice']);
1657 push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} },
1658 CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)
1659 for @{ $ic_cvar_configs };
1661 push @{ $form->{TEMPLATE_ARRAYS}->{"project_cvar_" . $_->config->name} }, $_->value_as_text for @{ $project->cvars_by_config };
1665 $form->{totalweight} = $form->format_amount($myconfig, $totalweight, 3);
1666 $form->{totalweight_nofmt} = $totalweight;
1667 my $defaults = AM->get_defaults();
1668 $form->{weightunit} = $defaults->{weightunit};
1671 foreach $item (sort keys %taxaccounts) {
1672 $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
1674 push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase} }, $form->format_amount($myconfig, $taxbase{$item}, 2));
1675 push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase_nofmt} }, $taxbase{$item});
1676 push(@{ $form->{TEMPLATE_ARRAYS}->{tax} }, $form->format_amount($myconfig, $taxamount, 2));
1677 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_nofmt} }, $taxamount);
1678 push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} }, $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
1679 push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate_nofmt} }, $form->{"${item}_rate"} * 100);
1680 push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} }, $form->{"${item}_taxnumber"});
1681 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_id} }, $form->{"${item}_tax_id"});
1683 if ( $form->{"${item}_tax_id"} ) {
1684 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"};
1685 my $description = $tax_obj ? $tax_obj->translated_attribute('taxdescription', $form->{language_id}, 0) : '';
1686 push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $description . q{ } . 100 * $form->{"${item}_rate"} . q{%});
1690 $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
1691 $form->{discount_total} = $form->format_amount($myconfig, $form->{discount_total}, 2);
1692 $form->{nodiscount} = $form->format_amount($myconfig, $nodiscount, 2);
1693 $form->{yesdiscount} = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
1695 if($form->{taxincluded}) {
1696 $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal} - $tax, 2);
1697 $form->{subtotal_nofmt} = $form->{ordtotal} - $tax;
1699 $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
1700 $form->{subtotal_nofmt} = $form->{ordtotal};
1703 my $grossamount = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
1704 $form->{ordtotal} = $form->round_amount( $grossamount, 2, 1);
1705 $form->{rounding} = $form->round_amount(
1706 $form->{ordtotal} - $form->round_amount($grossamount, 2),
1711 $form->{rounding} = $form->format_amount($myconfig, $form->{rounding}, 2);
1712 $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
1714 $form->set_payment_options($myconfig, $form->{$form->{type} =~ /_quotation/ ? 'quodate' : 'orddate'}, $form->{type});
1716 $form->{username} = $myconfig->{name};
1718 $form->{department} = SL::DB::Manager::Department->find_by(id => $form->{department_id})->description if $form->{department_id};
1719 $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
1720 $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
1722 $form->{order} = SL::DB::Manager::Order->find_by(id => $form->{id}) if $form->{id};
1723 $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) for @separate_totals;
1725 $main::lxdebug->leave_sub();
1734 OE.pm - Order entry module
1738 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>.
1744 =item retrieve_simple PARAMS
1746 simple OE retrieval by id. does not look up customer, vendor, units or any other stuff. only oe and orderitems.
1748 my $order = retrieve_simple(id => 2);