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 $form->{fulltext} = trim($form->{fulltext});
322 if ($form->{fulltext}) {
323 my @fulltext_fields = qw(o.notes
327 o.transaction_description
332 $query .= join ' ILIKE ? OR ', @fulltext_fields;
333 $query .= ' ILIKE ?';
334 $query .= qq| OR EXISTS (SELECT files.id FROM files LEFT JOIN file_full_texts ON (file_full_texts.file_id = files.id) WHERE files.object_id = o.id AND files.object_type = 'sales_order' AND file_full_texts.full_text ILIKE ?)|;
335 $query .= qq| OR EXISTS (SELECT notes.id FROM notes WHERE notes.trans_id = o.id AND notes.trans_module LIKE 'oe' AND (notes.subject ILIKE ? OR notes.body ILIKE ?))|;
337 push(@values, like($form->{fulltext})) for 1 .. (scalar @fulltext_fields) + 3;
340 if ($form->{parts_partnumber}) {
343 SELECT orderitems.trans_id
345 LEFT JOIN parts ON (orderitems.parts_id = parts.id)
346 WHERE (orderitems.trans_id = o.id)
347 AND (parts.partnumber ILIKE ?)
351 push @values, like($form->{parts_partnumber});
354 if ($form->{parts_description}) {
357 SELECT orderitems.trans_id
359 WHERE (orderitems.trans_id = o.id)
360 AND (orderitems.description ILIKE ?)
364 push @values, like($form->{parts_description});
368 my @tokens = parse_line('\s+', 0, $form->{all});
369 # ordnumber quonumber customer.name vendor.name transaction_description
371 o.ordnumber ILIKE ? OR
372 o.quonumber ILIKE ? OR
374 o.transaction_description ILIKE ?
376 push @values, (like($_))x4 for @tokens;
379 my ($cvar_where, @cvar_values) = CVar->build_filter_query('module' => 'CT',
380 'trans_id_field' => 'ct.id',
384 $query .= qq| AND ($cvar_where)|;
385 push @values, @cvar_values;
388 my $sortdir = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
389 my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name"), "o.itime"));
390 my %allowed_sort_columns = (
391 "transdate" => "o.transdate",
392 "reqdate" => "o.reqdate",
394 "ordnumber" => "o.ordnumber",
395 "cusordnumber" => "o.cusordnumber",
396 "quonumber" => "o.quonumber",
398 "employee" => "e.name",
399 "salesman" => "s.name",
400 "shipvia" => "o.shipvia",
401 "transaction_description" => "o.transaction_description",
402 "shippingpoint" => "o.shippingpoint",
403 "insertdate" => "o.itime",
404 "taxzone" => "tz.description",
405 "payment_terms" => "pt.description",
406 "department" => "department.description",
407 "intnotes" => "o.intnotes",
409 if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
410 $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}" . ", o.itime ${sortdir}";
412 $query .= qq| ORDER by | . $sortorder;
414 my $sth = $dbh->prepare($query);
415 $sth->execute(@values) ||
416 $form->dberror($query . " (" . join(", ", @values) . ")");
420 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
421 $ref->{billed_amount} = $billed_amount{$ref->{id}};
422 $ref->{billed_netamount} = $billed_netamount{$ref->{id}};
423 if ($ref->{billed_amount} < 0) { # case: credit note(s) higher than invoices
424 $ref->{remaining_amount} = $ref->{amount} + $ref->{billed_amount};
425 $ref->{remaining_netamount} = $ref->{netamount} + $ref->{billed_netamount};
427 $ref->{remaining_amount} = $ref->{amount} - $ref->{billed_amount};
428 $ref->{remaining_netamount} = $ref->{netamount} - $ref->{billed_netamount};
430 $ref->{exchangerate} ||= $ref->{daily_exchangerate};
431 $ref->{exchangerate} ||= 1;
432 push @{ $form->{OE} }, $ref if $ref->{id} != $id{ $ref->{id} };
433 $id{ $ref->{id} } = $ref->{id};
438 $main::lxdebug->leave_sub();
441 sub transactions_for_todo_list {
442 $main::lxdebug->enter_sub();
447 my $myconfig = \%main::myconfig;
448 my $form = $main::form;
450 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
452 my $query = qq|SELECT id FROM employee WHERE login = ?|;
453 my ($e_id) = selectrow_query($form, $dbh, $query, $::myconfig{login});
456 qq|SELECT oe.id, oe.transdate, oe.reqdate, oe.quonumber, oe.transaction_description, oe.amount,
457 CASE WHEN (COALESCE(oe.customer_id, 0) = 0) THEN 'vendor' ELSE 'customer' END AS vc,
462 LEFT JOIN customer c ON (oe.customer_id = c.id)
463 LEFT JOIN vendor v ON (oe.vendor_id = v.id)
464 LEFT JOIN employee e ON (oe.employee_id = e.id)
465 WHERE (COALESCE(quotation, FALSE) = TRUE)
466 AND (COALESCE(closed, FALSE) = FALSE)
467 AND ((oe.employee_id = ?) OR (oe.salesman_id = ?))
468 AND NOT (oe.reqdate ISNULL)
469 AND (oe.reqdate < current_date)
472 my $quotations = selectall_hashref_query($form, $dbh, $query, $e_id, $e_id);
474 $main::lxdebug->leave_sub();
480 my ($self, $myconfig, $form) = @_;
481 $main::lxdebug->enter_sub();
483 my $rc = SL::DB->client->with_transaction(\&_save, $self, $myconfig, $form);
485 $::lxdebug->leave_sub;
491 $main::lxdebug->enter_sub();
493 my ($self, $myconfig, $form) = @_;
495 my $dbh = SL::DB->client->dbh;
496 my $restricter = SL::HTML::Restrict->create;
498 my ($query, @values, $sth, $null);
499 my $exchangerate = 0;
501 my $all_units = AM->retrieve_units($myconfig, $form);
502 $form->{all_units} = $all_units;
504 my $ic_cvar_configs = CVar->get_configs(module => 'IC',
507 $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id};
508 unless ($form->{employee_id}) {
509 $form->get_employee($dbh);
512 my $ml = ($form->{type} eq 'sales_order') ? 1 : -1;
514 my $number_field = $form->{type} =~ m{order} ? 'ordnumber' : 'quonumber';
515 my $trans_number = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{$number_field}, id => $form->{id});
516 $form->{$number_field} ||= $trans_number->create_unique; # set $form->{ordnumber} or $form->{quonumber}
517 my $is_new = !$form->{id};
520 $query = qq|DELETE FROM custom_variables
521 WHERE (config_id IN (SELECT id FROM custom_variable_configs WHERE (module = 'ShipTo')))
522 AND (trans_id IN (SELECT shipto_id FROM shipto WHERE (module = 'OE') AND (trans_id = ?)))|;
523 do_query($form, $dbh, $query, $form->{id});
525 $query = qq|DELETE FROM shipto | .
526 qq|WHERE trans_id = ? AND module = 'OE'|;
527 do_query($form, $dbh, $query, $form->{id});
531 $query = qq|SELECT nextval('id')|;
532 ($form->{id}) = selectrow_query($form, $dbh, $query);
534 $query = qq|INSERT INTO oe (id, ordnumber, employee_id, currency_id, taxzone_id) VALUES (?, '', ?, (SELECT currency_id FROM defaults), ?)|;
535 do_query($form, $dbh, $query, $form->{id}, $form->{employee_id}, $form->{taxzone_id});
552 my @processed_orderitems;
554 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
555 my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
558 for my $i (1 .. $form->{rowcount}) {
560 map({ $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) } qw(qty ship));
562 if ($form->{"id_$i"}) {
565 $query = qq|SELECT unit FROM parts WHERE id = ?|;
566 my ($item_unit) = selectrow_query($form, $dbh, $query, $form->{"id_$i"});
569 if (defined($all_units->{$item_unit}->{factor}) &&
570 (($all_units->{$item_unit}->{factor} * 1) != 0)) {
571 $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
573 my $baseqty = $form->{"qty_$i"} * $basefactor;
575 $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
576 $form->{"marge_absolut_$i"} = $form->parse_amount($myconfig, $form->{"marge_absolut_$i"}) * 1;
578 $form->{"lastcost_$i"} = $form->parse_amount($myconfig, $form->{"lastcost_$i"});
580 # keep entered selling price
582 $form->parse_amount($myconfig, $form->{"sellprice_$i"});
584 my ($dec) = ($fxsellprice =~ /\.(\d+)/);
586 my $decimalplaces = ($dec > 2) ? $dec : 2;
588 # undo discount formatting
589 $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
592 $form->{"sellprice_$i"} = $fxsellprice * (1 - $form->{"discount_$i"});
594 # round linetotal at least to 2 decimal places
595 $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
596 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
598 $form->{"inventory_accno_$i"} *= 1;
599 $form->{"expense_accno_$i"} *= 1;
601 @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
605 map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
607 if ($form->{taxincluded}) {
608 $taxamount = $linetotal * $taxrate / (1 + $taxrate);
609 $taxbase = $linetotal - $taxamount;
611 # we are not keeping a natural price, do not round
612 $form->{"sellprice_$i"} =
613 $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
615 $taxamount = $linetotal * $taxrate;
616 $taxbase = $linetotal;
619 if ($form->round_amount($taxrate, 7) == 0) {
620 if ($form->{taxincluded}) {
621 foreach my $item (@taxaccounts) {
622 $taxamount = $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
623 $taxaccounts{$item} += $taxamount;
624 $taxdiff += $taxamount;
625 $taxbase{$item} += $taxbase;
627 $taxaccounts{ $taxaccounts[0] } += $taxdiff;
629 foreach my $item (@taxaccounts) {
630 $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
631 $taxbase{$item} += $taxbase;
635 foreach my $item (@taxaccounts) {
636 $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
637 $taxbase{$item} += $taxbase;
641 $netamount += $form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor;
643 $reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef;
645 # Get pricegroup_id and save it. Unfortunately the interface
646 # also uses ID "0" for signalling that none is selected, but "0"
647 # must not be stored in the database. Therefore we cannot simply
649 ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
651 $pricegroup_id = undef if !$pricegroup_id;
653 # force new project, if not set yet
654 if ($::instance_conf->get_order_always_project && !$form->{"globalproject_id"} && ($form->{type} eq 'sales_order')) {
655 require SL::DB::Customer;
656 my $customer = SL::DB::Manager::Customer->find_by(id => $form->{customer_id});
657 die "Can't find customer" unless $customer;
658 die $main::locale->text("Error while creating project with project number of new order number, project number #1 already exists!", $form->{ordnumber})
659 if SL::DB::Manager::Project->find_by(projectnumber => $form->{ordnumber});
661 my $new_project = SL::DB::Project->new(
662 projectnumber => $form->{ordnumber},
663 description => $customer->name,
664 customer_id => $customer->id,
666 project_type_id => $::instance_conf->get_project_type_id,
667 project_status_id => $::instance_conf->get_project_status_id,
670 $form->{"globalproject_id"} = $new_project->id;
673 CVar->get_non_editable_ic_cvars(form => $form,
676 sub_module => 'orderitems',
677 may_converted_from => ['orderitems', 'invoice']);
681 # save detail record in orderitems table
682 if (! $form->{"orderitems_id_$i"}) {
683 $query = qq|SELECT nextval('orderitemsid')|;
684 ($form->{"orderitems_id_$i"}) = selectrow_query($form, $dbh, $query);
686 $query = qq|INSERT INTO orderitems (id, position) VALUES (?, ?)|;
687 do_query($form, $dbh, $query, $form->{"orderitems_id_$i"}, conv_i($position));
690 my $orderitems_id = $form->{"orderitems_id_$i"};
691 push @processed_orderitems, $orderitems_id;
694 UPDATE orderitems SET
695 trans_id = ?, position = ?, parts_id = ?, description = ?, longdescription = ?, qty = ?, base_qty = ?,
696 sellprice = ?, discount = ?, unit = ?, reqdate = ?, project_id = ?, serialnumber = ?, ship = ?,
697 pricegroup_id = ?, subtotal = ?,
698 marge_percent = ?, marge_total = ?, lastcost = ?, price_factor_id = ?,
699 active_price_source = ?, active_discount_source = ?,
700 price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?
704 conv_i($form->{id}), conv_i($position), conv_i($form->{"id_$i"}),
705 $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}),
706 $form->{"qty_$i"}, $baseqty,
707 $fxsellprice, $form->{"discount_$i"},
708 $form->{"unit_$i"}, conv_date($reqdate), conv_i($form->{"project_id_$i"}),
709 $form->{"serialnumber_$i"}, $form->{"ship_$i"},
710 $pricegroup_id, $form->{"subtotal_$i"} ? 't' : 'f',
711 $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
712 $form->{"lastcost_$i"}, conv_i($form->{"price_factor_id_$i"}),
713 $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"},
714 conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}),
715 conv_i($orderitems_id),
718 do_query($form, $dbh, $query, @values);
720 $form->{"sellprice_$i"} = $fxsellprice;
721 $form->{"discount_$i"} *= 100;
723 CVar->save_custom_variables(module => 'IC',
724 sub_module => 'orderitems',
725 trans_id => $orderitems_id,
726 configs => $ic_cvar_configs,
728 name_prefix => 'ic_',
729 name_postfix => "_$i",
732 # link previous items with orderitems
733 # assume we have a new workflow if we link from invoice or order to quotation
734 # unluckily orderitems are used for quotation and orders - therefore one more
735 # check to be sure NOT to link from order to quotation
736 foreach (qw(orderitems)) {
737 if (!$form->{saveasnew} && !$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}
738 && $form->{type} !~ 'quotation') {
739 RecordLinks->create_links('dbh' => $dbh,
742 'from_ids' => $form->{"converted_from_${_}_id_$i"},
743 'to_table' => 'orderitems',
744 'to_id' => $orderitems_id,
747 delete $form->{"converted_from_${_}_id_$i"};
752 # search for orphaned ids
753 $query = sprintf 'SELECT id FROM orderitems WHERE trans_id = ? AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_orderitems;
754 @values = (conv_i($form->{id}), map { conv_i($_) } @processed_orderitems);
755 my @orphaned_ids = map { $_->{id} } selectall_hashref_query($form, $dbh, $query, @values);
757 if (scalar @orphaned_ids) {
758 # clean up orderitems
759 $query = sprintf 'DELETE FROM orderitems WHERE id IN (%s)', join ', ', ("?") x scalar @orphaned_ids;
760 do_query($form, $dbh, $query, @orphaned_ids);
763 $reqdate = ($form->{reqdate}) ? $form->{reqdate} : undef;
767 map { $tax += $form->round_amount($taxaccounts{$_}, 2) } keys %taxaccounts;
769 $amount = $form->round_amount($netamount + $tax, 2, 1);
770 $netamount = $form->round_amount($netamount, 2);
772 if ($form->{currency} eq $form->{defaultcurrency}) {
773 $form->{exchangerate} = 1;
775 $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? 'buy' : 'sell');
778 # from inputfield (exchangerate) or hidden (forex)
779 my $exchangerate_from_form = $form->{forex} || $form->parse_amount($myconfig, $form->{exchangerate});
781 $form->{exchangerate} = $exchangerate || $exchangerate_from_form;
783 my $quotation = $form->{type} =~ /_order$/ ? 'f' : 't';
788 ordnumber = ?, quonumber = ?, cusordnumber = ?, transdate = ?, vendor_id = ?,
789 customer_id = ?, amount = ?, netamount = ?, reqdate = ?, tax_point = ?, taxincluded = ?,
790 shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, currency_id = (SELECT id FROM currencies WHERE name=?), closed = ?,
791 delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
792 taxzone_id = ?, shipto_id = ?, billing_address_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,delivery_term_id = ?,
793 globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
794 , order_probability = ?, expected_billing_date = ?
797 @values = ($form->{ordnumber} || '', $form->{quonumber},
798 $form->{cusordnumber}, conv_date($form->{transdate}),
799 conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
800 $amount, $netamount, conv_date($reqdate), conv_date($form->{tax_point}),
801 $form->{taxincluded} ? 't' : 'f', $form->{shippingpoint},
802 $form->{shipvia}, $restricter->process($form->{notes}), $form->{intnotes},
803 $form->{currency}, $form->{closed} ? 't' : 'f',
804 $form->{delivered} ? "t" : "f", $form->{proforma} ? 't' : 'f',
805 $quotation, conv_i($form->{department_id}),
806 conv_i($form->{language_id}), conv_i($form->{taxzone_id}),
807 conv_i($form->{shipto_id}), conv_i($form->{billing_address_id}), conv_i($form->{payment_id}),
808 conv_i($form->{delivery_vendor_id}),
809 conv_i($form->{delivery_customer_id}),
810 conv_i($form->{delivery_term_id}),
811 conv_i($form->{globalproject_id}), conv_i($form->{employee_id}),
812 conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
813 $form->{transaction_description},
814 $form->{marge_total} * 1, $form->{marge_percent} * 1,
815 $form->{order_probability} * 1, conv_date($form->{expected_billing_date}),
816 conv_i($form->{id}));
817 do_query($form, $dbh, $query, @values);
819 $form->new_lastmtime('oe');
821 $form->{ordtotal} = $amount;
823 $form->{name} = $form->{ $form->{vc} };
824 $form->{name} =~ s/--\Q$form->{"$form->{vc}_id"}\E//;
827 if (!$form->{shipto_id}) {
828 $form->add_shipto($dbh, $form->{id}, "OE");
831 # save printed, emailed, queued
832 $form->save_status($dbh);
834 # Link this record to the records it was created from.
835 $form->{convert_from_oe_ids} =~ s/^\s+//;
836 $form->{convert_from_oe_ids} =~ s/\s+$//;
837 my @convert_from_oe_ids = split m/\s+/, $form->{convert_from_oe_ids};
838 delete $form->{convert_from_oe_ids};
839 if (!$form->{useasnew} && scalar @convert_from_oe_ids) {
840 RecordLinks->create_links('dbh' => $dbh,
842 'from_table' => 'oe',
843 'from_ids' => \@convert_from_oe_ids,
845 'to_id' => $form->{id},
847 $self->_close_quotations_rfqs('dbh' => $dbh,
848 'from_id' => \@convert_from_oe_ids,
849 'to_id' => $form->{id});
852 if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
853 if ($form->{vc} eq 'customer') {
854 $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
856 if ($form->{vc} eq 'vendor') {
857 $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
861 $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ?
862 "quonumber" : "ordnumber"};
864 Common::webdav_folder($form);
866 $self->save_periodic_invoices_config(dbh => $dbh,
867 oe_id => $form->{id},
868 config_yaml => $form->{periodic_invoices_config})
869 if ($form->{type} eq 'sales_order');
871 $self->_link_created_sales_order_to_requirement_specs_for_sales_quotations(
872 type => $form->{type},
873 converted_from_ids => \@convert_from_oe_ids,
874 sales_order_id => $form->{id},
878 $self->_set_project_in_linked_requirement_spec(
879 type => $form->{type},
880 project_id => $form->{globalproject_id},
881 sales_order_id => $form->{id},
884 $main::lxdebug->leave_sub();
889 sub _link_created_sales_order_to_requirement_specs_for_sales_quotations {
890 my ($self, %params) = @_;
892 # If this is a sales order created from a sales quotation and if
893 # that sales quotation was created from a requirement spec document
894 # then link the newly created sales order to the requirement spec
897 return if !$params{is_new};
898 return if $params{type} ne 'sales_order';
899 return if !@{ $params{converted_from_ids} };
901 my $oe_objects = SL::DB::Manager::Order->get_all(where => [ id => $params{converted_from_ids} ]);
902 my @sales_quotations = grep { $_->is_type('sales_quotation') } @{ $oe_objects };
904 return if !@sales_quotations;
906 my $rs_orders = SL::DB::Manager::RequirementSpecOrder->get_all(where => [ order_id => [ map { $_->id } @sales_quotations ] ]);
908 return if !@{ $rs_orders };
910 $rs_orders->[0]->db->with_transaction(sub {
911 foreach my $rs_order (@{ $rs_orders }) {
912 SL::DB::RequirementSpecOrder->new(
913 order_id => $params{sales_order_id},
914 requirement_spec_id => $rs_order->requirement_spec_id,
915 version_id => $rs_order->version_id,
923 sub _set_project_in_linked_requirement_spec {
924 my ($self, %params) = @_;
926 return if $params{type} ne 'sales_order';
927 return if !$params{project_id} || !$params{sales_order_id};
930 UPDATE requirement_specs
933 SELECT so.requirement_spec_id
934 FROM requirement_spec_orders so
935 WHERE so.order_id = ?
939 do_query($::form, $::form->get_standard_dbh, $query, $params{project_id}, $params{sales_order_id});
942 sub save_periodic_invoices_config {
943 my ($self, %params) = @_;
945 return if !$params{oe_id};
947 my $config = $params{config_yaml} ? SL::YAML::Load($params{config_yaml}) : undef;
948 return if 'HASH' ne ref $config;
950 my $obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $params{oe_id})
951 || SL::DB::PeriodicInvoicesConfig->new(oe_id => $params{oe_id});
952 $obj->update_attributes(%{ $config });
955 sub load_periodic_invoice_config {
959 delete $form->{periodic_invoices_config};
962 my $config_obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $form->{id});
965 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
966 print printer_id copies direct_debit send_email email_recipient_contact_id email_recipient_address email_sender email_subject email_body) };
967 $form->{periodic_invoices_config} = SL::YAML::Dump($config);
972 sub _close_quotations_rfqs {
973 $main::lxdebug->enter_sub();
978 Common::check_params(\%params, qw(from_id to_id));
980 my $myconfig = \%main::myconfig;
981 my $form = $main::form;
983 my $dbh = $params{dbh} || SL::DB->client->dbh;
985 SL::DB->client->with_transaction(sub {
987 my $query = qq|SELECT quotation FROM oe WHERE id = ?|;
988 my $sth = prepare_query($form, $dbh, $query);
990 do_statement($form, $sth, $query, conv_i($params{to_id}));
992 my ($quotation) = $sth->fetchrow_array();
1000 foreach my $from_id (@{ $params{from_id} }) {
1001 $from_id = conv_i($from_id);
1002 do_statement($form, $sth, $query, $from_id);
1003 ($quotation) = $sth->fetchrow_array();
1004 push @close_ids, $from_id if ($quotation);
1009 if (scalar @close_ids) {
1010 $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_ids) . qq|)|;
1011 do_query($form, $dbh, $query, @close_ids);
1014 }) or do { die SL::DB->client->error };
1016 $main::lxdebug->leave_sub();
1020 $main::lxdebug->enter_sub();
1022 my ($self, $myconfig, $form) = @_;
1024 my $rc = SL::DB::Order->new->db->with_transaction(sub {
1025 my @spoolfiles = grep { $_ } map { $_->spoolfile } @{ SL::DB::Manager::Status->get_all(where => [ trans_id => $form->{id} ]) };
1027 SL::DB::Order->new(id => $form->{id})->delete;
1029 my $spool = $::lx_office_conf{paths}->{spool};
1030 unlink map { "$spool/$_" } @spoolfiles if $spool;
1033 }) or do { die SL::DB->client->error };
1035 $main::lxdebug->leave_sub();
1041 my ($self, $myconfig, $form) = @_;
1042 $main::lxdebug->enter_sub();
1044 my $rc = SL::DB->client->with_transaction(\&_retrieve, $self, $myconfig, $form);
1046 $::lxdebug->leave_sub;
1051 my ($self, $myconfig, $form) = @_;
1053 # connect to database
1054 my $dbh = SL::DB->client->dbh;
1056 my ($query, $query_add, @values, @ids, $sth);
1058 # translate the ids (given by id_# and trans_id_#) into one array of ids, so we can join them later
1060 push @ids, $form->{"trans_id_$_"}
1061 if ($form->{"multi_id_$_"} and $form->{"trans_id_$_"})
1062 } (1 .. $form->{"rowcount"});
1064 if ($form->{rowcount} && scalar @ids) {
1065 $form->{convert_from_oe_ids} = join ' ', @ids;
1068 # if called in multi id mode, and still only got one id, switch back to single id
1069 if ($form->{"rowcount"} and $#ids == 0) {
1070 $form->{"id"} = $ids[0];
1072 delete $form->{convert_from_oe_ids};
1075 # and remember for the rest of the function
1076 my $is_collective_order = scalar @ids;
1078 # If collective order was created from exactly 1 order, we assume the same
1079 # behaviour as a "save as new" from within an order is actually desired, i.e.
1080 # the original order isn't part of a workflow where we want to remember
1081 # record_links, but simply a quick way of generating a new order from an old
1082 # one without having to enter everything again.
1083 # Setting useasnew will prevent the creation of record_links for the items
1084 # when saving the new order.
1085 # This form variable is probably not necessary, could just set saveasnew instead
1086 $form->{useasnew} = 1 if $is_collective_order == 1;
1089 my $extra_days = $form->{type} eq 'sales_quotation' ? $::instance_conf->get_reqdate_interval :
1090 $form->{type} eq 'sales_order' ? $::instance_conf->get_delivery_date_interval : 1;
1091 if ( ($form->{type} eq 'sales_order' && !$::instance_conf->get_deliverydate_on)
1092 || ($form->{type} eq 'sales_quotation' && !$::instance_conf->get_reqdate_on)) {
1093 $form->{reqdate} = '';
1095 $form->{reqdate} = DateTime->today_local->next_workday(extra_days => $extra_days)->to_kivitendo;
1097 $form->{transdate} = DateTime->today_local->to_kivitendo;
1100 # get default accounts
1101 $query = qq|SELECT (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
1102 (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id) AS income_accno,
1103 (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id) AS expense_accno,
1104 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
1105 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
1106 (SELECT c.accno FROM chart c WHERE d.rndgain_accno_id = c.id) AS rndgain_accno,
1107 (SELECT c.accno FROM chart c WHERE d.rndloss_accno_id = c.id) AS rndloss_accno
1110 my $ref = selectfirst_hashref_query($form, $dbh, $query);
1111 map { $form->{$_} = $ref->{$_} } keys %$ref;
1113 $form->{currency} = $form->get_default_currency($myconfig);
1115 # set reqdate if this is an invoice->order conversion. If someone knows a better check to ensure
1116 # we come from invoices, feel free.
1117 $form->{reqdate} = $form->{deliverydate}
1118 if ( $form->{deliverydate}
1119 and $form->{callback} =~ /action=ar_transactions/);
1121 my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
1123 if ($form->{id} or @ids) {
1125 # retrieve order for single id
1126 # NOTE: this query is intended to fetch all information only ONCE.
1127 # so if any of these infos is important (or even different) for any item,
1128 # it will be killed out and then has to be fetched from the item scope query further down
1130 qq|SELECT o.cp_id, o.ordnumber, o.transdate, o.reqdate,
1131 o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
1132 (SELECT cu.name FROM currencies cu WHERE cu.id=o.currency_id) AS currency, e.name AS employee, o.employee_id, o.salesman_id,
1133 o.${vc}_id, cv.name AS ${vc}, o.amount AS invtotal,
1134 o.closed, o.reqdate, o.tax_point, o.quonumber, o.department_id, o.cusordnumber,
1136 d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
1137 o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id, o.billing_address_id,
1138 o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id,
1139 o.itime::DATE AS insertdate, o.order_probability, o.expected_billing_date
1141 JOIN ${vc} cv ON (o.${vc}_id = cv.id)
1142 LEFT JOIN employee e ON (o.employee_id = e.id)
1143 LEFT JOIN department d ON (o.department_id = d.id) | .
1146 : "WHERE o.id IN (" . join(', ', map("? ", @ids)) . ")"
1148 @values = $form->{id} ? ($form->{id}) : @ids;
1149 $sth = prepare_execute_query($form, $dbh, $query, @values);
1151 $ref = $sth->fetchrow_hashref("NAME_lc");
1154 map { $form->{$_} = $ref->{$_} } keys %$ref;
1156 $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ? "quonumber" : "ordnumber"};
1158 # set all entries for multiple ids blank that yield different information
1159 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1160 map { $form->{$_} = '' if ($ref->{$_} ne $form->{$_}) } keys %$ref;
1163 $form->{mtime} ||= $form->{itime};
1164 $form->{lastmtime} = $form->{mtime};
1166 # if not given, fill transdate with current_date
1167 $form->{transdate} = $form->current_date($myconfig)
1168 unless $form->{transdate};
1172 if ($form->{delivery_customer_id}) {
1173 $query = qq|SELECT name FROM customer WHERE id = ?|;
1174 ($form->{delivery_customer_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_customer_id});
1177 if ($form->{delivery_vendor_id}) {
1178 $query = qq|SELECT name FROM customer WHERE id = ?|;
1179 ($form->{delivery_vendor_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_vendor_id});
1182 # shipto and pinted/mailed/queued status makes only sense for single id retrieve
1184 $query = qq|SELECT s.* FROM shipto s WHERE s.trans_id = ? AND s.module = 'OE'|;
1185 $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
1187 $ref = $sth->fetchrow_hashref("NAME_lc");
1188 $form->{$_} = $ref->{$_} for grep { m{^shipto(?!_id$)} } keys %$ref;
1191 if ($ref->{shipto_id}) {
1192 my $cvars = CVar->get_custom_variables(
1195 trans_id => $ref->{shipto_id},
1197 $form->{"shiptocvar_$_->{name}"} = $_->{value} for @{ $cvars };
1200 # get printed, emailed and queued
1201 $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
1202 $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
1204 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1205 $form->{printed} .= "$ref->{formname} " if $ref->{printed};
1206 $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
1207 $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
1210 map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
1213 my $transdate = $form->{tax_point} ? $dbh->quote($form->{tax_point}) : $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
1215 $form->{taxzone_id} = 0 unless ($form->{taxzone_id});
1216 unshift @values, ($form->{taxzone_id}) x 2;
1218 # retrieve individual items
1219 # this query looks up all information about the items
1220 # stuff different from the whole will not be overwritten, but saved with a suffix.
1222 qq|SELECT o.id AS orderitems_id,
1223 c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from as inventory_valid,
1224 c2.accno AS income_accno, c2.new_chart_id AS income_new_chart, date($transdate) - c2.valid_from as income_valid,
1225 c3.accno AS expense_accno, c3.new_chart_id AS expense_new_chart, date($transdate) - c3.valid_from as expense_valid,
1226 oe.ordnumber AS ordnumber_oe, oe.transdate AS transdate_oe, oe.cusordnumber AS cusordnumber_oe,
1227 p.partnumber, p.part_type, p.listprice, o.description, o.qty,
1228 p.classification_id,
1229 o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.part_type,
1230 o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
1231 o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
1232 o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source, o.active_discount_source,
1233 pr.projectnumber, p.formel,
1234 pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
1236 JOIN parts p ON (o.parts_id = p.id)
1237 JOIN oe ON (o.trans_id = oe.id)
1238 LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c1.id)
1239 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)
1240 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)
1241 LEFT JOIN project pr ON (o.project_id = pr.id)
1242 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
1244 ? qq|WHERE o.trans_id = ?|
1245 : qq|WHERE o.trans_id IN (| . join(", ", map("?", @ids)) . qq|)|) .
1246 qq|ORDER BY o.trans_id, o.position|;
1248 @ids = $form->{id} ? ($form->{id}) : @ids;
1249 $sth = prepare_execute_query($form, $dbh, $query, @values);
1251 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1252 # Retrieve custom variables.
1253 my $cvars = CVar->get_custom_variables(dbh => $dbh,
1255 sub_module => 'orderitems',
1256 trans_id => $ref->{orderitems_id},
1258 map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
1261 if (!$ref->{"part_type"} eq 'part') {
1262 map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid));
1264 # delete($ref->{"part_inventory_accno_id"});
1266 # in collective order, copy global ordnumber, transdate, cusordnumber into item scope
1267 # unless already present there
1268 # remove _oe entries afterwards
1269 map { $ref->{$_} = $ref->{"${_}_oe"} if ($ref->{$_} eq '') }
1270 qw|ordnumber transdate cusordnumber|
1272 map { delete $ref->{$_} } qw|ordnumber_oe transdate_oe cusordnumber_oe|;
1276 while ($ref->{inventory_new_chart} && ($ref->{inventory_valid} >= 0)) {
1278 qq|SELECT accno AS inventory_accno, | .
1279 qq| new_chart_id AS inventory_new_chart, | .
1280 qq| date($transdate) - valid_from AS inventory_valid | .
1281 qq|FROM chart WHERE id = $ref->{inventory_new_chart}|;
1282 ($ref->{inventory_accno}, $ref->{inventory_new_chart},
1283 $ref->{inventory_valid}) = selectrow_query($form, $dbh, $query);
1286 while ($ref->{income_new_chart} && ($ref->{income_valid} >= 0)) {
1288 qq|SELECT accno AS income_accno, | .
1289 qq| new_chart_id AS income_new_chart, | .
1290 qq| date($transdate) - valid_from AS income_valid | .
1291 qq|FROM chart WHERE id = $ref->{income_new_chart}|;
1292 ($ref->{income_accno}, $ref->{income_new_chart},
1293 $ref->{income_valid}) = selectrow_query($form, $dbh, $query);
1296 while ($ref->{expense_new_chart} && ($ref->{expense_valid} >= 0)) {
1298 qq|SELECT accno AS expense_accno, | .
1299 qq| new_chart_id AS expense_new_chart, | .
1300 qq| date($transdate) - valid_from AS expense_valid | .
1301 qq|FROM chart WHERE id = $ref->{expense_new_chart}|;
1302 ($ref->{expense_accno}, $ref->{expense_new_chart},
1303 $ref->{expense_valid}) = selectrow_query($form, $dbh, $query);
1306 # delete orderitems_id in collective orders, so that they get cloned no matter what
1307 # is this correct? or is the following meant?
1308 # remember orderitems_ids in converted_from_orderitems_ids, so that they may be linked
1309 $ref->{converted_from_orderitems_id} = delete $ref->{orderitems_id} if $is_collective_order;
1311 # get tax rates and description
1312 my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1314 qq|SELECT c.accno, t.taxdescription, t.rate, t.id as tax_id, c.accno as taxnumber | .
1316 qq|LEFT JOIN chart c on (c.id = t.chart_id) | .
1317 qq|WHERE t.id IN (SELECT tk.tax_id FROM taxkeys tk | .
1318 qq| WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?) | .
1319 qq| AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1) | .
1320 qq|ORDER BY c.accno|;
1321 my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
1322 $ref->{taxaccounts} = "";
1324 while (my $ptr = $stw->fetchrow_hashref("NAME_lc")) {
1325 if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1329 $ref->{taxaccounts} .= "$ptr->{accno} ";
1330 if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
1331 $form->{"$ptr->{accno}_rate"} = $ptr->{rate};
1332 $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1333 $form->{"$ptr->{accno}_taxnumber"} = $ptr->{taxnumber};
1334 $form->{"$ptr->{accno}_tax_id"} = $ptr->{tax_id};
1335 $form->{taxaccounts} .= "$ptr->{accno} ";
1340 chop $ref->{taxaccounts};
1342 push @{ $form->{form_details} }, $ref;
1349 # get last name used
1350 $form->lastname_used($dbh, $myconfig, $form->{vc})
1351 unless $form->{"$form->{vc}_id"};
1355 $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? "buy" : "sell");
1357 Common::webdav_folder($form);
1359 $self->load_periodic_invoice_config($form);
1364 sub retrieve_simple {
1365 $main::lxdebug->enter_sub();
1370 Common::check_params(\%params, qw(id));
1372 my $myconfig = \%main::myconfig;
1373 my $form = $main::form;
1375 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
1377 my $oe_query = qq|SELECT * FROM oe WHERE id = ?|;
1378 my $oi_query = qq|SELECT * FROM orderitems WHERE trans_id = ? ORDER BY position|;
1380 my $order = selectfirst_hashref_query($form, $dbh, $oe_query, conv_i($params{id}));
1381 $order->{orderitems} = selectall_hashref_query( $form, $dbh, $oi_query, conv_i($params{id}));
1383 $main::lxdebug->leave_sub();
1389 $main::lxdebug->enter_sub();
1391 my ($self, $myconfig, $form) = @_;
1393 # connect to database
1394 my $dbh = SL::DB->client->dbh;
1400 my $nodiscount_subtotal = 0;
1401 my $discount_subtotal = 0;
1404 my @partsgroup = ();
1407 my $subtotal_header = 0;
1408 my $subposition = 0;
1416 push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
1418 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
1421 foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
1422 $price_factors{$pfac->{id}} = $pfac;
1423 $pfac->{factor} *= 1;
1424 $pfac->{formatted_factor} = $form->format_amount($myconfig, $pfac->{factor});
1427 # sort items by partsgroup
1428 for $i (1 .. $form->{rowcount}) {
1430 if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
1431 $partsgroup = $form->{"partsgroup_$i"};
1433 push @partsgroup, [$i, $partsgroup];
1434 push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
1440 $projects = SL::DB::Manager::Project->get_all(query => [ id => \@project_ids ]);
1441 %projects_by_id = map { $_->id => $_ } @$projects;
1444 if ($projects_by_id{$form->{"globalproject_id"}}) {
1445 $form->{globalprojectnumber} = $projects_by_id{$form->{"globalproject_id"}}->projectnumber;
1446 $form->{globalprojectdescription} = $projects_by_id{$form->{"globalproject_id"}}->description;
1448 for (@{ $projects_by_id{$form->{"globalproject_id"}}->cvars_by_config }) {
1449 $form->{"project_cvar_" . $_->config->name} = $_->value_as_text;
1453 $form->{discount} = [];
1455 # get some values of parts from db on store them in extra array,
1456 # so that they can be sorted in later
1457 my %prepared_template_arrays = IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
1458 my @prepared_arrays = keys %prepared_template_arrays;
1459 my @separate_totals = qw(non_separate_subtotal);
1461 $form->{TEMPLATE_ARRAYS} = { };
1463 my $ic_cvar_configs = CVar->get_configs(module => 'IC');
1464 my $project_cvar_configs = CVar->get_configs(module => 'Projects');
1467 qw(runningnumber number description longdescription qty qty_nofmt ship ship_nofmt unit bin
1468 partnotes serialnumber reqdate sellprice sellprice_nofmt listprice listprice_nofmt netprice netprice_nofmt
1469 discount discount_nofmt p_discount discount_sub discount_sub_nofmt nodiscount_sub nodiscount_sub_nofmt
1470 linetotal linetotal_nofmt nodiscount_linetotal nodiscount_linetotal_nofmt tax_rate projectnumber projectdescription
1471 price_factor price_factor_name partsgroup weight weight_nofmt lineweight lineweight_nofmt optional);
1473 push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
1474 push @arrays, map { "project_cvar_$_->{name}" } @{ $project_cvar_configs };
1476 my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
1478 map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @prepared_arrays);
1480 my $totalweight = 0;
1482 foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
1485 if ($item->[1] ne $sameitem) {
1486 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'partsgroup');
1487 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
1488 $sameitem = $item->[1];
1490 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1493 $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
1495 if ($form->{"id_$i"} != 0) {
1497 # add number, description and qty to $form->{number}, ....
1499 if ($form->{"subtotal_$i"} && !$subtotal_header) {
1500 $subtotal_header = $i;
1501 $position = int($position);
1504 } elsif ($subtotal_header) {
1506 $position = int($position);
1507 $position = $position.".".$subposition;
1509 $position = int($position);
1513 my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
1515 push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, $prepared_template_arrays{$_}[$i - 1]) for @prepared_arrays;
1517 push @{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'normal';
1518 push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} }, $position;
1519 push @{ $form->{TEMPLATE_ARRAYS}->{number} }, $form->{"partnumber_$i"};
1520 push @{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->{"description_$i"};
1521 push @{ $form->{TEMPLATE_ARRAYS}->{longdescription} }, $form->{"longdescription_$i"};
1522 push @{ $form->{TEMPLATE_ARRAYS}->{qty} }, $form->format_amount($myconfig, $form->{"qty_$i"});
1523 push @{ $form->{TEMPLATE_ARRAYS}->{qty_nofmt} }, $form->{"qty_$i"};
1524 push @{ $form->{TEMPLATE_ARRAYS}->{ship} }, $form->format_amount($myconfig, $form->{"ship_$i"});
1525 push @{ $form->{TEMPLATE_ARRAYS}->{ship_nofmt} }, $form->{"ship_$i"};
1526 push @{ $form->{TEMPLATE_ARRAYS}->{unit} }, $form->{"unit_$i"};
1527 push @{ $form->{TEMPLATE_ARRAYS}->{bin} }, $form->{"bin_$i"};
1528 push @{ $form->{TEMPLATE_ARRAYS}->{partnotes} }, $form->{"partnotes_$i"};
1529 push @{ $form->{TEMPLATE_ARRAYS}->{serialnumber} }, $form->{"serialnumber_$i"};
1530 push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} }, $form->{"reqdate_$i"};
1531 push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} }, $form->{"sellprice_$i"};
1532 push @{ $form->{TEMPLATE_ARRAYS}->{sellprice_nofmt} }, $form->parse_amount($myconfig, $form->{"sellprice_$i"});
1533 push @{ $form->{TEMPLATE_ARRAYS}->{listprice} }, $form->format_amount($myconfig, $form->{"listprice_$i"}, 2);
1534 push @{ $form->{TEMPLATE_ARRAYS}->{listprice_nofmt} }, $form->{"listprice_$i"};
1535 push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} }, $price_factor->{formatted_factor};
1536 push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
1537 push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} }, $form->{"partsgroup_$i"};
1538 push @{ $form->{TEMPLATE_ARRAYS}->{optional} }, $form->{"optional_$i"};
1540 my $sellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
1541 my ($dec) = ($sellprice =~ /\.(\d+)/);
1542 my $decimalplaces = max 2, length($dec);
1544 my $parsed_discount = $form->parse_amount($myconfig, $form->{"discount_$i"});
1546 my $linetotal_exact = $form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor};
1547 my $linetotal = $form->round_amount($linetotal_exact, 2);
1549 my $nodiscount_exact_linetotal = $form->{"qty_$i"} * $sellprice / $price_factor->{factor};
1550 my $nodiscount_linetotal = $form->round_amount($nodiscount_exact_linetotal,2);
1552 my $discount = $nodiscount_linetotal - $linetotal; # is always rounded because $nodiscount_linetotal and $linetotal are rounded
1554 my $discount_round_error = $discount + ($linetotal_exact - $nodiscount_exact_linetotal); # not used
1556 $form->{"netprice_$i"} = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, $decimalplaces);
1558 push @{ $form->{TEMPLATE_ARRAYS}->{netprice} }, ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
1559 push @{ $form->{TEMPLATE_ARRAYS}->{netprice_nofmt} }, ($form->{"netprice_$i"} != 0) ? $form->{"netprice_$i"} : '';
1561 $linetotal = ($linetotal != 0) ? $linetotal : '';
1563 push @{ $form->{TEMPLATE_ARRAYS}->{discount} }, ($discount != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
1564 push @{ $form->{TEMPLATE_ARRAYS}->{discount_nofmt} }, ($discount != 0) ? $discount * -1 : '';
1565 push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} }, $form->{"discount_$i"};
1567 if ( $prepared_template_arrays{separate}[$i - 1] ) {
1568 my $pabbr = $prepared_template_arrays{separate}[$i - 1];
1569 if ( ! $form->{"separate_${pabbr}_subtotal"} ) {
1570 push @separate_totals , "separate_${pabbr}_subtotal";
1571 $form->{"separate_${pabbr}_subtotal"} = 0;
1573 $form->{"separate_${pabbr}_subtotal"} += $linetotal;
1575 $form->{non_separate_subtotal} += $linetotal;
1578 $form->{ordtotal} += $linetotal unless $form->{"optional_$i"};
1579 $form->{nodiscount_total} += $nodiscount_linetotal;
1580 $form->{discount_total} += $discount;
1582 if ($subtotal_header) {
1583 $discount_subtotal += $linetotal;
1584 $nodiscount_subtotal += $nodiscount_linetotal;
1587 if ($form->{"subtotal_$i"} && $subtotal_header && ($subtotal_header != $i)) {
1588 push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} }, $form->format_amount($myconfig, $discount_subtotal, 2);
1589 push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub_nofmt} }, $discount_subtotal;
1590 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} }, $form->format_amount($myconfig, $nodiscount_subtotal, 2);
1591 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub_nofmt} }, $nodiscount_subtotal;
1593 $discount_subtotal = 0;
1594 $nodiscount_subtotal = 0;
1595 $subtotal_header = 0;
1598 push @{ $form->{TEMPLATE_ARRAYS}->{$_} }, "" for qw(discount_sub nodiscount_sub discount_sub_nofmt nodiscount_sub_nofmt);
1601 if (!$form->{"discount_$i"}) {
1602 $nodiscount += $linetotal;
1605 my $project = $projects_by_id{$form->{"project_id_$i"}} || SL::DB::Project->new;
1607 push @{ $form->{TEMPLATE_ARRAYS}->{linetotal} }, $form->format_amount($myconfig, $linetotal, 2);
1608 push @{ $form->{TEMPLATE_ARRAYS}->{linetotal_nofmt} }, $linetotal_exact;
1609 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} }, $form->format_amount($myconfig, $nodiscount_linetotal, 2);
1610 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal_nofmt} }, $nodiscount_linetotal;
1611 push @{ $form->{TEMPLATE_ARRAYS}->{projectnumber} }, $project->projectnumber;
1612 push @{ $form->{TEMPLATE_ARRAYS}->{projectdescription} }, $project->description;
1614 my $lineweight = $form->{"qty_$i"} * $form->{"weight_$i"};
1615 $totalweight += $lineweight;
1616 push @{ $form->{TEMPLATE_ARRAYS}->{weight} }, $form->format_amount($myconfig, $form->{"weight_$i"}, 3);
1617 push @{ $form->{TEMPLATE_ARRAYS}->{weight_nofmt} }, $form->{"weight_$i"};
1618 push @{ $form->{TEMPLATE_ARRAYS}->{lineweight} }, $form->format_amount($myconfig, $lineweight, 3);
1619 push @{ $form->{TEMPLATE_ARRAYS}->{lineweight_nofmt} }, $lineweight;
1621 my ($taxamount, $taxbase);
1624 map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
1626 unless ($form->{"optional_$i"}) {
1627 if ($form->{taxincluded}) {
1630 $taxamount = $linetotal * $taxrate / (1 + $taxrate);
1631 $taxbase = $linetotal / (1 + $taxrate);
1633 $taxamount = $linetotal * $taxrate;
1634 $taxbase = $linetotal;
1638 if ($taxamount != 0) {
1639 foreach my $accno (split / /, $form->{"taxaccounts_$i"}) {
1640 $taxaccounts{$accno} += $taxamount * $form->{"${accno}_rate"} / $taxrate;
1641 $taxbase{$accno} += $taxbase;
1645 $tax_rate = $taxrate * 100;
1646 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
1648 if ($form->{"part_type_$i"} eq 'assembly') {
1651 # get parts and push them onto the stack
1653 if ($form->{groupitems}) {
1654 $sortorder = qq|ORDER BY pg.partsgroup, a.position|;
1656 $sortorder = qq|ORDER BY a.position|;
1659 $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty, | .
1660 qq|pg.partsgroup | .
1661 qq|FROM assembly a | .
1662 qq| JOIN parts p ON (a.parts_id = p.id) | .
1663 qq| LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
1664 qq| WHERE a.bom = '1' | .
1665 qq| AND a.id = ? | . $sortorder;
1666 @values = ($form->{"id_$i"});
1667 $sth = $dbh->prepare($query);
1668 $sth->execute(@values) || $form->dberror($query);
1670 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
1671 if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
1672 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1673 $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
1674 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'assembly-item-partsgroup');
1675 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
1678 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'assembly-item');
1679 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq|, $ref->{partnumber}, $ref->{description}|);
1680 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1685 CVar->get_non_editable_ic_cvars(form => $form,
1688 sub_module => 'orderitems',
1689 may_converted_from => ['orderitems', 'invoice']);
1691 push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} },
1692 CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)
1693 for @{ $ic_cvar_configs };
1695 push @{ $form->{TEMPLATE_ARRAYS}->{"project_cvar_" . $_->config->name} }, $_->value_as_text for @{ $project->cvars_by_config };
1699 $form->{totalweight} = $form->format_amount($myconfig, $totalweight, 3);
1700 $form->{totalweight_nofmt} = $totalweight;
1701 my $defaults = AM->get_defaults();
1702 $form->{weightunit} = $defaults->{weightunit};
1705 foreach $item (sort keys %taxaccounts) {
1706 $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
1708 push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase} }, $form->format_amount($myconfig, $taxbase{$item}, 2));
1709 push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase_nofmt} }, $taxbase{$item});
1710 push(@{ $form->{TEMPLATE_ARRAYS}->{tax} }, $form->format_amount($myconfig, $taxamount, 2));
1711 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_nofmt} }, $taxamount);
1712 push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} }, $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
1713 push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate_nofmt} }, $form->{"${item}_rate"} * 100);
1714 push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} }, $form->{"${item}_taxnumber"});
1715 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_id} }, $form->{"${item}_tax_id"});
1717 if ( $form->{"${item}_tax_id"} ) {
1718 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"};
1719 my $description = $tax_obj ? $tax_obj->translated_attribute('taxdescription', $form->{language_id}, 0) : '';
1720 push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $description . q{ } . 100 * $form->{"${item}_rate"} . q{%});
1724 $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
1725 $form->{discount_total} = $form->format_amount($myconfig, $form->{discount_total}, 2);
1726 $form->{nodiscount} = $form->format_amount($myconfig, $nodiscount, 2);
1727 $form->{yesdiscount} = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
1729 if($form->{taxincluded}) {
1730 $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal} - $tax, 2);
1731 $form->{subtotal_nofmt} = $form->{ordtotal} - $tax;
1733 $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
1734 $form->{subtotal_nofmt} = $form->{ordtotal};
1737 my $grossamount = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
1738 $form->{ordtotal} = $form->round_amount( $grossamount, 2, 1);
1739 $form->{rounding} = $form->round_amount(
1740 $form->{ordtotal} - $form->round_amount($grossamount, 2),
1745 $form->{rounding} = $form->format_amount($myconfig, $form->{rounding}, 2);
1746 $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
1748 $form->set_payment_options($myconfig, $form->{$form->{type} =~ /_quotation/ ? 'quodate' : 'orddate'}, $form->{type});
1750 $form->{username} = $myconfig->{name};
1752 $form->{department} = SL::DB::Manager::Department->find_by(id => $form->{department_id})->description if $form->{department_id};
1753 $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
1754 $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
1756 $form->{order} = SL::DB::Manager::Order->find_by(id => $form->{id}) if $form->{id};
1757 $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) for @separate_totals;
1759 $main::lxdebug->leave_sub();
1768 OE.pm - Order entry module
1772 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>.
1778 =item retrieve_simple PARAMS
1780 simple OE retrieval by id. does not look up customer, vendor, units or any other stuff. only oe and orderitems.
1782 my $order = retrieve_simple(id => 2);