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};
119 my ($phone_notes_columns, $phone_notes_join);
120 $form->{phone_notes} = trim($form->{phone_notes});
121 if ($form->{phone_notes}) {
122 $phone_notes_columns = qq| , phone_notes.subject AS phone_notes_subject, phone_notes.body AS phone_notes_body |;
123 $phone_notes_join = qq| JOIN notes phone_notes ON (o.id = phone_notes.trans_id AND phone_notes.trans_module LIKE 'oe') |;
127 qq|SELECT o.id, o.ordnumber, o.transdate, o.reqdate, | .
128 qq| o.amount, ct.${vc}number, ct.name, o.netamount, o.${vc}_id, o.globalproject_id, | .
129 qq| o.closed, o.delivered, o.quonumber, o.cusordnumber, o.shippingpoint, o.shipvia, | .
130 qq| o.transaction_description, | .
131 qq| o.marge_total, o.marge_percent, | .
132 qq| o.exchangerate, | .
133 qq| o.itime::DATE AS insertdate, | .
135 qq| department.description as department, | .
136 qq| ex.$rate AS daily_exchangerate, | .
137 qq| pt.description AS payment_terms, | .
138 qq| pr.projectnumber AS globalprojectnumber, | .
139 qq| e.name AS employee, s.name AS salesman, | .
140 qq| ct.${vc}number AS vcnumber, ct.country, ct.ustid, ct.business_id, | .
141 qq| tz.description AS taxzone | .
142 $periodic_invoices_columns .
143 $phone_notes_columns .
144 qq| , o.order_probability, o.expected_billing_date, (o.netamount * o.order_probability / 100) AS expected_netamount | .
146 qq|JOIN $vc ct ON (o.${vc}_id = ct.id) | .
147 qq|LEFT JOIN contacts cp ON (o.cp_id = cp.cp_id) | .
148 qq|LEFT JOIN employee e ON (o.employee_id = e.id) | .
149 qq|LEFT JOIN employee s ON (o.salesman_id = s.id) | .
150 qq|LEFT JOIN exchangerate ex ON (ex.currency_id = o.currency_id | .
151 qq| AND ex.transdate = o.transdate) | .
152 qq|LEFT JOIN project pr ON (o.globalproject_id = pr.id) | .
153 qq|LEFT JOIN payment_terms pt ON (pt.id = o.payment_id)| .
154 qq|LEFT JOIN tax_zones tz ON (o.taxzone_id = tz.id) | .
155 qq|LEFT JOIN department ON (o.department_id = department.id) | .
156 qq|$periodic_invoices_joins | .
158 qq|WHERE (o.quotation = ?) |;
159 push(@values, $quotation);
161 if ($form->{department_id}) {
162 $query .= qq| AND o.department_id = ?|;
163 push(@values, $form->{department_id});
166 if ($form->{"project_id"}) {
168 qq|AND ((globalproject_id = ?) OR EXISTS | .
169 qq| (SELECT * FROM orderitems oi | .
170 qq| WHERE oi.project_id = ? AND oi.trans_id = o.id))|;
171 push(@values, conv_i($form->{"project_id"}), conv_i($form->{"project_id"}));
174 if ($form->{"projectnumber"}) {
176 AND ((pr.projectnumber ILIKE ?) OR EXISTS (
177 SELECT * FROM orderitems oi
178 LEFT JOIN project proi ON proi.id = oi.project_id
179 WHERE proi.projectnumber ILIKE ? AND oi.trans_id = o.id
182 push @values, like($form->{"projectnumber"}), like($form->{"projectnumber"});
185 if ($form->{"business_id"}) {
186 $query .= " AND ct.business_id = ?";
187 push(@values, $form->{"business_id"});
190 if ($form->{"${vc}_id"}) {
191 $query .= " AND o.${vc}_id = ?";
192 push(@values, $form->{"${vc}_id"});
194 } elsif ($form->{$vc}) {
195 $query .= " AND ct.name ILIKE ?";
196 push(@values, like($form->{$vc}));
199 if ($form->{"cp_name"}) {
200 $query .= " AND (cp.cp_name ILIKE ? OR cp.cp_givenname ILIKE ?)";
201 push(@values, (like($form->{"cp_name"}))x2);
204 if ( !( ($vc eq 'customer' && ($main::auth->assert('sales_all_edit', 1) || $main::auth->assert('sales_order_view', 1)))
205 || ($vc eq 'vendor' && ($main::auth->assert('purchase_all_edit', 1) || $main::auth->assert('purchase_order_view', 1))) ) ) {
206 $query .= " AND o.employee_id = (select id from employee where login= ?)";
207 push @values, $::myconfig{login};
209 if ($form->{employee_id}) {
210 $query .= " AND o.employee_id = ?";
211 push @values, conv_i($form->{employee_id});
214 if ($form->{salesman_id}) {
215 $query .= " AND o.salesman_id = ?";
216 push @values, conv_i($form->{salesman_id});
219 if (!$form->{open} && !$form->{closed}) {
220 $query .= " AND o.id = 0";
221 } elsif (!($form->{open} && $form->{closed})) {
222 $query .= ($form->{open}) ? " AND o.closed = '0'" : " AND o.closed = '1'";
225 if (($form->{"notdelivered"} || $form->{"delivered"}) &&
226 ($form->{"notdelivered"} ne $form->{"delivered"})) {
227 $query .= $form->{"delivered"} ?
228 " AND o.delivered " : " AND NOT o.delivered";
231 if ($form->{$ordnumber}) {
232 $query .= qq| AND o.$ordnumber ILIKE ?|;
233 push(@values, like($form->{$ordnumber}));
236 if ($form->{cusordnumber}) {
237 $query .= qq| AND o.cusordnumber ILIKE ?|;
238 push(@values, like($form->{cusordnumber}));
241 if($form->{transdatefrom}) {
242 $query .= qq| AND o.transdate >= ?|;
243 push(@values, conv_date($form->{transdatefrom}));
246 if($form->{transdateto}) {
247 $query .= qq| AND o.transdate <= ?|;
248 push(@values, conv_date($form->{transdateto}));
251 if($form->{reqdatefrom}) {
252 $query .= qq| AND o.reqdate >= ?|;
253 push(@values, conv_date($form->{reqdatefrom}));
256 if($form->{reqdateto}) {
257 $query .= qq| AND o.reqdate <= ?|;
258 push(@values, conv_date($form->{reqdateto}));
261 if($form->{insertdatefrom}) {
262 $query .= qq| AND o.itime::DATE >= ?|;
263 push(@values, conv_date($form->{insertdatefrom}));
266 if($form->{insertdateto}) {
267 $query .= qq| AND o.itime::DATE <= ?|;
268 push(@values, conv_date($form->{insertdateto}));
271 if ($form->{shippingpoint}) {
272 $query .= qq| AND o.shippingpoint ILIKE ?|;
273 push(@values, like($form->{shippingpoint}));
276 if ($form->{taxzone_id} ne '') { # taxzone_id could be 0
277 $query .= qq| AND tz.id = ?|;
278 push(@values, $form->{taxzone_id});
281 if ($form->{transaction_description}) {
282 $query .= qq| AND o.transaction_description ILIKE ?|;
283 push(@values, like($form->{transaction_description}));
286 if ($form->{periodic_invoices_active} ne $form->{periodic_invoices_inactive}) {
287 my $not = $form->{periodic_invoices_inactive} ? 'NOT' : '';
288 $query .= qq| AND ${not} COALESCE(pcfg.active, 'f')|;
291 if ($form->{reqdate_unset_or_old}) {
292 $query .= qq| AND ((o.reqdate IS NULL) OR (o.reqdate < date_trunc('month', current_date)))|;
295 if (($form->{order_probability_value} || '') ne '') {
296 my $op = $form->{order_probability_value} eq 'le' ? '<=' : '>=';
297 $query .= qq| AND (o.order_probability ${op} ?)|;
298 push @values, trim($form->{order_probability_value});
301 if ($form->{expected_billing_date_from}) {
302 $query .= qq| AND (o.expected_billing_date >= ?)|;
303 push @values, conv_date($form->{expected_billing_date_from});
306 if ($form->{expected_billing_date_to}) {
307 $query .= qq| AND (o.expected_billing_date <= ?)|;
308 push @values, conv_date($form->{expected_billing_date_to});
311 if ($form->{intnotes}) {
312 $query .= qq| AND o.intnotes ILIKE ?|;
313 push(@values, like($form->{intnotes}));
316 if ($form->{phone_notes}) {
317 $query .= qq| AND (phone_notes.subject ILIKE ? OR phone_notes.body ILIKE ?)|;
318 push(@values, like($form->{phone_notes}), like($form->{phone_notes}));
321 if ($form->{parts_partnumber}) {
324 SELECT orderitems.trans_id
326 LEFT JOIN parts ON (orderitems.parts_id = parts.id)
327 WHERE (orderitems.trans_id = o.id)
328 AND (parts.partnumber ILIKE ?)
332 push @values, like($form->{parts_partnumber});
335 if ($form->{parts_description}) {
338 SELECT orderitems.trans_id
340 WHERE (orderitems.trans_id = o.id)
341 AND (orderitems.description ILIKE ?)
345 push @values, like($form->{parts_description});
349 my @tokens = parse_line('\s+', 0, $form->{all});
350 # ordnumber quonumber customer.name vendor.name transaction_description
352 o.ordnumber ILIKE ? OR
353 o.quonumber ILIKE ? OR
355 o.transaction_description ILIKE ?
357 push @values, (like($_))x4 for @tokens;
360 my ($cvar_where, @cvar_values) = CVar->build_filter_query('module' => 'CT',
361 'trans_id_field' => 'ct.id',
365 $query .= qq| AND ($cvar_where)|;
366 push @values, @cvar_values;
369 my $sortdir = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
370 my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name"), "o.itime"));
371 my %allowed_sort_columns = (
372 "transdate" => "o.transdate",
373 "reqdate" => "o.reqdate",
375 "ordnumber" => "o.ordnumber",
376 "cusordnumber" => "o.cusordnumber",
377 "quonumber" => "o.quonumber",
379 "employee" => "e.name",
380 "salesman" => "s.name",
381 "shipvia" => "o.shipvia",
382 "transaction_description" => "o.transaction_description",
383 "shippingpoint" => "o.shippingpoint",
384 "insertdate" => "o.itime",
385 "taxzone" => "tz.description",
386 "payment_terms" => "pt.description",
387 "department" => "department.description",
388 "intnotes" => "o.intnotes",
390 if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
391 $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}" . ", o.itime ${sortdir}";
393 $query .= qq| ORDER by | . $sortorder;
395 my $sth = $dbh->prepare($query);
396 $sth->execute(@values) ||
397 $form->dberror($query . " (" . join(", ", @values) . ")");
401 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
402 $ref->{billed_amount} = $billed_amount{$ref->{id}};
403 $ref->{billed_netamount} = $billed_netamount{$ref->{id}};
404 if ($ref->{billed_amount} < 0) { # case: credit note(s) higher than invoices
405 $ref->{remaining_amount} = $ref->{amount} + $ref->{billed_amount};
406 $ref->{remaining_netamount} = $ref->{netamount} + $ref->{billed_netamount};
408 $ref->{remaining_amount} = $ref->{amount} - $ref->{billed_amount};
409 $ref->{remaining_netamount} = $ref->{netamount} - $ref->{billed_netamount};
411 $ref->{exchangerate} ||= $ref->{daily_exchangerate};
412 $ref->{exchangerate} ||= 1;
413 push @{ $form->{OE} }, $ref if $ref->{id} != $id{ $ref->{id} };
414 $id{ $ref->{id} } = $ref->{id};
419 $main::lxdebug->leave_sub();
422 sub transactions_for_todo_list {
423 $main::lxdebug->enter_sub();
428 my $myconfig = \%main::myconfig;
429 my $form = $main::form;
431 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
433 my $query = qq|SELECT id FROM employee WHERE login = ?|;
434 my ($e_id) = selectrow_query($form, $dbh, $query, $::myconfig{login});
437 qq|SELECT oe.id, oe.transdate, oe.reqdate, oe.quonumber, oe.transaction_description, oe.amount,
438 CASE WHEN (COALESCE(oe.customer_id, 0) = 0) THEN 'vendor' ELSE 'customer' END AS vc,
443 LEFT JOIN customer c ON (oe.customer_id = c.id)
444 LEFT JOIN vendor v ON (oe.vendor_id = v.id)
445 LEFT JOIN employee e ON (oe.employee_id = e.id)
446 WHERE (COALESCE(quotation, FALSE) = TRUE)
447 AND (COALESCE(closed, FALSE) = FALSE)
448 AND ((oe.employee_id = ?) OR (oe.salesman_id = ?))
449 AND NOT (oe.reqdate ISNULL)
450 AND (oe.reqdate < current_date)
453 my $quotations = selectall_hashref_query($form, $dbh, $query, $e_id, $e_id);
455 $main::lxdebug->leave_sub();
461 my ($self, $myconfig, $form) = @_;
462 $main::lxdebug->enter_sub();
464 my $rc = SL::DB->client->with_transaction(\&_save, $self, $myconfig, $form);
466 $::lxdebug->leave_sub;
472 $main::lxdebug->enter_sub();
474 my ($self, $myconfig, $form) = @_;
476 my $dbh = SL::DB->client->dbh;
477 my $restricter = SL::HTML::Restrict->create;
479 my ($query, @values, $sth, $null);
480 my $exchangerate = 0;
482 my $all_units = AM->retrieve_units($myconfig, $form);
483 $form->{all_units} = $all_units;
485 my $ic_cvar_configs = CVar->get_configs(module => 'IC',
488 $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id};
489 unless ($form->{employee_id}) {
490 $form->get_employee($dbh);
493 my $ml = ($form->{type} eq 'sales_order') ? 1 : -1;
495 my $number_field = $form->{type} =~ m{order} ? 'ordnumber' : 'quonumber';
496 my $trans_number = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{$number_field}, id => $form->{id});
497 $form->{$number_field} ||= $trans_number->create_unique; # set $form->{ordnumber} or $form->{quonumber}
498 my $is_new = !$form->{id};
501 $query = qq|DELETE FROM custom_variables
502 WHERE (config_id IN (SELECT id FROM custom_variable_configs WHERE (module = 'ShipTo')))
503 AND (trans_id IN (SELECT shipto_id FROM shipto WHERE (module = 'OE') AND (trans_id = ?)))|;
504 do_query($form, $dbh, $query, $form->{id});
506 $query = qq|DELETE FROM shipto | .
507 qq|WHERE trans_id = ? AND module = 'OE'|;
508 do_query($form, $dbh, $query, $form->{id});
512 $query = qq|SELECT nextval('id')|;
513 ($form->{id}) = selectrow_query($form, $dbh, $query);
515 $query = qq|INSERT INTO oe (id, ordnumber, employee_id, currency_id, taxzone_id) VALUES (?, '', ?, (SELECT currency_id FROM defaults), ?)|;
516 do_query($form, $dbh, $query, $form->{id}, $form->{employee_id}, $form->{taxzone_id});
533 my @processed_orderitems;
535 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
536 my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
539 for my $i (1 .. $form->{rowcount}) {
541 map({ $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) } qw(qty ship));
543 if ($form->{"id_$i"}) {
546 $query = qq|SELECT unit FROM parts WHERE id = ?|;
547 my ($item_unit) = selectrow_query($form, $dbh, $query, $form->{"id_$i"});
550 if (defined($all_units->{$item_unit}->{factor}) &&
551 (($all_units->{$item_unit}->{factor} * 1) != 0)) {
552 $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
554 my $baseqty = $form->{"qty_$i"} * $basefactor;
556 $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
557 $form->{"marge_absolut_$i"} = $form->parse_amount($myconfig, $form->{"marge_absolut_$i"}) * 1;
559 $form->{"lastcost_$i"} = $form->parse_amount($myconfig, $form->{"lastcost_$i"});
561 # keep entered selling price
563 $form->parse_amount($myconfig, $form->{"sellprice_$i"});
565 my ($dec) = ($fxsellprice =~ /\.(\d+)/);
567 my $decimalplaces = ($dec > 2) ? $dec : 2;
569 # undo discount formatting
570 $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
573 $form->{"sellprice_$i"} = $fxsellprice * (1 - $form->{"discount_$i"});
575 # round linetotal at least to 2 decimal places
576 $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
577 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
579 $form->{"inventory_accno_$i"} *= 1;
580 $form->{"expense_accno_$i"} *= 1;
582 @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
586 map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
588 if ($form->{taxincluded}) {
589 $taxamount = $linetotal * $taxrate / (1 + $taxrate);
590 $taxbase = $linetotal - $taxamount;
592 # we are not keeping a natural price, do not round
593 $form->{"sellprice_$i"} =
594 $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
596 $taxamount = $linetotal * $taxrate;
597 $taxbase = $linetotal;
600 if ($form->round_amount($taxrate, 7) == 0) {
601 if ($form->{taxincluded}) {
602 foreach my $item (@taxaccounts) {
603 $taxamount = $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
604 $taxaccounts{$item} += $taxamount;
605 $taxdiff += $taxamount;
606 $taxbase{$item} += $taxbase;
608 $taxaccounts{ $taxaccounts[0] } += $taxdiff;
610 foreach my $item (@taxaccounts) {
611 $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
612 $taxbase{$item} += $taxbase;
616 foreach my $item (@taxaccounts) {
617 $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
618 $taxbase{$item} += $taxbase;
622 $netamount += $form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor;
624 $reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef;
626 # Get pricegroup_id and save it. Unfortunately the interface
627 # also uses ID "0" for signalling that none is selected, but "0"
628 # must not be stored in the database. Therefore we cannot simply
630 ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
632 $pricegroup_id = undef if !$pricegroup_id;
634 # force new project, if not set yet
635 if ($::instance_conf->get_order_always_project && !$form->{"globalproject_id"} && ($form->{type} eq 'sales_order')) {
636 require SL::DB::Customer;
637 my $customer = SL::DB::Manager::Customer->find_by(id => $form->{customer_id});
638 die "Can't find customer" unless $customer;
639 die $main::locale->text("Error while creating project with project number of new order number, project number #1 already exists!", $form->{ordnumber})
640 if SL::DB::Manager::Project->find_by(projectnumber => $form->{ordnumber});
642 my $new_project = SL::DB::Project->new(
643 projectnumber => $form->{ordnumber},
644 description => $customer->name,
645 customer_id => $customer->id,
647 project_type_id => $::instance_conf->get_project_type_id,
648 project_status_id => $::instance_conf->get_project_status_id,
651 $form->{"globalproject_id"} = $new_project->id;
654 CVar->get_non_editable_ic_cvars(form => $form,
657 sub_module => 'orderitems',
658 may_converted_from => ['orderitems', 'invoice']);
662 # save detail record in orderitems table
663 if (! $form->{"orderitems_id_$i"}) {
664 $query = qq|SELECT nextval('orderitemsid')|;
665 ($form->{"orderitems_id_$i"}) = selectrow_query($form, $dbh, $query);
667 $query = qq|INSERT INTO orderitems (id, position) VALUES (?, ?)|;
668 do_query($form, $dbh, $query, $form->{"orderitems_id_$i"}, conv_i($position));
671 my $orderitems_id = $form->{"orderitems_id_$i"};
672 push @processed_orderitems, $orderitems_id;
675 UPDATE orderitems SET
676 trans_id = ?, position = ?, parts_id = ?, description = ?, longdescription = ?, qty = ?, base_qty = ?,
677 sellprice = ?, discount = ?, unit = ?, reqdate = ?, project_id = ?, serialnumber = ?, ship = ?,
678 pricegroup_id = ?, subtotal = ?,
679 marge_percent = ?, marge_total = ?, lastcost = ?, price_factor_id = ?,
680 active_price_source = ?, active_discount_source = ?,
681 price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?
685 conv_i($form->{id}), conv_i($position), conv_i($form->{"id_$i"}),
686 $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}),
687 $form->{"qty_$i"}, $baseqty,
688 $fxsellprice, $form->{"discount_$i"},
689 $form->{"unit_$i"}, conv_date($reqdate), conv_i($form->{"project_id_$i"}),
690 $form->{"serialnumber_$i"}, $form->{"ship_$i"},
691 $pricegroup_id, $form->{"subtotal_$i"} ? 't' : 'f',
692 $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
693 $form->{"lastcost_$i"}, conv_i($form->{"price_factor_id_$i"}),
694 $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"},
695 conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}),
696 conv_i($orderitems_id),
699 do_query($form, $dbh, $query, @values);
701 $form->{"sellprice_$i"} = $fxsellprice;
702 $form->{"discount_$i"} *= 100;
704 CVar->save_custom_variables(module => 'IC',
705 sub_module => 'orderitems',
706 trans_id => $orderitems_id,
707 configs => $ic_cvar_configs,
709 name_prefix => 'ic_',
710 name_postfix => "_$i",
713 # link previous items with orderitems
714 # assume we have a new workflow if we link from invoice or order to quotation
715 # unluckily orderitems are used for quotation and orders - therefore one more
716 # check to be sure NOT to link from order to quotation
717 foreach (qw(orderitems)) {
718 if (!$form->{saveasnew} && !$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}
719 && $form->{type} !~ 'quotation') {
720 RecordLinks->create_links('dbh' => $dbh,
723 'from_ids' => $form->{"converted_from_${_}_id_$i"},
724 'to_table' => 'orderitems',
725 'to_id' => $orderitems_id,
728 delete $form->{"converted_from_${_}_id_$i"};
733 # search for orphaned ids
734 $query = sprintf 'SELECT id FROM orderitems WHERE trans_id = ? AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_orderitems;
735 @values = (conv_i($form->{id}), map { conv_i($_) } @processed_orderitems);
736 my @orphaned_ids = map { $_->{id} } selectall_hashref_query($form, $dbh, $query, @values);
738 if (scalar @orphaned_ids) {
739 # clean up orderitems
740 $query = sprintf 'DELETE FROM orderitems WHERE id IN (%s)', join ', ', ("?") x scalar @orphaned_ids;
741 do_query($form, $dbh, $query, @orphaned_ids);
744 $reqdate = ($form->{reqdate}) ? $form->{reqdate} : undef;
748 map { $tax += $form->round_amount($taxaccounts{$_}, 2) } keys %taxaccounts;
750 $amount = $form->round_amount($netamount + $tax, 2, 1);
751 $netamount = $form->round_amount($netamount, 2);
753 if ($form->{currency} eq $form->{defaultcurrency}) {
754 $form->{exchangerate} = 1;
756 $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? 'buy' : 'sell');
759 # from inputfield (exchangerate) or hidden (forex)
760 my $exchangerate_from_form = $form->{forex} || $form->parse_amount($myconfig, $form->{exchangerate});
762 $form->{exchangerate} = $exchangerate || $exchangerate_from_form;
764 my $quotation = $form->{type} =~ /_order$/ ? 'f' : 't';
769 ordnumber = ?, quonumber = ?, cusordnumber = ?, transdate = ?, vendor_id = ?,
770 customer_id = ?, amount = ?, netamount = ?, reqdate = ?, tax_point = ?, taxincluded = ?,
771 shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, currency_id = (SELECT id FROM currencies WHERE name=?), closed = ?,
772 delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
773 taxzone_id = ?, shipto_id = ?, billing_address_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,delivery_term_id = ?,
774 globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
775 , order_probability = ?, expected_billing_date = ?
778 @values = ($form->{ordnumber} || '', $form->{quonumber},
779 $form->{cusordnumber}, conv_date($form->{transdate}),
780 conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
781 $amount, $netamount, conv_date($reqdate), conv_date($form->{tax_point}),
782 $form->{taxincluded} ? 't' : 'f', $form->{shippingpoint},
783 $form->{shipvia}, $restricter->process($form->{notes}), $form->{intnotes},
784 $form->{currency}, $form->{closed} ? 't' : 'f',
785 $form->{delivered} ? "t" : "f", $form->{proforma} ? 't' : 'f',
786 $quotation, conv_i($form->{department_id}),
787 conv_i($form->{language_id}), conv_i($form->{taxzone_id}),
788 conv_i($form->{shipto_id}), conv_i($form->{billing_address_id}), conv_i($form->{payment_id}),
789 conv_i($form->{delivery_vendor_id}),
790 conv_i($form->{delivery_customer_id}),
791 conv_i($form->{delivery_term_id}),
792 conv_i($form->{globalproject_id}), conv_i($form->{employee_id}),
793 conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
794 $form->{transaction_description},
795 $form->{marge_total} * 1, $form->{marge_percent} * 1,
796 $form->{order_probability} * 1, conv_date($form->{expected_billing_date}),
797 conv_i($form->{id}));
798 do_query($form, $dbh, $query, @values);
800 $form->new_lastmtime('oe');
802 $form->{ordtotal} = $amount;
804 $form->{name} = $form->{ $form->{vc} };
805 $form->{name} =~ s/--\Q$form->{"$form->{vc}_id"}\E//;
808 if (!$form->{shipto_id}) {
809 $form->add_shipto($dbh, $form->{id}, "OE");
812 # save printed, emailed, queued
813 $form->save_status($dbh);
815 # Link this record to the records it was created from.
816 $form->{convert_from_oe_ids} =~ s/^\s+//;
817 $form->{convert_from_oe_ids} =~ s/\s+$//;
818 my @convert_from_oe_ids = split m/\s+/, $form->{convert_from_oe_ids};
819 delete $form->{convert_from_oe_ids};
820 if (!$form->{useasnew} && scalar @convert_from_oe_ids) {
821 RecordLinks->create_links('dbh' => $dbh,
823 'from_table' => 'oe',
824 'from_ids' => \@convert_from_oe_ids,
826 'to_id' => $form->{id},
828 $self->_close_quotations_rfqs('dbh' => $dbh,
829 'from_id' => \@convert_from_oe_ids,
830 'to_id' => $form->{id});
833 if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
834 if ($form->{vc} eq 'customer') {
835 $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
837 if ($form->{vc} eq 'vendor') {
838 $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
842 $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ?
843 "quonumber" : "ordnumber"};
845 Common::webdav_folder($form);
847 $self->save_periodic_invoices_config(dbh => $dbh,
848 oe_id => $form->{id},
849 config_yaml => $form->{periodic_invoices_config})
850 if ($form->{type} eq 'sales_order');
852 $self->_link_created_sales_order_to_requirement_specs_for_sales_quotations(
853 type => $form->{type},
854 converted_from_ids => \@convert_from_oe_ids,
855 sales_order_id => $form->{id},
859 $self->_set_project_in_linked_requirement_spec(
860 type => $form->{type},
861 project_id => $form->{globalproject_id},
862 sales_order_id => $form->{id},
865 $main::lxdebug->leave_sub();
870 sub _link_created_sales_order_to_requirement_specs_for_sales_quotations {
871 my ($self, %params) = @_;
873 # If this is a sales order created from a sales quotation and if
874 # that sales quotation was created from a requirement spec document
875 # then link the newly created sales order to the requirement spec
878 return if !$params{is_new};
879 return if $params{type} ne 'sales_order';
880 return if !@{ $params{converted_from_ids} };
882 my $oe_objects = SL::DB::Manager::Order->get_all(where => [ id => $params{converted_from_ids} ]);
883 my @sales_quotations = grep { $_->is_type('sales_quotation') } @{ $oe_objects };
885 return if !@sales_quotations;
887 my $rs_orders = SL::DB::Manager::RequirementSpecOrder->get_all(where => [ order_id => [ map { $_->id } @sales_quotations ] ]);
889 return if !@{ $rs_orders };
891 $rs_orders->[0]->db->with_transaction(sub {
892 foreach my $rs_order (@{ $rs_orders }) {
893 SL::DB::RequirementSpecOrder->new(
894 order_id => $params{sales_order_id},
895 requirement_spec_id => $rs_order->requirement_spec_id,
896 version_id => $rs_order->version_id,
904 sub _set_project_in_linked_requirement_spec {
905 my ($self, %params) = @_;
907 return if $params{type} ne 'sales_order';
908 return if !$params{project_id} || !$params{sales_order_id};
911 UPDATE requirement_specs
914 SELECT so.requirement_spec_id
915 FROM requirement_spec_orders so
916 WHERE so.order_id = ?
920 do_query($::form, $::form->get_standard_dbh, $query, $params{project_id}, $params{sales_order_id});
923 sub save_periodic_invoices_config {
924 my ($self, %params) = @_;
926 return if !$params{oe_id};
928 my $config = $params{config_yaml} ? SL::YAML::Load($params{config_yaml}) : undef;
929 return if 'HASH' ne ref $config;
931 my $obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $params{oe_id})
932 || SL::DB::PeriodicInvoicesConfig->new(oe_id => $params{oe_id});
933 $obj->update_attributes(%{ $config });
936 sub load_periodic_invoice_config {
940 delete $form->{periodic_invoices_config};
943 my $config_obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $form->{id});
946 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
947 print printer_id copies direct_debit send_email email_recipient_contact_id email_recipient_address email_sender email_subject email_body) };
948 $form->{periodic_invoices_config} = SL::YAML::Dump($config);
953 sub _close_quotations_rfqs {
954 $main::lxdebug->enter_sub();
959 Common::check_params(\%params, qw(from_id to_id));
961 my $myconfig = \%main::myconfig;
962 my $form = $main::form;
964 my $dbh = $params{dbh} || SL::DB->client->dbh;
966 SL::DB->client->with_transaction(sub {
968 my $query = qq|SELECT quotation FROM oe WHERE id = ?|;
969 my $sth = prepare_query($form, $dbh, $query);
971 do_statement($form, $sth, $query, conv_i($params{to_id}));
973 my ($quotation) = $sth->fetchrow_array();
981 foreach my $from_id (@{ $params{from_id} }) {
982 $from_id = conv_i($from_id);
983 do_statement($form, $sth, $query, $from_id);
984 ($quotation) = $sth->fetchrow_array();
985 push @close_ids, $from_id if ($quotation);
990 if (scalar @close_ids) {
991 $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_ids) . qq|)|;
992 do_query($form, $dbh, $query, @close_ids);
995 }) or do { die SL::DB->client->error };
997 $main::lxdebug->leave_sub();
1001 $main::lxdebug->enter_sub();
1003 my ($self, $myconfig, $form) = @_;
1005 my $rc = SL::DB::Order->new->db->with_transaction(sub {
1006 my @spoolfiles = grep { $_ } map { $_->spoolfile } @{ SL::DB::Manager::Status->get_all(where => [ trans_id => $form->{id} ]) };
1008 SL::DB::Order->new(id => $form->{id})->delete;
1010 my $spool = $::lx_office_conf{paths}->{spool};
1011 unlink map { "$spool/$_" } @spoolfiles if $spool;
1014 }) or do { die SL::DB->client->error };
1016 $main::lxdebug->leave_sub();
1022 my ($self, $myconfig, $form) = @_;
1023 $main::lxdebug->enter_sub();
1025 my $rc = SL::DB->client->with_transaction(\&_retrieve, $self, $myconfig, $form);
1027 $::lxdebug->leave_sub;
1032 my ($self, $myconfig, $form) = @_;
1034 # connect to database
1035 my $dbh = SL::DB->client->dbh;
1037 my ($query, $query_add, @values, @ids, $sth);
1039 # translate the ids (given by id_# and trans_id_#) into one array of ids, so we can join them later
1041 push @ids, $form->{"trans_id_$_"}
1042 if ($form->{"multi_id_$_"} and $form->{"trans_id_$_"})
1043 } (1 .. $form->{"rowcount"});
1045 if ($form->{rowcount} && scalar @ids) {
1046 $form->{convert_from_oe_ids} = join ' ', @ids;
1049 # if called in multi id mode, and still only got one id, switch back to single id
1050 if ($form->{"rowcount"} and $#ids == 0) {
1051 $form->{"id"} = $ids[0];
1053 delete $form->{convert_from_oe_ids};
1056 # and remember for the rest of the function
1057 my $is_collective_order = scalar @ids;
1059 # If collective order was created from exactly 1 order, we assume the same
1060 # behaviour as a "save as new" from within an order is actually desired, i.e.
1061 # the original order isn't part of a workflow where we want to remember
1062 # record_links, but simply a quick way of generating a new order from an old
1063 # one without having to enter everything again.
1064 # Setting useasnew will prevent the creation of record_links for the items
1065 # when saving the new order.
1066 # This form variable is probably not necessary, could just set saveasnew instead
1067 $form->{useasnew} = 1 if $is_collective_order == 1;
1070 my $extra_days = $form->{type} eq 'sales_quotation' ? $::instance_conf->get_reqdate_interval :
1071 $form->{type} eq 'sales_order' ? $::instance_conf->get_delivery_date_interval : 1;
1072 if ( ($form->{type} eq 'sales_order' && !$::instance_conf->get_deliverydate_on)
1073 || ($form->{type} eq 'sales_quotation' && !$::instance_conf->get_reqdate_on)) {
1074 $form->{reqdate} = '';
1076 $form->{reqdate} = DateTime->today_local->next_workday(extra_days => $extra_days)->to_kivitendo;
1078 $form->{transdate} = DateTime->today_local->to_kivitendo;
1081 # get default accounts
1082 $query = qq|SELECT (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
1083 (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id) AS income_accno,
1084 (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id) AS expense_accno,
1085 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
1086 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
1087 (SELECT c.accno FROM chart c WHERE d.rndgain_accno_id = c.id) AS rndgain_accno,
1088 (SELECT c.accno FROM chart c WHERE d.rndloss_accno_id = c.id) AS rndloss_accno
1091 my $ref = selectfirst_hashref_query($form, $dbh, $query);
1092 map { $form->{$_} = $ref->{$_} } keys %$ref;
1094 $form->{currency} = $form->get_default_currency($myconfig);
1096 # set reqdate if this is an invoice->order conversion. If someone knows a better check to ensure
1097 # we come from invoices, feel free.
1098 $form->{reqdate} = $form->{deliverydate}
1099 if ( $form->{deliverydate}
1100 and $form->{callback} =~ /action=ar_transactions/);
1102 my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
1104 if ($form->{id} or @ids) {
1106 # retrieve order for single id
1107 # NOTE: this query is intended to fetch all information only ONCE.
1108 # so if any of these infos is important (or even different) for any item,
1109 # it will be killed out and then has to be fetched from the item scope query further down
1111 qq|SELECT o.cp_id, o.ordnumber, o.transdate, o.reqdate,
1112 o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
1113 (SELECT cu.name FROM currencies cu WHERE cu.id=o.currency_id) AS currency, e.name AS employee, o.employee_id, o.salesman_id,
1114 o.${vc}_id, cv.name AS ${vc}, o.amount AS invtotal,
1115 o.closed, o.reqdate, o.tax_point, o.quonumber, o.department_id, o.cusordnumber,
1117 d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
1118 o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id, o.billing_address_id,
1119 o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id,
1120 o.itime::DATE AS insertdate, o.order_probability, o.expected_billing_date
1122 JOIN ${vc} cv ON (o.${vc}_id = cv.id)
1123 LEFT JOIN employee e ON (o.employee_id = e.id)
1124 LEFT JOIN department d ON (o.department_id = d.id) | .
1127 : "WHERE o.id IN (" . join(', ', map("? ", @ids)) . ")"
1129 @values = $form->{id} ? ($form->{id}) : @ids;
1130 $sth = prepare_execute_query($form, $dbh, $query, @values);
1132 $ref = $sth->fetchrow_hashref("NAME_lc");
1135 map { $form->{$_} = $ref->{$_} } keys %$ref;
1137 $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ? "quonumber" : "ordnumber"};
1139 # set all entries for multiple ids blank that yield different information
1140 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1141 map { $form->{$_} = '' if ($ref->{$_} ne $form->{$_}) } keys %$ref;
1144 $form->{mtime} ||= $form->{itime};
1145 $form->{lastmtime} = $form->{mtime};
1147 # if not given, fill transdate with current_date
1148 $form->{transdate} = $form->current_date($myconfig)
1149 unless $form->{transdate};
1153 if ($form->{delivery_customer_id}) {
1154 $query = qq|SELECT name FROM customer WHERE id = ?|;
1155 ($form->{delivery_customer_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_customer_id});
1158 if ($form->{delivery_vendor_id}) {
1159 $query = qq|SELECT name FROM customer WHERE id = ?|;
1160 ($form->{delivery_vendor_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_vendor_id});
1163 # shipto and pinted/mailed/queued status makes only sense for single id retrieve
1165 $query = qq|SELECT s.* FROM shipto s WHERE s.trans_id = ? AND s.module = 'OE'|;
1166 $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
1168 $ref = $sth->fetchrow_hashref("NAME_lc");
1169 $form->{$_} = $ref->{$_} for grep { m{^shipto(?!_id$)} } keys %$ref;
1172 if ($ref->{shipto_id}) {
1173 my $cvars = CVar->get_custom_variables(
1176 trans_id => $ref->{shipto_id},
1178 $form->{"shiptocvar_$_->{name}"} = $_->{value} for @{ $cvars };
1181 # get printed, emailed and queued
1182 $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
1183 $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
1185 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1186 $form->{printed} .= "$ref->{formname} " if $ref->{printed};
1187 $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
1188 $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
1191 map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
1194 my $transdate = $form->{tax_point} ? $dbh->quote($form->{tax_point}) : $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
1196 $form->{taxzone_id} = 0 unless ($form->{taxzone_id});
1197 unshift @values, ($form->{taxzone_id}) x 2;
1199 # retrieve individual items
1200 # this query looks up all information about the items
1201 # stuff different from the whole will not be overwritten, but saved with a suffix.
1203 qq|SELECT o.id AS orderitems_id,
1204 c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from as inventory_valid,
1205 c2.accno AS income_accno, c2.new_chart_id AS income_new_chart, date($transdate) - c2.valid_from as income_valid,
1206 c3.accno AS expense_accno, c3.new_chart_id AS expense_new_chart, date($transdate) - c3.valid_from as expense_valid,
1207 oe.ordnumber AS ordnumber_oe, oe.transdate AS transdate_oe, oe.cusordnumber AS cusordnumber_oe,
1208 p.partnumber, p.part_type, p.listprice, o.description, o.qty,
1209 p.classification_id,
1210 o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.part_type,
1211 o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
1212 o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
1213 o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source, o.active_discount_source,
1214 pr.projectnumber, p.formel,
1215 pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
1217 JOIN parts p ON (o.parts_id = p.id)
1218 JOIN oe ON (o.trans_id = oe.id)
1219 LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c1.id)
1220 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)
1221 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)
1222 LEFT JOIN project pr ON (o.project_id = pr.id)
1223 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
1225 ? qq|WHERE o.trans_id = ?|
1226 : qq|WHERE o.trans_id IN (| . join(", ", map("?", @ids)) . qq|)|) .
1227 qq|ORDER BY o.trans_id, o.position|;
1229 @ids = $form->{id} ? ($form->{id}) : @ids;
1230 $sth = prepare_execute_query($form, $dbh, $query, @values);
1232 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1233 # Retrieve custom variables.
1234 my $cvars = CVar->get_custom_variables(dbh => $dbh,
1236 sub_module => 'orderitems',
1237 trans_id => $ref->{orderitems_id},
1239 map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
1242 if (!$ref->{"part_type"} eq 'part') {
1243 map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid));
1245 # delete($ref->{"part_inventory_accno_id"});
1247 # in collective order, copy global ordnumber, transdate, cusordnumber into item scope
1248 # unless already present there
1249 # remove _oe entries afterwards
1250 map { $ref->{$_} = $ref->{"${_}_oe"} if ($ref->{$_} eq '') }
1251 qw|ordnumber transdate cusordnumber|
1253 map { delete $ref->{$_} } qw|ordnumber_oe transdate_oe cusordnumber_oe|;
1257 while ($ref->{inventory_new_chart} && ($ref->{inventory_valid} >= 0)) {
1259 qq|SELECT accno AS inventory_accno, | .
1260 qq| new_chart_id AS inventory_new_chart, | .
1261 qq| date($transdate) - valid_from AS inventory_valid | .
1262 qq|FROM chart WHERE id = $ref->{inventory_new_chart}|;
1263 ($ref->{inventory_accno}, $ref->{inventory_new_chart},
1264 $ref->{inventory_valid}) = selectrow_query($form, $dbh, $query);
1267 while ($ref->{income_new_chart} && ($ref->{income_valid} >= 0)) {
1269 qq|SELECT accno AS income_accno, | .
1270 qq| new_chart_id AS income_new_chart, | .
1271 qq| date($transdate) - valid_from AS income_valid | .
1272 qq|FROM chart WHERE id = $ref->{income_new_chart}|;
1273 ($ref->{income_accno}, $ref->{income_new_chart},
1274 $ref->{income_valid}) = selectrow_query($form, $dbh, $query);
1277 while ($ref->{expense_new_chart} && ($ref->{expense_valid} >= 0)) {
1279 qq|SELECT accno AS expense_accno, | .
1280 qq| new_chart_id AS expense_new_chart, | .
1281 qq| date($transdate) - valid_from AS expense_valid | .
1282 qq|FROM chart WHERE id = $ref->{expense_new_chart}|;
1283 ($ref->{expense_accno}, $ref->{expense_new_chart},
1284 $ref->{expense_valid}) = selectrow_query($form, $dbh, $query);
1287 # delete orderitems_id in collective orders, so that they get cloned no matter what
1288 # is this correct? or is the following meant?
1289 # remember orderitems_ids in converted_from_orderitems_ids, so that they may be linked
1290 $ref->{converted_from_orderitems_id} = delete $ref->{orderitems_id} if $is_collective_order;
1292 # get tax rates and description
1293 my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1295 qq|SELECT c.accno, t.taxdescription, t.rate, t.id as tax_id, c.accno as taxnumber | .
1297 qq|LEFT JOIN chart c on (c.id = t.chart_id) | .
1298 qq|WHERE t.id IN (SELECT tk.tax_id FROM taxkeys tk | .
1299 qq| WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?) | .
1300 qq| AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1) | .
1301 qq|ORDER BY c.accno|;
1302 my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
1303 $ref->{taxaccounts} = "";
1305 while (my $ptr = $stw->fetchrow_hashref("NAME_lc")) {
1306 if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1310 $ref->{taxaccounts} .= "$ptr->{accno} ";
1311 if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
1312 $form->{"$ptr->{accno}_rate"} = $ptr->{rate};
1313 $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1314 $form->{"$ptr->{accno}_taxnumber"} = $ptr->{taxnumber};
1315 $form->{"$ptr->{accno}_tax_id"} = $ptr->{tax_id};
1316 $form->{taxaccounts} .= "$ptr->{accno} ";
1321 chop $ref->{taxaccounts};
1323 push @{ $form->{form_details} }, $ref;
1330 # get last name used
1331 $form->lastname_used($dbh, $myconfig, $form->{vc})
1332 unless $form->{"$form->{vc}_id"};
1336 $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? "buy" : "sell");
1338 Common::webdav_folder($form);
1340 $self->load_periodic_invoice_config($form);
1345 sub retrieve_simple {
1346 $main::lxdebug->enter_sub();
1351 Common::check_params(\%params, qw(id));
1353 my $myconfig = \%main::myconfig;
1354 my $form = $main::form;
1356 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
1358 my $oe_query = qq|SELECT * FROM oe WHERE id = ?|;
1359 my $oi_query = qq|SELECT * FROM orderitems WHERE trans_id = ? ORDER BY position|;
1361 my $order = selectfirst_hashref_query($form, $dbh, $oe_query, conv_i($params{id}));
1362 $order->{orderitems} = selectall_hashref_query( $form, $dbh, $oi_query, conv_i($params{id}));
1364 $main::lxdebug->leave_sub();
1370 $main::lxdebug->enter_sub();
1372 my ($self, $myconfig, $form) = @_;
1374 # connect to database
1375 my $dbh = SL::DB->client->dbh;
1381 my $nodiscount_subtotal = 0;
1382 my $discount_subtotal = 0;
1385 my @partsgroup = ();
1388 my $subtotal_header = 0;
1389 my $subposition = 0;
1397 push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
1399 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
1402 foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
1403 $price_factors{$pfac->{id}} = $pfac;
1404 $pfac->{factor} *= 1;
1405 $pfac->{formatted_factor} = $form->format_amount($myconfig, $pfac->{factor});
1408 # sort items by partsgroup
1409 for $i (1 .. $form->{rowcount}) {
1411 if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
1412 $partsgroup = $form->{"partsgroup_$i"};
1414 push @partsgroup, [$i, $partsgroup];
1415 push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
1421 $projects = SL::DB::Manager::Project->get_all(query => [ id => \@project_ids ]);
1422 %projects_by_id = map { $_->id => $_ } @$projects;
1425 if ($projects_by_id{$form->{"globalproject_id"}}) {
1426 $form->{globalprojectnumber} = $projects_by_id{$form->{"globalproject_id"}}->projectnumber;
1427 $form->{globalprojectdescription} = $projects_by_id{$form->{"globalproject_id"}}->description;
1429 for (@{ $projects_by_id{$form->{"globalproject_id"}}->cvars_by_config }) {
1430 $form->{"project_cvar_" . $_->config->name} = $_->value_as_text;
1434 $form->{discount} = [];
1436 # get some values of parts from db on store them in extra array,
1437 # so that they can be sorted in later
1438 my %prepared_template_arrays = IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
1439 my @prepared_arrays = keys %prepared_template_arrays;
1440 my @separate_totals = qw(non_separate_subtotal);
1442 $form->{TEMPLATE_ARRAYS} = { };
1444 my $ic_cvar_configs = CVar->get_configs(module => 'IC');
1445 my $project_cvar_configs = CVar->get_configs(module => 'Projects');
1448 qw(runningnumber number description longdescription qty qty_nofmt ship ship_nofmt unit bin
1449 partnotes serialnumber reqdate sellprice sellprice_nofmt listprice listprice_nofmt netprice netprice_nofmt
1450 discount discount_nofmt p_discount discount_sub discount_sub_nofmt nodiscount_sub nodiscount_sub_nofmt
1451 linetotal linetotal_nofmt nodiscount_linetotal nodiscount_linetotal_nofmt tax_rate projectnumber projectdescription
1452 price_factor price_factor_name partsgroup weight weight_nofmt lineweight lineweight_nofmt optional);
1454 push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
1455 push @arrays, map { "project_cvar_$_->{name}" } @{ $project_cvar_configs };
1457 my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
1459 map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @prepared_arrays);
1461 my $totalweight = 0;
1463 foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
1466 if ($item->[1] ne $sameitem) {
1467 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'partsgroup');
1468 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
1469 $sameitem = $item->[1];
1471 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1474 $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
1476 if ($form->{"id_$i"} != 0) {
1478 # add number, description and qty to $form->{number}, ....
1480 if ($form->{"subtotal_$i"} && !$subtotal_header) {
1481 $subtotal_header = $i;
1482 $position = int($position);
1485 } elsif ($subtotal_header) {
1487 $position = int($position);
1488 $position = $position.".".$subposition;
1490 $position = int($position);
1494 my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
1496 push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, $prepared_template_arrays{$_}[$i - 1]) for @prepared_arrays;
1498 push @{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'normal';
1499 push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} }, $position;
1500 push @{ $form->{TEMPLATE_ARRAYS}->{number} }, $form->{"partnumber_$i"};
1501 push @{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->{"description_$i"};
1502 push @{ $form->{TEMPLATE_ARRAYS}->{longdescription} }, $form->{"longdescription_$i"};
1503 push @{ $form->{TEMPLATE_ARRAYS}->{qty} }, $form->format_amount($myconfig, $form->{"qty_$i"});
1504 push @{ $form->{TEMPLATE_ARRAYS}->{qty_nofmt} }, $form->{"qty_$i"};
1505 push @{ $form->{TEMPLATE_ARRAYS}->{ship} }, $form->format_amount($myconfig, $form->{"ship_$i"});
1506 push @{ $form->{TEMPLATE_ARRAYS}->{ship_nofmt} }, $form->{"ship_$i"};
1507 push @{ $form->{TEMPLATE_ARRAYS}->{unit} }, $form->{"unit_$i"};
1508 push @{ $form->{TEMPLATE_ARRAYS}->{bin} }, $form->{"bin_$i"};
1509 push @{ $form->{TEMPLATE_ARRAYS}->{partnotes} }, $form->{"partnotes_$i"};
1510 push @{ $form->{TEMPLATE_ARRAYS}->{serialnumber} }, $form->{"serialnumber_$i"};
1511 push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} }, $form->{"reqdate_$i"};
1512 push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} }, $form->{"sellprice_$i"};
1513 push @{ $form->{TEMPLATE_ARRAYS}->{sellprice_nofmt} }, $form->parse_amount($myconfig, $form->{"sellprice_$i"});
1514 push @{ $form->{TEMPLATE_ARRAYS}->{listprice} }, $form->format_amount($myconfig, $form->{"listprice_$i"}, 2);
1515 push @{ $form->{TEMPLATE_ARRAYS}->{listprice_nofmt} }, $form->{"listprice_$i"};
1516 push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} }, $price_factor->{formatted_factor};
1517 push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
1518 push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} }, $form->{"partsgroup_$i"};
1519 push @{ $form->{TEMPLATE_ARRAYS}->{optional} }, $form->{"optional_$i"};
1521 my $sellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
1522 my ($dec) = ($sellprice =~ /\.(\d+)/);
1523 my $decimalplaces = max 2, length($dec);
1525 my $parsed_discount = $form->parse_amount($myconfig, $form->{"discount_$i"});
1527 my $linetotal_exact = $form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor};
1528 my $linetotal = $form->round_amount($linetotal_exact, 2);
1530 my $nodiscount_exact_linetotal = $form->{"qty_$i"} * $sellprice / $price_factor->{factor};
1531 my $nodiscount_linetotal = $form->round_amount($nodiscount_exact_linetotal,2);
1533 my $discount = $nodiscount_linetotal - $linetotal; # is always rounded because $nodiscount_linetotal and $linetotal are rounded
1535 my $discount_round_error = $discount + ($linetotal_exact - $nodiscount_exact_linetotal); # not used
1537 $form->{"netprice_$i"} = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, $decimalplaces);
1539 push @{ $form->{TEMPLATE_ARRAYS}->{netprice} }, ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
1540 push @{ $form->{TEMPLATE_ARRAYS}->{netprice_nofmt} }, ($form->{"netprice_$i"} != 0) ? $form->{"netprice_$i"} : '';
1542 $linetotal = ($linetotal != 0) ? $linetotal : '';
1544 push @{ $form->{TEMPLATE_ARRAYS}->{discount} }, ($discount != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
1545 push @{ $form->{TEMPLATE_ARRAYS}->{discount_nofmt} }, ($discount != 0) ? $discount * -1 : '';
1546 push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} }, $form->{"discount_$i"};
1548 if ( $prepared_template_arrays{separate}[$i - 1] ) {
1549 my $pabbr = $prepared_template_arrays{separate}[$i - 1];
1550 if ( ! $form->{"separate_${pabbr}_subtotal"} ) {
1551 push @separate_totals , "separate_${pabbr}_subtotal";
1552 $form->{"separate_${pabbr}_subtotal"} = 0;
1554 $form->{"separate_${pabbr}_subtotal"} += $linetotal;
1556 $form->{non_separate_subtotal} += $linetotal;
1559 $form->{ordtotal} += $linetotal unless $form->{"optional_$i"};
1560 $form->{nodiscount_total} += $nodiscount_linetotal;
1561 $form->{discount_total} += $discount;
1563 if ($subtotal_header) {
1564 $discount_subtotal += $linetotal;
1565 $nodiscount_subtotal += $nodiscount_linetotal;
1568 if ($form->{"subtotal_$i"} && $subtotal_header && ($subtotal_header != $i)) {
1569 push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} }, $form->format_amount($myconfig, $discount_subtotal, 2);
1570 push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub_nofmt} }, $discount_subtotal;
1571 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} }, $form->format_amount($myconfig, $nodiscount_subtotal, 2);
1572 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub_nofmt} }, $nodiscount_subtotal;
1574 $discount_subtotal = 0;
1575 $nodiscount_subtotal = 0;
1576 $subtotal_header = 0;
1579 push @{ $form->{TEMPLATE_ARRAYS}->{$_} }, "" for qw(discount_sub nodiscount_sub discount_sub_nofmt nodiscount_sub_nofmt);
1582 if (!$form->{"discount_$i"}) {
1583 $nodiscount += $linetotal;
1586 my $project = $projects_by_id{$form->{"project_id_$i"}} || SL::DB::Project->new;
1588 push @{ $form->{TEMPLATE_ARRAYS}->{linetotal} }, $form->format_amount($myconfig, $linetotal, 2);
1589 push @{ $form->{TEMPLATE_ARRAYS}->{linetotal_nofmt} }, $linetotal_exact;
1590 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} }, $form->format_amount($myconfig, $nodiscount_linetotal, 2);
1591 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal_nofmt} }, $nodiscount_linetotal;
1592 push @{ $form->{TEMPLATE_ARRAYS}->{projectnumber} }, $project->projectnumber;
1593 push @{ $form->{TEMPLATE_ARRAYS}->{projectdescription} }, $project->description;
1595 my $lineweight = $form->{"qty_$i"} * $form->{"weight_$i"};
1596 $totalweight += $lineweight;
1597 push @{ $form->{TEMPLATE_ARRAYS}->{weight} }, $form->format_amount($myconfig, $form->{"weight_$i"}, 3);
1598 push @{ $form->{TEMPLATE_ARRAYS}->{weight_nofmt} }, $form->{"weight_$i"};
1599 push @{ $form->{TEMPLATE_ARRAYS}->{lineweight} }, $form->format_amount($myconfig, $lineweight, 3);
1600 push @{ $form->{TEMPLATE_ARRAYS}->{lineweight_nofmt} }, $lineweight;
1602 my ($taxamount, $taxbase);
1605 map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
1607 unless ($form->{"optional_$i"}) {
1608 if ($form->{taxincluded}) {
1611 $taxamount = $linetotal * $taxrate / (1 + $taxrate);
1612 $taxbase = $linetotal / (1 + $taxrate);
1614 $taxamount = $linetotal * $taxrate;
1615 $taxbase = $linetotal;
1619 if ($taxamount != 0) {
1620 foreach my $accno (split / /, $form->{"taxaccounts_$i"}) {
1621 $taxaccounts{$accno} += $taxamount * $form->{"${accno}_rate"} / $taxrate;
1622 $taxbase{$accno} += $taxbase;
1626 $tax_rate = $taxrate * 100;
1627 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
1629 if ($form->{"part_type_$i"} eq 'assembly') {
1632 # get parts and push them onto the stack
1634 if ($form->{groupitems}) {
1635 $sortorder = qq|ORDER BY pg.partsgroup, a.position|;
1637 $sortorder = qq|ORDER BY a.position|;
1640 $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty, | .
1641 qq|pg.partsgroup | .
1642 qq|FROM assembly a | .
1643 qq| JOIN parts p ON (a.parts_id = p.id) | .
1644 qq| LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
1645 qq| WHERE a.bom = '1' | .
1646 qq| AND a.id = ? | . $sortorder;
1647 @values = ($form->{"id_$i"});
1648 $sth = $dbh->prepare($query);
1649 $sth->execute(@values) || $form->dberror($query);
1651 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
1652 if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
1653 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1654 $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
1655 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'assembly-item-partsgroup');
1656 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
1659 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'assembly-item');
1660 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq|, $ref->{partnumber}, $ref->{description}|);
1661 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1666 CVar->get_non_editable_ic_cvars(form => $form,
1669 sub_module => 'orderitems',
1670 may_converted_from => ['orderitems', 'invoice']);
1672 push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} },
1673 CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)
1674 for @{ $ic_cvar_configs };
1676 push @{ $form->{TEMPLATE_ARRAYS}->{"project_cvar_" . $_->config->name} }, $_->value_as_text for @{ $project->cvars_by_config };
1680 $form->{totalweight} = $form->format_amount($myconfig, $totalweight, 3);
1681 $form->{totalweight_nofmt} = $totalweight;
1682 my $defaults = AM->get_defaults();
1683 $form->{weightunit} = $defaults->{weightunit};
1686 foreach $item (sort keys %taxaccounts) {
1687 $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
1689 push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase} }, $form->format_amount($myconfig, $taxbase{$item}, 2));
1690 push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase_nofmt} }, $taxbase{$item});
1691 push(@{ $form->{TEMPLATE_ARRAYS}->{tax} }, $form->format_amount($myconfig, $taxamount, 2));
1692 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_nofmt} }, $taxamount);
1693 push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} }, $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
1694 push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate_nofmt} }, $form->{"${item}_rate"} * 100);
1695 push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} }, $form->{"${item}_taxnumber"});
1696 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_id} }, $form->{"${item}_tax_id"});
1698 if ( $form->{"${item}_tax_id"} ) {
1699 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"};
1700 my $description = $tax_obj ? $tax_obj->translated_attribute('taxdescription', $form->{language_id}, 0) : '';
1701 push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $description . q{ } . 100 * $form->{"${item}_rate"} . q{%});
1705 $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
1706 $form->{discount_total} = $form->format_amount($myconfig, $form->{discount_total}, 2);
1707 $form->{nodiscount} = $form->format_amount($myconfig, $nodiscount, 2);
1708 $form->{yesdiscount} = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
1710 if($form->{taxincluded}) {
1711 $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal} - $tax, 2);
1712 $form->{subtotal_nofmt} = $form->{ordtotal} - $tax;
1714 $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
1715 $form->{subtotal_nofmt} = $form->{ordtotal};
1718 my $grossamount = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
1719 $form->{ordtotal} = $form->round_amount( $grossamount, 2, 1);
1720 $form->{rounding} = $form->round_amount(
1721 $form->{ordtotal} - $form->round_amount($grossamount, 2),
1726 $form->{rounding} = $form->format_amount($myconfig, $form->{rounding}, 2);
1727 $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
1729 $form->set_payment_options($myconfig, $form->{$form->{type} =~ /_quotation/ ? 'quodate' : 'orddate'}, $form->{type});
1731 $form->{username} = $myconfig->{name};
1733 $form->{department} = SL::DB::Manager::Department->find_by(id => $form->{department_id})->description if $form->{department_id};
1734 $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
1735 $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
1737 $form->{order} = SL::DB::Manager::Order->find_by(id => $form->{id}) if $form->{id};
1738 $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) for @separate_totals;
1740 $main::lxdebug->leave_sub();
1749 OE.pm - Order entry module
1753 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>.
1759 =item retrieve_simple PARAMS
1761 simple OE retrieval by id. does not look up customer, vendor, units or any other stuff. only oe and orderitems.
1763 my $order = retrieve_simple(id => 2);