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 ?';
337 SELECT files.id FROM files LEFT JOIN file_full_texts ON (file_full_texts.file_id = files.id)
338 WHERE files.object_id = o.id AND files.object_type = 'sales_order'
339 AND file_full_texts.full_text ILIKE ?)
344 SELECT notes.id FROM notes
345 WHERE notes.trans_id = o.id AND notes.trans_module LIKE 'oe'
346 AND (notes.subject ILIKE ? OR notes.body ILIKE ?))
351 SELECT follow_up_links.id FROM follow_up_links
352 WHERE follow_up_links.trans_id = o.id AND trans_type = 'sales_order'
354 SELECT notes.id FROM notes
355 WHERE trans_module LIKE 'fu' AND trans_id = follow_up_links.follow_up_id
356 AND (notes.subject ILIKE ? OR notes.body ILIKE ?)))
361 push(@values, like($form->{fulltext})) for 1 .. (scalar @fulltext_fields) + 5;
364 if ($form->{parts_partnumber}) {
367 SELECT orderitems.trans_id
369 LEFT JOIN parts ON (orderitems.parts_id = parts.id)
370 WHERE (orderitems.trans_id = o.id)
371 AND (parts.partnumber ILIKE ?)
375 push @values, like($form->{parts_partnumber});
378 if ($form->{parts_description}) {
381 SELECT orderitems.trans_id
383 WHERE (orderitems.trans_id = o.id)
384 AND (orderitems.description ILIKE ?)
388 push @values, like($form->{parts_description});
392 my @tokens = parse_line('\s+', 0, $form->{all});
393 # ordnumber quonumber customer.name vendor.name transaction_description
395 o.ordnumber ILIKE ? OR
396 o.quonumber ILIKE ? OR
398 o.transaction_description ILIKE ?
400 push @values, (like($_))x4 for @tokens;
403 my ($cvar_where, @cvar_values) = CVar->build_filter_query('module' => 'CT',
404 'trans_id_field' => 'ct.id',
408 $query .= qq| AND ($cvar_where)|;
409 push @values, @cvar_values;
412 my $sortdir = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
413 my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name"), "o.itime"));
414 my %allowed_sort_columns = (
415 "transdate" => "o.transdate",
416 "reqdate" => "o.reqdate",
418 "ordnumber" => "o.ordnumber",
419 "cusordnumber" => "o.cusordnumber",
420 "quonumber" => "o.quonumber",
422 "employee" => "e.name",
423 "salesman" => "s.name",
424 "shipvia" => "o.shipvia",
425 "transaction_description" => "o.transaction_description",
426 "shippingpoint" => "o.shippingpoint",
427 "insertdate" => "o.itime",
428 "taxzone" => "tz.description",
429 "payment_terms" => "pt.description",
430 "department" => "department.description",
431 "intnotes" => "o.intnotes",
433 if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
434 $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}" . ", o.itime ${sortdir}";
436 $query .= qq| ORDER by | . $sortorder;
438 my $sth = $dbh->prepare($query);
439 $sth->execute(@values) ||
440 $form->dberror($query . " (" . join(", ", @values) . ")");
444 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
445 $ref->{billed_amount} = $billed_amount{$ref->{id}};
446 $ref->{billed_netamount} = $billed_netamount{$ref->{id}};
447 if ($ref->{billed_amount} < 0) { # case: credit note(s) higher than invoices
448 $ref->{remaining_amount} = $ref->{amount} + $ref->{billed_amount};
449 $ref->{remaining_netamount} = $ref->{netamount} + $ref->{billed_netamount};
451 $ref->{remaining_amount} = $ref->{amount} - $ref->{billed_amount};
452 $ref->{remaining_netamount} = $ref->{netamount} - $ref->{billed_netamount};
454 $ref->{exchangerate} ||= $ref->{daily_exchangerate};
455 $ref->{exchangerate} ||= 1;
456 push @{ $form->{OE} }, $ref if $ref->{id} != $id{ $ref->{id} };
457 $id{ $ref->{id} } = $ref->{id};
462 $main::lxdebug->leave_sub();
465 sub transactions_for_todo_list {
466 $main::lxdebug->enter_sub();
471 my $myconfig = \%main::myconfig;
472 my $form = $main::form;
474 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
476 my $query = qq|SELECT id FROM employee WHERE login = ?|;
477 my ($e_id) = selectrow_query($form, $dbh, $query, $::myconfig{login});
480 qq|SELECT oe.id, oe.transdate, oe.reqdate, oe.quonumber, oe.transaction_description, oe.amount,
481 CASE WHEN (COALESCE(oe.customer_id, 0) = 0) THEN 'vendor' ELSE 'customer' END AS vc,
486 LEFT JOIN customer c ON (oe.customer_id = c.id)
487 LEFT JOIN vendor v ON (oe.vendor_id = v.id)
488 LEFT JOIN employee e ON (oe.employee_id = e.id)
489 WHERE (COALESCE(quotation, FALSE) = TRUE)
490 AND (COALESCE(closed, FALSE) = FALSE)
491 AND ((oe.employee_id = ?) OR (oe.salesman_id = ?))
492 AND NOT (oe.reqdate ISNULL)
493 AND (oe.reqdate < current_date)
496 my $quotations = selectall_hashref_query($form, $dbh, $query, $e_id, $e_id);
498 $main::lxdebug->leave_sub();
504 my ($self, $myconfig, $form) = @_;
505 $main::lxdebug->enter_sub();
507 my $rc = SL::DB->client->with_transaction(\&_save, $self, $myconfig, $form);
509 $::lxdebug->leave_sub;
515 $main::lxdebug->enter_sub();
517 my ($self, $myconfig, $form) = @_;
519 my $dbh = SL::DB->client->dbh;
520 my $restricter = SL::HTML::Restrict->create;
522 my ($query, @values, $sth, $null);
523 my $exchangerate = 0;
525 my $all_units = AM->retrieve_units($myconfig, $form);
526 $form->{all_units} = $all_units;
528 my $ic_cvar_configs = CVar->get_configs(module => 'IC',
531 $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id};
532 unless ($form->{employee_id}) {
533 $form->get_employee($dbh);
536 my $ml = ($form->{type} eq 'sales_order') ? 1 : -1;
538 my $number_field = $form->{type} =~ m{order} ? 'ordnumber' : 'quonumber';
539 my $trans_number = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{$number_field}, id => $form->{id});
540 $form->{$number_field} ||= $trans_number->create_unique; # set $form->{ordnumber} or $form->{quonumber}
541 my $is_new = !$form->{id};
544 $query = qq|DELETE FROM custom_variables
545 WHERE (config_id IN (SELECT id FROM custom_variable_configs WHERE (module = 'ShipTo')))
546 AND (trans_id IN (SELECT shipto_id FROM shipto WHERE (module = 'OE') AND (trans_id = ?)))|;
547 do_query($form, $dbh, $query, $form->{id});
549 $query = qq|DELETE FROM shipto | .
550 qq|WHERE trans_id = ? AND module = 'OE'|;
551 do_query($form, $dbh, $query, $form->{id});
555 $query = qq|SELECT nextval('id')|;
556 ($form->{id}) = selectrow_query($form, $dbh, $query);
558 $query = qq|INSERT INTO oe (id, ordnumber, employee_id, currency_id, taxzone_id) VALUES (?, '', ?, (SELECT currency_id FROM defaults), ?)|;
559 do_query($form, $dbh, $query, $form->{id}, $form->{employee_id}, $form->{taxzone_id});
576 my @processed_orderitems;
578 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
579 my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
582 for my $i (1 .. $form->{rowcount}) {
584 map({ $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) } qw(qty ship));
586 if ($form->{"id_$i"}) {
589 $query = qq|SELECT unit FROM parts WHERE id = ?|;
590 my ($item_unit) = selectrow_query($form, $dbh, $query, $form->{"id_$i"});
593 if (defined($all_units->{$item_unit}->{factor}) &&
594 (($all_units->{$item_unit}->{factor} * 1) != 0)) {
595 $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
597 my $baseqty = $form->{"qty_$i"} * $basefactor;
599 $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
600 $form->{"marge_absolut_$i"} = $form->parse_amount($myconfig, $form->{"marge_absolut_$i"}) * 1;
602 $form->{"lastcost_$i"} = $form->parse_amount($myconfig, $form->{"lastcost_$i"});
604 # keep entered selling price
606 $form->parse_amount($myconfig, $form->{"sellprice_$i"});
608 my ($dec) = ($fxsellprice =~ /\.(\d+)/);
610 my $decimalplaces = ($dec > 2) ? $dec : 2;
612 # undo discount formatting
613 $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
616 $form->{"sellprice_$i"} = $fxsellprice * (1 - $form->{"discount_$i"});
618 # round linetotal at least to 2 decimal places
619 $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
620 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
622 $form->{"inventory_accno_$i"} *= 1;
623 $form->{"expense_accno_$i"} *= 1;
625 @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
629 map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
631 if ($form->{taxincluded}) {
632 $taxamount = $linetotal * $taxrate / (1 + $taxrate);
633 $taxbase = $linetotal - $taxamount;
635 # we are not keeping a natural price, do not round
636 $form->{"sellprice_$i"} =
637 $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
639 $taxamount = $linetotal * $taxrate;
640 $taxbase = $linetotal;
643 if ($form->round_amount($taxrate, 7) == 0) {
644 if ($form->{taxincluded}) {
645 foreach my $item (@taxaccounts) {
646 $taxamount = $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
647 $taxaccounts{$item} += $taxamount;
648 $taxdiff += $taxamount;
649 $taxbase{$item} += $taxbase;
651 $taxaccounts{ $taxaccounts[0] } += $taxdiff;
653 foreach my $item (@taxaccounts) {
654 $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
655 $taxbase{$item} += $taxbase;
659 foreach my $item (@taxaccounts) {
660 $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
661 $taxbase{$item} += $taxbase;
665 $netamount += $form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor;
667 $reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef;
669 # Get pricegroup_id and save it. Unfortunately the interface
670 # also uses ID "0" for signalling that none is selected, but "0"
671 # must not be stored in the database. Therefore we cannot simply
673 ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
675 $pricegroup_id = undef if !$pricegroup_id;
677 # force new project, if not set yet
678 if ($::instance_conf->get_order_always_project && !$form->{"globalproject_id"} && ($form->{type} eq 'sales_order')) {
679 require SL::DB::Customer;
680 my $customer = SL::DB::Manager::Customer->find_by(id => $form->{customer_id});
681 die "Can't find customer" unless $customer;
682 die $main::locale->text("Error while creating project with project number of new order number, project number #1 already exists!", $form->{ordnumber})
683 if SL::DB::Manager::Project->find_by(projectnumber => $form->{ordnumber});
685 my $new_project = SL::DB::Project->new(
686 projectnumber => $form->{ordnumber},
687 description => $customer->name,
688 customer_id => $customer->id,
690 project_type_id => $::instance_conf->get_project_type_id,
691 project_status_id => $::instance_conf->get_project_status_id,
694 $form->{"globalproject_id"} = $new_project->id;
697 CVar->get_non_editable_ic_cvars(form => $form,
700 sub_module => 'orderitems',
701 may_converted_from => ['orderitems', 'invoice']);
705 # save detail record in orderitems table
706 if (! $form->{"orderitems_id_$i"}) {
707 $query = qq|SELECT nextval('orderitemsid')|;
708 ($form->{"orderitems_id_$i"}) = selectrow_query($form, $dbh, $query);
710 $query = qq|INSERT INTO orderitems (id, position) VALUES (?, ?)|;
711 do_query($form, $dbh, $query, $form->{"orderitems_id_$i"}, conv_i($position));
714 my $orderitems_id = $form->{"orderitems_id_$i"};
715 push @processed_orderitems, $orderitems_id;
718 UPDATE orderitems SET
719 trans_id = ?, position = ?, parts_id = ?, description = ?, longdescription = ?, qty = ?, base_qty = ?,
720 sellprice = ?, discount = ?, unit = ?, reqdate = ?, project_id = ?, serialnumber = ?, ship = ?,
721 pricegroup_id = ?, subtotal = ?,
722 marge_percent = ?, marge_total = ?, lastcost = ?, price_factor_id = ?,
723 active_price_source = ?, active_discount_source = ?,
724 price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?
728 conv_i($form->{id}), conv_i($position), conv_i($form->{"id_$i"}),
729 $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}),
730 $form->{"qty_$i"}, $baseqty,
731 $fxsellprice, $form->{"discount_$i"},
732 $form->{"unit_$i"}, conv_date($reqdate), conv_i($form->{"project_id_$i"}),
733 $form->{"serialnumber_$i"}, $form->{"ship_$i"},
734 $pricegroup_id, $form->{"subtotal_$i"} ? 't' : 'f',
735 $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
736 $form->{"lastcost_$i"}, conv_i($form->{"price_factor_id_$i"}),
737 $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"},
738 conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}),
739 conv_i($orderitems_id),
742 do_query($form, $dbh, $query, @values);
744 $form->{"sellprice_$i"} = $fxsellprice;
745 $form->{"discount_$i"} *= 100;
747 CVar->save_custom_variables(module => 'IC',
748 sub_module => 'orderitems',
749 trans_id => $orderitems_id,
750 configs => $ic_cvar_configs,
752 name_prefix => 'ic_',
753 name_postfix => "_$i",
756 # link previous items with orderitems
757 # assume we have a new workflow if we link from invoice or order to quotation
758 # unluckily orderitems are used for quotation and orders - therefore one more
759 # check to be sure NOT to link from order to quotation
760 foreach (qw(orderitems)) {
761 if (!$form->{saveasnew} && !$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}
762 && $form->{type} !~ 'quotation') {
763 RecordLinks->create_links('dbh' => $dbh,
766 'from_ids' => $form->{"converted_from_${_}_id_$i"},
767 'to_table' => 'orderitems',
768 'to_id' => $orderitems_id,
771 delete $form->{"converted_from_${_}_id_$i"};
776 # search for orphaned ids
777 $query = sprintf 'SELECT id FROM orderitems WHERE trans_id = ? AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_orderitems;
778 @values = (conv_i($form->{id}), map { conv_i($_) } @processed_orderitems);
779 my @orphaned_ids = map { $_->{id} } selectall_hashref_query($form, $dbh, $query, @values);
781 if (scalar @orphaned_ids) {
782 # clean up orderitems
783 $query = sprintf 'DELETE FROM orderitems WHERE id IN (%s)', join ', ', ("?") x scalar @orphaned_ids;
784 do_query($form, $dbh, $query, @orphaned_ids);
787 $reqdate = ($form->{reqdate}) ? $form->{reqdate} : undef;
791 map { $tax += $form->round_amount($taxaccounts{$_}, 2) } keys %taxaccounts;
793 $amount = $form->round_amount($netamount + $tax, 2, 1);
794 $netamount = $form->round_amount($netamount, 2);
796 if ($form->{currency} eq $form->{defaultcurrency}) {
797 $form->{exchangerate} = 1;
799 $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? 'buy' : 'sell');
802 # from inputfield (exchangerate) or hidden (forex)
803 my $exchangerate_from_form = $form->{forex} || $form->parse_amount($myconfig, $form->{exchangerate});
805 $form->{exchangerate} = $exchangerate || $exchangerate_from_form;
807 my $quotation = $form->{type} =~ /_order$/ ? 'f' : 't';
812 ordnumber = ?, quonumber = ?, cusordnumber = ?, transdate = ?, vendor_id = ?,
813 customer_id = ?, amount = ?, netamount = ?, reqdate = ?, tax_point = ?, taxincluded = ?,
814 shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, currency_id = (SELECT id FROM currencies WHERE name=?), closed = ?,
815 delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
816 taxzone_id = ?, shipto_id = ?, billing_address_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,delivery_term_id = ?,
817 globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
818 , order_probability = ?, expected_billing_date = ?
821 @values = ($form->{ordnumber} || '', $form->{quonumber},
822 $form->{cusordnumber}, conv_date($form->{transdate}),
823 conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
824 $amount, $netamount, conv_date($reqdate), conv_date($form->{tax_point}),
825 $form->{taxincluded} ? 't' : 'f', $form->{shippingpoint},
826 $form->{shipvia}, $restricter->process($form->{notes}), $form->{intnotes},
827 $form->{currency}, $form->{closed} ? 't' : 'f',
828 $form->{delivered} ? "t" : "f", $form->{proforma} ? 't' : 'f',
829 $quotation, conv_i($form->{department_id}),
830 conv_i($form->{language_id}), conv_i($form->{taxzone_id}),
831 conv_i($form->{shipto_id}), conv_i($form->{billing_address_id}), conv_i($form->{payment_id}),
832 conv_i($form->{delivery_vendor_id}),
833 conv_i($form->{delivery_customer_id}),
834 conv_i($form->{delivery_term_id}),
835 conv_i($form->{globalproject_id}), conv_i($form->{employee_id}),
836 conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
837 $form->{transaction_description},
838 $form->{marge_total} * 1, $form->{marge_percent} * 1,
839 $form->{order_probability} * 1, conv_date($form->{expected_billing_date}),
840 conv_i($form->{id}));
841 do_query($form, $dbh, $query, @values);
843 $form->new_lastmtime('oe');
845 $form->{ordtotal} = $amount;
847 $form->{name} = $form->{ $form->{vc} };
848 $form->{name} =~ s/--\Q$form->{"$form->{vc}_id"}\E//;
851 if (!$form->{shipto_id}) {
852 $form->add_shipto($dbh, $form->{id}, "OE");
855 # save printed, emailed, queued
856 $form->save_status($dbh);
858 # Link this record to the records it was created from.
859 $form->{convert_from_oe_ids} =~ s/^\s+//;
860 $form->{convert_from_oe_ids} =~ s/\s+$//;
861 my @convert_from_oe_ids = split m/\s+/, $form->{convert_from_oe_ids};
862 delete $form->{convert_from_oe_ids};
863 if (!$form->{useasnew} && scalar @convert_from_oe_ids) {
864 RecordLinks->create_links('dbh' => $dbh,
866 'from_table' => 'oe',
867 'from_ids' => \@convert_from_oe_ids,
869 'to_id' => $form->{id},
871 $self->_close_quotations_rfqs('dbh' => $dbh,
872 'from_id' => \@convert_from_oe_ids,
873 'to_id' => $form->{id});
876 if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
877 if ($form->{vc} eq 'customer') {
878 $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
880 if ($form->{vc} eq 'vendor') {
881 $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
885 $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ?
886 "quonumber" : "ordnumber"};
888 Common::webdav_folder($form);
890 $self->save_periodic_invoices_config(dbh => $dbh,
891 oe_id => $form->{id},
892 config_yaml => $form->{periodic_invoices_config})
893 if ($form->{type} eq 'sales_order');
895 $self->_link_created_sales_order_to_requirement_specs_for_sales_quotations(
896 type => $form->{type},
897 converted_from_ids => \@convert_from_oe_ids,
898 sales_order_id => $form->{id},
902 $self->_set_project_in_linked_requirement_spec(
903 type => $form->{type},
904 project_id => $form->{globalproject_id},
905 sales_order_id => $form->{id},
908 $main::lxdebug->leave_sub();
913 sub _link_created_sales_order_to_requirement_specs_for_sales_quotations {
914 my ($self, %params) = @_;
916 # If this is a sales order created from a sales quotation and if
917 # that sales quotation was created from a requirement spec document
918 # then link the newly created sales order to the requirement spec
921 return if !$params{is_new};
922 return if $params{type} ne 'sales_order';
923 return if !@{ $params{converted_from_ids} };
925 my $oe_objects = SL::DB::Manager::Order->get_all(where => [ id => $params{converted_from_ids} ]);
926 my @sales_quotations = grep { $_->is_type('sales_quotation') } @{ $oe_objects };
928 return if !@sales_quotations;
930 my $rs_orders = SL::DB::Manager::RequirementSpecOrder->get_all(where => [ order_id => [ map { $_->id } @sales_quotations ] ]);
932 return if !@{ $rs_orders };
934 $rs_orders->[0]->db->with_transaction(sub {
935 foreach my $rs_order (@{ $rs_orders }) {
936 SL::DB::RequirementSpecOrder->new(
937 order_id => $params{sales_order_id},
938 requirement_spec_id => $rs_order->requirement_spec_id,
939 version_id => $rs_order->version_id,
947 sub _set_project_in_linked_requirement_spec {
948 my ($self, %params) = @_;
950 return if $params{type} ne 'sales_order';
951 return if !$params{project_id} || !$params{sales_order_id};
954 UPDATE requirement_specs
957 SELECT so.requirement_spec_id
958 FROM requirement_spec_orders so
959 WHERE so.order_id = ?
963 do_query($::form, $::form->get_standard_dbh, $query, $params{project_id}, $params{sales_order_id});
966 sub save_periodic_invoices_config {
967 my ($self, %params) = @_;
969 return if !$params{oe_id};
971 my $config = $params{config_yaml} ? SL::YAML::Load($params{config_yaml}) : undef;
972 return if 'HASH' ne ref $config;
974 my $obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $params{oe_id})
975 || SL::DB::PeriodicInvoicesConfig->new(oe_id => $params{oe_id});
976 $obj->update_attributes(%{ $config });
979 sub load_periodic_invoice_config {
983 delete $form->{periodic_invoices_config};
986 my $config_obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $form->{id});
989 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
990 print printer_id copies direct_debit send_email email_recipient_contact_id email_recipient_address email_sender email_subject email_body) };
991 $form->{periodic_invoices_config} = SL::YAML::Dump($config);
996 sub _close_quotations_rfqs {
997 $main::lxdebug->enter_sub();
1002 Common::check_params(\%params, qw(from_id to_id));
1004 my $myconfig = \%main::myconfig;
1005 my $form = $main::form;
1007 my $dbh = $params{dbh} || SL::DB->client->dbh;
1009 SL::DB->client->with_transaction(sub {
1011 my $query = qq|SELECT quotation FROM oe WHERE id = ?|;
1012 my $sth = prepare_query($form, $dbh, $query);
1014 do_statement($form, $sth, $query, conv_i($params{to_id}));
1016 my ($quotation) = $sth->fetchrow_array();
1024 foreach my $from_id (@{ $params{from_id} }) {
1025 $from_id = conv_i($from_id);
1026 do_statement($form, $sth, $query, $from_id);
1027 ($quotation) = $sth->fetchrow_array();
1028 push @close_ids, $from_id if ($quotation);
1033 if (scalar @close_ids) {
1034 $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_ids) . qq|)|;
1035 do_query($form, $dbh, $query, @close_ids);
1038 }) or do { die SL::DB->client->error };
1040 $main::lxdebug->leave_sub();
1044 $main::lxdebug->enter_sub();
1046 my ($self, $myconfig, $form) = @_;
1048 my $rc = SL::DB::Order->new->db->with_transaction(sub {
1049 my @spoolfiles = grep { $_ } map { $_->spoolfile } @{ SL::DB::Manager::Status->get_all(where => [ trans_id => $form->{id} ]) };
1051 SL::DB::Order->new(id => $form->{id})->delete;
1053 my $spool = $::lx_office_conf{paths}->{spool};
1054 unlink map { "$spool/$_" } @spoolfiles if $spool;
1057 }) or do { die SL::DB->client->error };
1059 $main::lxdebug->leave_sub();
1065 my ($self, $myconfig, $form) = @_;
1066 $main::lxdebug->enter_sub();
1068 my $rc = SL::DB->client->with_transaction(\&_retrieve, $self, $myconfig, $form);
1070 $::lxdebug->leave_sub;
1075 my ($self, $myconfig, $form) = @_;
1077 # connect to database
1078 my $dbh = SL::DB->client->dbh;
1080 my ($query, $query_add, @values, @ids, $sth);
1082 # translate the ids (given by id_# and trans_id_#) into one array of ids, so we can join them later
1084 push @ids, $form->{"trans_id_$_"}
1085 if ($form->{"multi_id_$_"} and $form->{"trans_id_$_"})
1086 } (1 .. $form->{"rowcount"});
1088 if ($form->{rowcount} && scalar @ids) {
1089 $form->{convert_from_oe_ids} = join ' ', @ids;
1092 # if called in multi id mode, and still only got one id, switch back to single id
1093 if ($form->{"rowcount"} and $#ids == 0) {
1094 $form->{"id"} = $ids[0];
1096 delete $form->{convert_from_oe_ids};
1099 # and remember for the rest of the function
1100 my $is_collective_order = scalar @ids;
1102 # If collective order was created from exactly 1 order, we assume the same
1103 # behaviour as a "save as new" from within an order is actually desired, i.e.
1104 # the original order isn't part of a workflow where we want to remember
1105 # record_links, but simply a quick way of generating a new order from an old
1106 # one without having to enter everything again.
1107 # Setting useasnew will prevent the creation of record_links for the items
1108 # when saving the new order.
1109 # This form variable is probably not necessary, could just set saveasnew instead
1110 $form->{useasnew} = 1 if $is_collective_order == 1;
1113 my $extra_days = $form->{type} eq 'sales_quotation' ? $::instance_conf->get_reqdate_interval :
1114 $form->{type} eq 'sales_order' ? $::instance_conf->get_delivery_date_interval : 1;
1115 if ( ($form->{type} eq 'sales_order' && !$::instance_conf->get_deliverydate_on)
1116 || ($form->{type} eq 'sales_quotation' && !$::instance_conf->get_reqdate_on)) {
1117 $form->{reqdate} = '';
1119 $form->{reqdate} = DateTime->today_local->next_workday(extra_days => $extra_days)->to_kivitendo;
1121 $form->{transdate} = DateTime->today_local->to_kivitendo;
1124 # get default accounts
1125 $query = qq|SELECT (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
1126 (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id) AS income_accno,
1127 (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id) AS expense_accno,
1128 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
1129 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
1130 (SELECT c.accno FROM chart c WHERE d.rndgain_accno_id = c.id) AS rndgain_accno,
1131 (SELECT c.accno FROM chart c WHERE d.rndloss_accno_id = c.id) AS rndloss_accno
1134 my $ref = selectfirst_hashref_query($form, $dbh, $query);
1135 map { $form->{$_} = $ref->{$_} } keys %$ref;
1137 $form->{currency} = $form->get_default_currency($myconfig);
1139 # set reqdate if this is an invoice->order conversion. If someone knows a better check to ensure
1140 # we come from invoices, feel free.
1141 $form->{reqdate} = $form->{deliverydate}
1142 if ( $form->{deliverydate}
1143 and $form->{callback} =~ /action=ar_transactions/);
1145 my $vc = $form->{vc} eq "customer" ? "customer" : "vendor";
1147 if ($form->{id} or @ids) {
1149 # retrieve order for single id
1150 # NOTE: this query is intended to fetch all information only ONCE.
1151 # so if any of these infos is important (or even different) for any item,
1152 # it will be killed out and then has to be fetched from the item scope query further down
1154 qq|SELECT o.cp_id, o.ordnumber, o.transdate, o.reqdate,
1155 o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
1156 (SELECT cu.name FROM currencies cu WHERE cu.id=o.currency_id) AS currency, e.name AS employee, o.employee_id, o.salesman_id,
1157 o.${vc}_id, cv.name AS ${vc}, o.amount AS invtotal,
1158 o.closed, o.reqdate, o.tax_point, o.quonumber, o.department_id, o.cusordnumber,
1160 d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
1161 o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id, o.billing_address_id,
1162 o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id,
1163 o.itime::DATE AS insertdate, o.order_probability, o.expected_billing_date
1165 JOIN ${vc} cv ON (o.${vc}_id = cv.id)
1166 LEFT JOIN employee e ON (o.employee_id = e.id)
1167 LEFT JOIN department d ON (o.department_id = d.id) | .
1170 : "WHERE o.id IN (" . join(', ', map("? ", @ids)) . ")"
1172 @values = $form->{id} ? ($form->{id}) : @ids;
1173 $sth = prepare_execute_query($form, $dbh, $query, @values);
1175 $ref = $sth->fetchrow_hashref("NAME_lc");
1178 map { $form->{$_} = $ref->{$_} } keys %$ref;
1180 $form->{saved_xyznumber} = $form->{$form->{type} =~ /_quotation$/ ? "quonumber" : "ordnumber"};
1182 # set all entries for multiple ids blank that yield different information
1183 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1184 map { $form->{$_} = '' if ($ref->{$_} ne $form->{$_}) } keys %$ref;
1187 $form->{mtime} ||= $form->{itime};
1188 $form->{lastmtime} = $form->{mtime};
1190 # if not given, fill transdate with current_date
1191 $form->{transdate} = $form->current_date($myconfig)
1192 unless $form->{transdate};
1196 if ($form->{delivery_customer_id}) {
1197 $query = qq|SELECT name FROM customer WHERE id = ?|;
1198 ($form->{delivery_customer_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_customer_id});
1201 if ($form->{delivery_vendor_id}) {
1202 $query = qq|SELECT name FROM customer WHERE id = ?|;
1203 ($form->{delivery_vendor_string}) = selectrow_query($form, $dbh, $query, $form->{delivery_vendor_id});
1206 # shipto and pinted/mailed/queued status makes only sense for single id retrieve
1208 $query = qq|SELECT s.* FROM shipto s WHERE s.trans_id = ? AND s.module = 'OE'|;
1209 $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
1211 $ref = $sth->fetchrow_hashref("NAME_lc");
1212 $form->{$_} = $ref->{$_} for grep { m{^shipto(?!_id$)} } keys %$ref;
1215 if ($ref->{shipto_id}) {
1216 my $cvars = CVar->get_custom_variables(
1219 trans_id => $ref->{shipto_id},
1221 $form->{"shiptocvar_$_->{name}"} = $_->{value} for @{ $cvars };
1224 # get printed, emailed and queued
1225 $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
1226 $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
1228 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1229 $form->{printed} .= "$ref->{formname} " if $ref->{printed};
1230 $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
1231 $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
1234 map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
1237 my $transdate = $form->{tax_point} ? $dbh->quote($form->{tax_point}) : $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
1239 $form->{taxzone_id} = 0 unless ($form->{taxzone_id});
1240 unshift @values, ($form->{taxzone_id}) x 2;
1242 # retrieve individual items
1243 # this query looks up all information about the items
1244 # stuff different from the whole will not be overwritten, but saved with a suffix.
1246 qq|SELECT o.id AS orderitems_id,
1247 c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from as inventory_valid,
1248 c2.accno AS income_accno, c2.new_chart_id AS income_new_chart, date($transdate) - c2.valid_from as income_valid,
1249 c3.accno AS expense_accno, c3.new_chart_id AS expense_new_chart, date($transdate) - c3.valid_from as expense_valid,
1250 oe.ordnumber AS ordnumber_oe, oe.transdate AS transdate_oe, oe.cusordnumber AS cusordnumber_oe,
1251 p.partnumber, p.part_type, p.listprice, o.description, o.qty,
1252 p.classification_id,
1253 o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.part_type,
1254 o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
1255 o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
1256 o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source, o.active_discount_source,
1257 pr.projectnumber, p.formel,
1258 pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
1260 JOIN parts p ON (o.parts_id = p.id)
1261 JOIN oe ON (o.trans_id = oe.id)
1262 LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c1.id)
1263 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)
1264 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)
1265 LEFT JOIN project pr ON (o.project_id = pr.id)
1266 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
1268 ? qq|WHERE o.trans_id = ?|
1269 : qq|WHERE o.trans_id IN (| . join(", ", map("?", @ids)) . qq|)|) .
1270 qq|ORDER BY o.trans_id, o.position|;
1272 @ids = $form->{id} ? ($form->{id}) : @ids;
1273 $sth = prepare_execute_query($form, $dbh, $query, @values);
1275 while ($ref = $sth->fetchrow_hashref("NAME_lc")) {
1276 # Retrieve custom variables.
1277 my $cvars = CVar->get_custom_variables(dbh => $dbh,
1279 sub_module => 'orderitems',
1280 trans_id => $ref->{orderitems_id},
1282 map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
1285 if (!$ref->{"part_type"} eq 'part') {
1286 map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid));
1288 # delete($ref->{"part_inventory_accno_id"});
1290 # in collective order, copy global ordnumber, transdate, cusordnumber into item scope
1291 # unless already present there
1292 # remove _oe entries afterwards
1293 map { $ref->{$_} = $ref->{"${_}_oe"} if ($ref->{$_} eq '') }
1294 qw|ordnumber transdate cusordnumber|
1296 map { delete $ref->{$_} } qw|ordnumber_oe transdate_oe cusordnumber_oe|;
1300 while ($ref->{inventory_new_chart} && ($ref->{inventory_valid} >= 0)) {
1302 qq|SELECT accno AS inventory_accno, | .
1303 qq| new_chart_id AS inventory_new_chart, | .
1304 qq| date($transdate) - valid_from AS inventory_valid | .
1305 qq|FROM chart WHERE id = $ref->{inventory_new_chart}|;
1306 ($ref->{inventory_accno}, $ref->{inventory_new_chart},
1307 $ref->{inventory_valid}) = selectrow_query($form, $dbh, $query);
1310 while ($ref->{income_new_chart} && ($ref->{income_valid} >= 0)) {
1312 qq|SELECT accno AS income_accno, | .
1313 qq| new_chart_id AS income_new_chart, | .
1314 qq| date($transdate) - valid_from AS income_valid | .
1315 qq|FROM chart WHERE id = $ref->{income_new_chart}|;
1316 ($ref->{income_accno}, $ref->{income_new_chart},
1317 $ref->{income_valid}) = selectrow_query($form, $dbh, $query);
1320 while ($ref->{expense_new_chart} && ($ref->{expense_valid} >= 0)) {
1322 qq|SELECT accno AS expense_accno, | .
1323 qq| new_chart_id AS expense_new_chart, | .
1324 qq| date($transdate) - valid_from AS expense_valid | .
1325 qq|FROM chart WHERE id = $ref->{expense_new_chart}|;
1326 ($ref->{expense_accno}, $ref->{expense_new_chart},
1327 $ref->{expense_valid}) = selectrow_query($form, $dbh, $query);
1330 # delete orderitems_id in collective orders, so that they get cloned no matter what
1331 # is this correct? or is the following meant?
1332 # remember orderitems_ids in converted_from_orderitems_ids, so that they may be linked
1333 $ref->{converted_from_orderitems_id} = delete $ref->{orderitems_id} if $is_collective_order;
1335 # get tax rates and description
1336 my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1338 qq|SELECT c.accno, t.taxdescription, t.rate, t.id as tax_id, c.accno as taxnumber | .
1340 qq|LEFT JOIN chart c on (c.id = t.chart_id) | .
1341 qq|WHERE t.id IN (SELECT tk.tax_id FROM taxkeys tk | .
1342 qq| WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?) | .
1343 qq| AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1) | .
1344 qq|ORDER BY c.accno|;
1345 my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
1346 $ref->{taxaccounts} = "";
1348 while (my $ptr = $stw->fetchrow_hashref("NAME_lc")) {
1349 if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1353 $ref->{taxaccounts} .= "$ptr->{accno} ";
1354 if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
1355 $form->{"$ptr->{accno}_rate"} = $ptr->{rate};
1356 $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1357 $form->{"$ptr->{accno}_taxnumber"} = $ptr->{taxnumber};
1358 $form->{"$ptr->{accno}_tax_id"} = $ptr->{tax_id};
1359 $form->{taxaccounts} .= "$ptr->{accno} ";
1364 chop $ref->{taxaccounts};
1366 push @{ $form->{form_details} }, $ref;
1373 # get last name used
1374 $form->lastname_used($dbh, $myconfig, $form->{vc})
1375 unless $form->{"$form->{vc}_id"};
1379 $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? "buy" : "sell");
1381 Common::webdav_folder($form);
1383 $self->load_periodic_invoice_config($form);
1388 sub retrieve_simple {
1389 $main::lxdebug->enter_sub();
1394 Common::check_params(\%params, qw(id));
1396 my $myconfig = \%main::myconfig;
1397 my $form = $main::form;
1399 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
1401 my $oe_query = qq|SELECT * FROM oe WHERE id = ?|;
1402 my $oi_query = qq|SELECT * FROM orderitems WHERE trans_id = ? ORDER BY position|;
1404 my $order = selectfirst_hashref_query($form, $dbh, $oe_query, conv_i($params{id}));
1405 $order->{orderitems} = selectall_hashref_query( $form, $dbh, $oi_query, conv_i($params{id}));
1407 $main::lxdebug->leave_sub();
1413 $main::lxdebug->enter_sub();
1415 my ($self, $myconfig, $form) = @_;
1417 # connect to database
1418 my $dbh = SL::DB->client->dbh;
1424 my $nodiscount_subtotal = 0;
1425 my $discount_subtotal = 0;
1428 my @partsgroup = ();
1431 my $subtotal_header = 0;
1432 my $subposition = 0;
1440 push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
1442 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
1445 foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
1446 $price_factors{$pfac->{id}} = $pfac;
1447 $pfac->{factor} *= 1;
1448 $pfac->{formatted_factor} = $form->format_amount($myconfig, $pfac->{factor});
1451 # sort items by partsgroup
1452 for $i (1 .. $form->{rowcount}) {
1454 if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
1455 $partsgroup = $form->{"partsgroup_$i"};
1457 push @partsgroup, [$i, $partsgroup];
1458 push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
1464 $projects = SL::DB::Manager::Project->get_all(query => [ id => \@project_ids ]);
1465 %projects_by_id = map { $_->id => $_ } @$projects;
1468 if ($projects_by_id{$form->{"globalproject_id"}}) {
1469 $form->{globalprojectnumber} = $projects_by_id{$form->{"globalproject_id"}}->projectnumber;
1470 $form->{globalprojectdescription} = $projects_by_id{$form->{"globalproject_id"}}->description;
1472 for (@{ $projects_by_id{$form->{"globalproject_id"}}->cvars_by_config }) {
1473 $form->{"project_cvar_" . $_->config->name} = $_->value_as_text;
1477 $form->{discount} = [];
1479 # get some values of parts from db on store them in extra array,
1480 # so that they can be sorted in later
1481 my %prepared_template_arrays = IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
1482 my @prepared_arrays = keys %prepared_template_arrays;
1483 my @separate_totals = qw(non_separate_subtotal);
1485 $form->{TEMPLATE_ARRAYS} = { };
1487 my $ic_cvar_configs = CVar->get_configs(module => 'IC');
1488 my $project_cvar_configs = CVar->get_configs(module => 'Projects');
1491 qw(runningnumber number description longdescription qty qty_nofmt ship ship_nofmt unit bin
1492 partnotes serialnumber reqdate sellprice sellprice_nofmt listprice listprice_nofmt netprice netprice_nofmt
1493 discount discount_nofmt p_discount discount_sub discount_sub_nofmt nodiscount_sub nodiscount_sub_nofmt
1494 linetotal linetotal_nofmt nodiscount_linetotal nodiscount_linetotal_nofmt tax_rate projectnumber projectdescription
1495 price_factor price_factor_name partsgroup weight weight_nofmt lineweight lineweight_nofmt optional);
1497 push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
1498 push @arrays, map { "project_cvar_$_->{name}" } @{ $project_cvar_configs };
1500 my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
1502 map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @prepared_arrays);
1504 my $totalweight = 0;
1506 foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
1509 if ($item->[1] ne $sameitem) {
1510 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'partsgroup');
1511 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
1512 $sameitem = $item->[1];
1514 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1517 $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
1519 if ($form->{"id_$i"} != 0) {
1521 # add number, description and qty to $form->{number}, ....
1523 if ($form->{"subtotal_$i"} && !$subtotal_header) {
1524 $subtotal_header = $i;
1525 $position = int($position);
1528 } elsif ($subtotal_header) {
1530 $position = int($position);
1531 $position = $position.".".$subposition;
1533 $position = int($position);
1537 my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
1539 push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, $prepared_template_arrays{$_}[$i - 1]) for @prepared_arrays;
1541 push @{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'normal';
1542 push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} }, $position;
1543 push @{ $form->{TEMPLATE_ARRAYS}->{number} }, $form->{"partnumber_$i"};
1544 push @{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->{"description_$i"};
1545 push @{ $form->{TEMPLATE_ARRAYS}->{longdescription} }, $form->{"longdescription_$i"};
1546 push @{ $form->{TEMPLATE_ARRAYS}->{qty} }, $form->format_amount($myconfig, $form->{"qty_$i"});
1547 push @{ $form->{TEMPLATE_ARRAYS}->{qty_nofmt} }, $form->{"qty_$i"};
1548 push @{ $form->{TEMPLATE_ARRAYS}->{ship} }, $form->format_amount($myconfig, $form->{"ship_$i"});
1549 push @{ $form->{TEMPLATE_ARRAYS}->{ship_nofmt} }, $form->{"ship_$i"};
1550 push @{ $form->{TEMPLATE_ARRAYS}->{unit} }, $form->{"unit_$i"};
1551 push @{ $form->{TEMPLATE_ARRAYS}->{bin} }, $form->{"bin_$i"};
1552 push @{ $form->{TEMPLATE_ARRAYS}->{partnotes} }, $form->{"partnotes_$i"};
1553 push @{ $form->{TEMPLATE_ARRAYS}->{serialnumber} }, $form->{"serialnumber_$i"};
1554 push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} }, $form->{"reqdate_$i"};
1555 push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} }, $form->{"sellprice_$i"};
1556 push @{ $form->{TEMPLATE_ARRAYS}->{sellprice_nofmt} }, $form->parse_amount($myconfig, $form->{"sellprice_$i"});
1557 push @{ $form->{TEMPLATE_ARRAYS}->{listprice} }, $form->format_amount($myconfig, $form->{"listprice_$i"}, 2);
1558 push @{ $form->{TEMPLATE_ARRAYS}->{listprice_nofmt} }, $form->{"listprice_$i"};
1559 push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} }, $price_factor->{formatted_factor};
1560 push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
1561 push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} }, $form->{"partsgroup_$i"};
1562 push @{ $form->{TEMPLATE_ARRAYS}->{optional} }, $form->{"optional_$i"};
1564 my $sellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
1565 my ($dec) = ($sellprice =~ /\.(\d+)/);
1566 my $decimalplaces = max 2, length($dec);
1568 my $parsed_discount = $form->parse_amount($myconfig, $form->{"discount_$i"});
1570 my $linetotal_exact = $form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor};
1571 my $linetotal = $form->round_amount($linetotal_exact, 2);
1573 my $nodiscount_exact_linetotal = $form->{"qty_$i"} * $sellprice / $price_factor->{factor};
1574 my $nodiscount_linetotal = $form->round_amount($nodiscount_exact_linetotal,2);
1576 my $discount = $nodiscount_linetotal - $linetotal; # is always rounded because $nodiscount_linetotal and $linetotal are rounded
1578 my $discount_round_error = $discount + ($linetotal_exact - $nodiscount_exact_linetotal); # not used
1580 $form->{"netprice_$i"} = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, $decimalplaces);
1582 push @{ $form->{TEMPLATE_ARRAYS}->{netprice} }, ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
1583 push @{ $form->{TEMPLATE_ARRAYS}->{netprice_nofmt} }, ($form->{"netprice_$i"} != 0) ? $form->{"netprice_$i"} : '';
1585 $linetotal = ($linetotal != 0) ? $linetotal : '';
1587 push @{ $form->{TEMPLATE_ARRAYS}->{discount} }, ($discount != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
1588 push @{ $form->{TEMPLATE_ARRAYS}->{discount_nofmt} }, ($discount != 0) ? $discount * -1 : '';
1589 push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} }, $form->{"discount_$i"};
1591 if ( $prepared_template_arrays{separate}[$i - 1] ) {
1592 my $pabbr = $prepared_template_arrays{separate}[$i - 1];
1593 if ( ! $form->{"separate_${pabbr}_subtotal"} ) {
1594 push @separate_totals , "separate_${pabbr}_subtotal";
1595 $form->{"separate_${pabbr}_subtotal"} = 0;
1597 $form->{"separate_${pabbr}_subtotal"} += $linetotal;
1599 $form->{non_separate_subtotal} += $linetotal;
1602 $form->{ordtotal} += $linetotal unless $form->{"optional_$i"};
1603 $form->{nodiscount_total} += $nodiscount_linetotal;
1604 $form->{discount_total} += $discount;
1606 if ($subtotal_header) {
1607 $discount_subtotal += $linetotal;
1608 $nodiscount_subtotal += $nodiscount_linetotal;
1611 if ($form->{"subtotal_$i"} && $subtotal_header && ($subtotal_header != $i)) {
1612 push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} }, $form->format_amount($myconfig, $discount_subtotal, 2);
1613 push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub_nofmt} }, $discount_subtotal;
1614 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} }, $form->format_amount($myconfig, $nodiscount_subtotal, 2);
1615 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub_nofmt} }, $nodiscount_subtotal;
1617 $discount_subtotal = 0;
1618 $nodiscount_subtotal = 0;
1619 $subtotal_header = 0;
1622 push @{ $form->{TEMPLATE_ARRAYS}->{$_} }, "" for qw(discount_sub nodiscount_sub discount_sub_nofmt nodiscount_sub_nofmt);
1625 if (!$form->{"discount_$i"}) {
1626 $nodiscount += $linetotal;
1629 my $project = $projects_by_id{$form->{"project_id_$i"}} || SL::DB::Project->new;
1631 push @{ $form->{TEMPLATE_ARRAYS}->{linetotal} }, $form->format_amount($myconfig, $linetotal, 2);
1632 push @{ $form->{TEMPLATE_ARRAYS}->{linetotal_nofmt} }, $linetotal_exact;
1633 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} }, $form->format_amount($myconfig, $nodiscount_linetotal, 2);
1634 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal_nofmt} }, $nodiscount_linetotal;
1635 push @{ $form->{TEMPLATE_ARRAYS}->{projectnumber} }, $project->projectnumber;
1636 push @{ $form->{TEMPLATE_ARRAYS}->{projectdescription} }, $project->description;
1638 my $lineweight = $form->{"qty_$i"} * $form->{"weight_$i"};
1639 $totalweight += $lineweight;
1640 push @{ $form->{TEMPLATE_ARRAYS}->{weight} }, $form->format_amount($myconfig, $form->{"weight_$i"}, 3);
1641 push @{ $form->{TEMPLATE_ARRAYS}->{weight_nofmt} }, $form->{"weight_$i"};
1642 push @{ $form->{TEMPLATE_ARRAYS}->{lineweight} }, $form->format_amount($myconfig, $lineweight, 3);
1643 push @{ $form->{TEMPLATE_ARRAYS}->{lineweight_nofmt} }, $lineweight;
1645 my ($taxamount, $taxbase);
1648 map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
1650 unless ($form->{"optional_$i"}) {
1651 if ($form->{taxincluded}) {
1654 $taxamount = $linetotal * $taxrate / (1 + $taxrate);
1655 $taxbase = $linetotal / (1 + $taxrate);
1657 $taxamount = $linetotal * $taxrate;
1658 $taxbase = $linetotal;
1662 if ($taxamount != 0) {
1663 foreach my $accno (split / /, $form->{"taxaccounts_$i"}) {
1664 $taxaccounts{$accno} += $taxamount * $form->{"${accno}_rate"} / $taxrate;
1665 $taxbase{$accno} += $taxbase;
1669 $tax_rate = $taxrate * 100;
1670 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
1672 if ($form->{"part_type_$i"} eq 'assembly') {
1675 # get parts and push them onto the stack
1677 if ($form->{groupitems}) {
1678 $sortorder = qq|ORDER BY pg.partsgroup, a.position|;
1680 $sortorder = qq|ORDER BY a.position|;
1683 $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty, | .
1684 qq|pg.partsgroup | .
1685 qq|FROM assembly a | .
1686 qq| JOIN parts p ON (a.parts_id = p.id) | .
1687 qq| LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
1688 qq| WHERE a.bom = '1' | .
1689 qq| AND a.id = ? | . $sortorder;
1690 @values = ($form->{"id_$i"});
1691 $sth = $dbh->prepare($query);
1692 $sth->execute(@values) || $form->dberror($query);
1694 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
1695 if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
1696 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1697 $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
1698 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'assembly-item-partsgroup');
1699 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
1702 push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type} }, 'assembly-item');
1703 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq|, $ref->{partnumber}, $ref->{description}|);
1704 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
1709 CVar->get_non_editable_ic_cvars(form => $form,
1712 sub_module => 'orderitems',
1713 may_converted_from => ['orderitems', 'invoice']);
1715 push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} },
1716 CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)
1717 for @{ $ic_cvar_configs };
1719 push @{ $form->{TEMPLATE_ARRAYS}->{"project_cvar_" . $_->config->name} }, $_->value_as_text for @{ $project->cvars_by_config };
1723 $form->{totalweight} = $form->format_amount($myconfig, $totalweight, 3);
1724 $form->{totalweight_nofmt} = $totalweight;
1725 my $defaults = AM->get_defaults();
1726 $form->{weightunit} = $defaults->{weightunit};
1729 foreach $item (sort keys %taxaccounts) {
1730 $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
1732 push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase} }, $form->format_amount($myconfig, $taxbase{$item}, 2));
1733 push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase_nofmt} }, $taxbase{$item});
1734 push(@{ $form->{TEMPLATE_ARRAYS}->{tax} }, $form->format_amount($myconfig, $taxamount, 2));
1735 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_nofmt} }, $taxamount);
1736 push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} }, $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
1737 push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate_nofmt} }, $form->{"${item}_rate"} * 100);
1738 push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} }, $form->{"${item}_taxnumber"});
1739 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_id} }, $form->{"${item}_tax_id"});
1741 if ( $form->{"${item}_tax_id"} ) {
1742 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"};
1743 my $description = $tax_obj ? $tax_obj->translated_attribute('taxdescription', $form->{language_id}, 0) : '';
1744 push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $description . q{ } . 100 * $form->{"${item}_rate"} . q{%});
1748 $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
1749 $form->{discount_total} = $form->format_amount($myconfig, $form->{discount_total}, 2);
1750 $form->{nodiscount} = $form->format_amount($myconfig, $nodiscount, 2);
1751 $form->{yesdiscount} = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
1753 if($form->{taxincluded}) {
1754 $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal} - $tax, 2);
1755 $form->{subtotal_nofmt} = $form->{ordtotal} - $tax;
1757 $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
1758 $form->{subtotal_nofmt} = $form->{ordtotal};
1761 my $grossamount = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
1762 $form->{ordtotal} = $form->round_amount( $grossamount, 2, 1);
1763 $form->{rounding} = $form->round_amount(
1764 $form->{ordtotal} - $form->round_amount($grossamount, 2),
1769 $form->{rounding} = $form->format_amount($myconfig, $form->{rounding}, 2);
1770 $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
1772 $form->set_payment_options($myconfig, $form->{$form->{type} =~ /_quotation/ ? 'quodate' : 'orddate'}, $form->{type});
1774 $form->{username} = $myconfig->{name};
1776 $form->{department} = SL::DB::Manager::Department->find_by(id => $form->{department_id})->description if $form->{department_id};
1777 $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
1778 $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
1780 $form->{order} = SL::DB::Manager::Order->find_by(id => $form->{id}) if $form->{id};
1781 $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) for @separate_totals;
1783 $main::lxdebug->leave_sub();
1792 OE.pm - Order entry module
1796 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>.
1802 =item retrieve_simple PARAMS
1804 simple OE retrieval by id. does not look up customer, vendor, units or any other stuff. only oe and orderitems.
1806 my $order = retrieve_simple(id => 2);