1 #=====================================================================
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
7 #=====================================================================
8 # SQL-Ledger, Accounting
11 # Author: Dieter Simader
12 # Email: dsimader@sql-ledger.org
13 # Web: http://www.sql-ledger.org
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #======================================================================
30 # Inventory Control module
32 #======================================================================
34 use POSIX qw(strftime);
35 use List::Util qw(first max);
36 use List::MoreUtils qw(any);
41 use SL::ReportGenerator;
49 our ($form, $locale, %myconfig, $lxdebug, $auth);
51 require "bin/mozilla/io.pl";
52 require "bin/mozilla/invoice_io.pl";
53 require "bin/mozilla/common.pl";
54 require "bin/mozilla/reportgenerator.pl";
59 # type=submit $locale->text('Add Part')
60 # type=submit $locale->text('Add Service')
61 # type=submit $locale->text('Add Assembly')
62 # type=submit $locale->text('Edit Part')
63 # type=submit $locale->text('Edit Service')
64 # type=submit $locale->text('Edit Assembly')
65 # $locale->text('Parts')
66 # $locale->text('Services')
67 # $locale->text('Inventory quantity must be zero before you can set this part obsolete!')
68 # $locale->text('Inventory quantity must be zero before you can set this assembly obsolete!')
69 # $locale->text('Part Number missing!')
70 # $locale->text('Service Number missing!')
71 # $locale->text('Assembly Number missing!')
72 # $locale->text('ea');
77 $lxdebug->enter_sub();
79 $auth->assert('part_service_assembly_edit');
81 my $title = 'Add ' . ucfirst $form->{item};
82 $form->{title} = $locale->text($title);
83 $form->{callback} = "$form->{script}?action=add&item=$form->{item}" unless $form->{callback};
84 $form->{unit_changeable} = 1;
86 IC->get_pricegroups(\%myconfig, \%$form);
90 $lxdebug->leave_sub();
94 $lxdebug->enter_sub();
96 $auth->assert('part_service_assembly_edit');
98 $form->{revers} = 0; # switch for backward sorting
99 $form->{lastsort} = ""; # memory for which table was sort at last time
100 $form->{ndxs_counter} = 0; # counter for added entries to top100
102 my %is_xyz = map { +"is_$_" => ($form->{searchitems} eq $_) } qw(part service assembly);
104 $form->{title} = (ucfirst $form->{searchitems}) . "s";
105 $form->{title} = $locale->text($form->{title});
106 $form->{title} = $locale->text('Assemblies') if ($is_xyz{is_assembly});
108 $form->{jsscript} = 1;
110 $form->{CUSTOM_VARIABLES} = CVar->get_configs('module' => 'IC');
111 ($form->{CUSTOM_VARIABLES_FILTER_CODE},
112 $form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables' => $form->{CUSTOM_VARIABLES},
113 'include_prefix' => 'l_',
114 'include_value' => 'Y');
118 $form->get_lists('partsgroup' => 'ALL_PARTSGROUPS');
119 print $form->parse_html_template('ic/search', { %is_xyz,
120 dateformat => $myconfig{dateformat},
121 limit => $myconfig{vclimit}, });
123 $lxdebug->leave_sub();
126 sub search_update_prices {
127 $lxdebug->enter_sub();
129 $auth->assert('part_service_assembly_edit');
131 my $pricegroups = IC->get_pricegroups(\%myconfig, \%$form);
133 $form->{title} = $locale->text('Update Prices');
137 print $form->parse_html_template('ic/search_update_prices', { PRICE_ROWS => $pricegroups });
139 $lxdebug->leave_sub();
142 sub confirm_price_update {
143 $lxdebug->enter_sub();
145 $auth->assert('part_service_assembly_edit');
148 my $value_found = undef;
150 foreach my $idx (qw(sellprice listprice), (1..$form->{price_rows})) {
151 my $name = $idx =~ m/\d/ ? $form->{"pricegroup_${idx}"} : $idx eq 'sellprice' ? $locale->text('Sell Price') : $locale->text('List Price');
152 my $type = $idx =~ m/\d/ ? $form->{"pricegroup_type_${idx}"} : $form->{"${idx}_type"};
153 my $value_idx = $idx =~ m/\d/ ? "price_${idx}" : $idx;
154 my $value = $form->parse_amount(\%myconfig, $form->{$value_idx});
156 if ((0 > $value) && ($type eq 'percent')) {
157 push @errors, $locale->text('You cannot adjust the price for pricegroup "#1" by a negative percentage.', $name);
159 } elsif (!$value && ($form->{$value_idx} ne '')) {
160 push @errors, $locale->text('No valid number entered for pricegroup "#1".', $name);
162 } elsif (0 < $value) {
167 push @errors, $locale->text('No prices will be updated because no prices have been entered.') if (!$value_found);
169 my $num_matches = IC->get_num_matches_for_priceupdate();
174 $form->show_generic_error(join('<br>', @errors), 'back_button' => 1);
177 $form->{nextsub} = "update_prices";
179 map { delete $form->{$_} } qw(action header);
181 print $form->parse_html_template('ic/confirm_price_update', { HIDDENS => [ map { name => $_, value => $form->{$_} }, keys %$form ],
182 num_matches => $num_matches });
184 $lxdebug->leave_sub();
188 $lxdebug->enter_sub();
190 $auth->assert('part_service_assembly_edit');
192 my $num_updated = IC->update_prices(\%myconfig, \%$form);
194 if (-1 != $num_updated) {
195 $form->redirect($locale->text('#1 prices were updated.', $num_updated));
197 $form->error($locale->text('Could not update prices!'));
200 $lxdebug->leave_sub();
204 # $lxdebug->enter_sub();
206 # $auth->assert('part_service_assembly_edit');
208 # our ($j, $lastndx);
211 # $form->{title} = $locale->text('Top 100 hinzufuegen');
215 # push @custom_hiddens, qw(searchitems title bom titel revers lastsort sort ndxs_counter extras);
216 # push @custom_hiddens, qw(itemstatus l_linetotal l_partnumber l_description l_onhand l_unit l_sellprice l_linetotalsellprice);
218 # +{ name => 'row', value => $j },
219 # +{ name => 'nextsub', value => 'item_selected' },
220 # +{ name => 'test', value => 'item_selected' },
221 # +{ name => 'lastndx', value => $lastndx },
222 # map(+{ name => $_, value => $form->{$_} }, @custom_hiddens),
225 # my ($partnumber, $description, $unit, $sellprice, $soldtotal);
226 # # if choice set data
227 ## if ($form->{ndx}) {
228 ## for my $i (0 .. $form->{ndxs_counter}) {
230 ## # insert data into top100
231 ## push @{ $form->{parts} },
233 ## partnumber => $form->{"totop100_partnumber_$j"},
234 ## description => $form->{"totop100_description_$j"},
235 ## unit => $form->{"totop100_unit_$j"},
236 ## sellprice => $form->{"totop100_sellprice_$j"},
237 ## soldtotal => $form->{"totop100_soldtotal_$j"},
244 # # set data for next page
245 # for my $i (1 .. $form->{ndxs_counter}) {
246 # $partnumber = $form->{"totop100_partnumber_$i"};
247 # $description = $form->{"totop100_description_$i"};
248 # $unit = $form->{"totop100_unit_$i"};
249 # $sellprice = $form->{"totop100_sellprice_$i"};
250 # $soldtotal = $form->{"totop100_soldtotal_$i"};
253 # totop100_partnumber => $form->{"totop100_partnumber_$i"},
254 # totop100_description => $form->{"totop100_description_$i"},
255 # totop100_unit => $form->{"totop100_unit_$i"},
256 # totop100_sellprice => $form->{"totop100_sellprice_$i"},
257 # totop100_soldtotal => $form->{"totop100_soldtotal_$i"},
261 ##<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
262 ##<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
263 ##<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
264 ##<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
265 ##<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
269 # print $form->parse_html_template('ic/choice', +{ HIDDENS => \@HIDDENS, PARTS => \@PARTS });
271 # $lxdebug->leave_sub();
275 # $lxdebug->enter_sub();
277 # $auth->assert('part_service_assembly_edit');
280 # our ($partnumber, $description, $unit, $sellprice, $soldtotal);
282 # my @sortorders = ("", "partnumber", "description", "all");
283 # my $sortorder = $sortorders[($form->{description} ? 2 : 0) + ($form->{partnumber} ? 1 : 0)];
284 # IC->get_parts(\%myconfig, \%$form, $sortorder);
286 # $form->{title} = $locale->text('Top 100 hinzufuegen');
292 # <form method=post action=ic.pl>
295 # <th class=listtop colspan=6>| . $locale->text('choice part') . qq|</th>
297 # <tr height="5"></tr>
298 # <tr class=listheading>
300 # <th class=listheading>| . $locale->text('Part Number') . qq|</th>
301 # <th class=listheading>| . $locale->text('Part Description') . qq|</th>
302 # <th class=listheading>| . $locale->text('Unit of measure') . qq|</th>
303 # <th class=listheading>| . $locale->text('Sell Price') . qq|</th>
304 # <th class=listheading>| . $locale->text('soldtotal') . qq|</th>
308 # my $i = $form->{rows};
310 # for ($j = 1; $j <= $i; $j++) {
313 # <tr class=listrow| . ($j % 2) . qq|>|;
316 # <td><input name=ndx class=radio type=radio value=$j checked></td>|;
319 # <td><input name=ndx class=radio type=radio value=$j></td>|;
322 # <td><input name="new_partnumber_$j" type=hidden value="$form->{"partnumber_$j"}">$form->{"partnumber_$j"}</td>
323 # <td><input name="new_description_$j" type=hidden value="$form->{"description_$j"}">$form->{"description_$j"}</td>
324 # <td><input name="new_unit_$j" type=hidden value="$form->{"unit_$j"}">$form->{"unit_$j"}</td>
325 # <td><input name="new_sellprice_$j" type=hidden value="$form->{"sellprice_$j"}">$form->{"sellprice_$j"}</td>
326 # <td><input name="new_soldtotal_$j" type=hidden value="$form->{"soldtotal_$j"}">$form->{"soldtotal_$j"}</td>
329 # <input name="new_id_$j" type=hidden value="$form->{"id_$j"}">|;
339 #<input type=hidden name=itemstatus value="$form->{itemstatus}">
340 #<input type=hidden name=l_linetotal value="$form->{l_linetotal}">
341 #<input type=hidden name=l_partnumber value="$form->{l_partnumber}">
342 #<input type=hidden name=l_description value="$form->{l_description}">
343 #<input type=hidden name=l_onhand value="$form->{l_onhand}">
344 #<input type=hidden name=l_unit value="$form->{l_unit}">
345 #<input type=hidden name=l_sellprice value="$form->{l_sellprice}">
346 #<input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
347 #<input type=hidden name=sort value="$form->{sort}">
348 #<input type=hidden name=revers value="$form->{revers}">
349 #<input type=hidden name=lastsort value="$form->{lastsort}">
351 #<input type=hidden name=bom value="$form->{bom}">
352 #<input type=hidden name=titel value="$form->{titel}">
353 #<input type=hidden name=searchitems value="$form->{searchitems}">
355 #<input type=hidden name=row value=$j>
357 #<input type=hidden name=nextsub value=item_selected>
359 #<input name=lastndx type=hidden value=$lastndx>
361 #<input name=ndxs_counter type=hidden value=$form->{ndxs_counter}>|;
365 # if (($form->{ndxs_counter}) > 0) {
366 # for ($i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
368 # $partnumber = $form->{"totop100_partnumber_$i"};
369 # $description = $form->{"totop100_description_$i"};
370 # $unit = $form->{"totop100_unit_$i"};
371 # $sellprice = $form->{"totop100_sellprice_$i"};
372 # $soldtotal = $form->{"totop100_soldtotal_$i"};
375 #<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
376 #<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
377 #<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
378 #<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
379 #<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
387 #<input class=submit type=submit name=action value="|
388 # . $locale->text('TOP100') . qq|">
392 # $lxdebug->leave_sub();
396 $lxdebug->enter_sub();
398 $auth->assert('part_service_assembly_edit');
401 $form->{ndxs_counter}++;
403 if ($form->{ndxs_counter} > 0) {
405 my $index = $form->{ndx};
407 $form->{"totop100_partnumber_$form->{ndxs_counter}"} = $form->{"new_partnumber_$index"};
408 $form->{"totop100_description_$form->{ndxs_counter}"} = $form->{"new_description_$index"};
409 $form->{"totop100_unit_$form->{ndxs_counter}"} = $form->{"new_unit_$index"};
410 $form->{"totop100_sellprice_$form->{ndxs_counter}"} = $form->{"new_sellprice_$index"};
411 $form->{"totop100_soldtotal_$form->{ndxs_counter}"} = $form->{"new_soldtotal_$index"};
415 $lxdebug->leave_sub();
419 $lxdebug->enter_sub();
421 $auth->assert('part_service_assembly_edit');
423 my ($revers, $lastsort, $callback, $option, $description, $sameitem,
424 $partnumber, $unit, $sellprice, $soldtotal, $totop100, $onhand, $align);
425 my (@column_index, %column_header, %column_data);
426 my ($totalsellprice, $totallastcost, $totallistprice, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice);
428 $form->{top100} = "top100";
429 $form->{l_soldtotal} = "Y";
430 $form->{soldtotal} = "soldtotal";
431 $form->{sort} = "soldtotal";
432 $form->{l_qty} = "N";
433 $form->{l_linetotal} = "";
435 $form->{number} = "position";
436 $form->{l_number} = "Y";
440 $form->{title} = $locale->text('Top 100');
442 $revers = $form->{revers};
443 $lastsort = $form->{lastsort};
445 if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
447 $form->{lastsort} = "partnumber";
448 $form->{sort} = "partnumber";
452 "$form->{script}?action=top100&searchitems=$form->{searchitems}&itemstatus=$form->{itemstatus}&bom=$form->{bom}&l_linetotal=$form->{l_linetotal}&title="
453 . $form->escape($form->{title}, 1);
455 # if we have a serialnumber limit search
456 if ($form->{serialnumber} || $form->{l_serialnumber}) {
457 $form->{l_serialnumber} = "Y";
458 unless ( $form->{bought}
461 || $form->{quoted}) {
462 $form->{bought} = $form->{sold} = 1;
465 IC->all_parts(\%myconfig, \%$form);
467 if ($form->{itemstatus} eq 'active') {
468 $option .= $locale->text('Active') . " : ";
470 if ($form->{itemstatus} eq 'obsolete') {
471 $option .= $locale->text('Obsolete') . " : ";
473 if ($form->{itemstatus} eq 'orphaned') {
474 $option .= $locale->text('Orphaned') . " : ";
476 if ($form->{itemstatus} eq 'onhand') {
477 $option .= $locale->text('On Hand') . " : ";
478 $form->{l_onhand} = "Y";
480 if ($form->{itemstatus} eq 'short') {
481 $option .= $locale->text('Short') . " : ";
482 $form->{l_onhand} = "Y";
484 if ($form->{onorder}) {
485 $form->{l_ordnumber} = "Y";
486 $callback .= "&onorder=$form->{onorder}";
487 $option .= $locale->text('On Order') . " : ";
489 if ($form->{ordered}) {
490 $form->{l_ordnumber} = "Y";
491 $callback .= "&ordered=$form->{ordered}";
492 $option .= $locale->text('Ordered') . " : ";
495 $form->{l_quonumber} = "Y";
496 $callback .= "&rfq=$form->{rfq}";
497 $option .= $locale->text('RFQ') . " : ";
499 if ($form->{quoted}) {
500 $form->{l_quonumber} = "Y";
501 $callback .= ""ed=$form->{quoted}";
502 $option .= $locale->text('Quoted') . " : ";
504 if ($form->{bought}) {
505 $form->{l_invnumber} = "Y";
506 $callback .= "&bought=$form->{bought}";
507 $option .= $locale->text('Bought') . " : ";
510 $form->{l_invnumber} = "Y";
511 $callback .= "&sold=$form->{sold}";
512 $option .= $locale->text('Sold') . " : ";
519 || $form->{quoted}) {
521 $form->{l_lastcost} = "";
522 $form->{l_name} = "Y";
523 if ($form->{transdatefrom}) {
524 $callback .= "&transdatefrom=$form->{transdatefrom}";
526 . $locale->text('From')
528 . $locale->date(\%myconfig, $form->{transdatefrom}, 1);
530 if ($form->{transdateto}) {
531 $callback .= "&transdateto=$form->{transdateto}";
533 . $locale->text('To')
535 . $locale->date(\%myconfig, $form->{transdateto}, 1);
541 if ($form->{partnumber}) {
542 $callback .= "&partnumber=$form->{partnumber}";
543 $option .= $locale->text('Part Number') . qq| : $form->{partnumber}<br>|;
546 $callback .= "&partnumber=$form->{ean}";
547 $option .= $locale->text('EAN') . qq| : $form->{ean}<br>|;
549 if ($form->{partsgroup}) {
550 $callback .= "&partsgroup=$form->{partsgroup}";
551 $option .= $locale->text('Group') . qq| : $form->{partsgroup}<br>|;
553 if ($form->{serialnumber}) {
554 $callback .= "&serialnumber=$form->{serialnumber}";
555 $option .= $locale->text('Serial Number') . qq| : $form->{serialnumber}<br>|;
557 if ($form->{description}) {
558 $callback .= "&description=$form->{description}";
559 $description = $form->{description};
560 $description =~ s/\n/<br>/g;
561 $option .= $locale->text('Part Description') . qq| : $form->{description}<br>|;
564 $callback .= "&make=$form->{make}";
565 $option .= $locale->text('Make') . qq| : $form->{make}<br>|;
567 if ($form->{model}) {
568 $callback .= "&model=$form->{model}";
569 $option .= $locale->text('Model') . qq| : $form->{model}<br>|;
571 if ($form->{drawing}) {
572 $callback .= "&drawing=$form->{drawing}";
573 $option .= $locale->text('Drawing') . qq| : $form->{drawing}<br>|;
575 if ($form->{microfiche}) {
576 $callback .= "µfiche=$form->{microfiche}";
577 $option .= $locale->text('Microfiche') . qq| : $form->{microfiche}<br>|;
579 if ($form->{l_soldtotal}) {
580 $callback .= "&soldtotal=$form->{soldtotal}";
581 $option .= $locale->text('soldtotal') . qq| : $form->{soldtotal}<br>|;
584 my @columns = $form->sort_columns(
585 qw(number partnumber ean description partsgroup bin onhand rop unit listprice linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost priceupdate weight image drawing microfiche invnumber ordnumber quonumber name serialnumber soldtotal)
588 if ($form->{l_linetotal}) {
589 $form->{l_onhand} = "Y";
590 $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
591 if ($form->{l_lastcost}) {
592 $form->{l_linetotallastcost} = "Y";
593 if (($form->{searchitems} eq 'assembly') && !$form->{bom}) {
594 $form->{l_linetotallastcost} = "";
597 $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
600 if ($form->{searchitems} eq 'service') {
602 # remove bin, weight and rop from list
603 map { $form->{"l_$_"} = "" } qw(bin weight rop);
605 $form->{l_onhand} = "";
607 # qty is irrelevant unless bought or sold
613 || $form->{quoted}) {
614 $form->{l_onhand} = "Y";
616 $form->{l_linetotalsellprice} = "";
617 $form->{l_linetotallastcost} = "";
621 foreach my $item (@columns) {
622 if ($form->{"l_$item"} eq "Y") {
623 push @column_index, $item;
625 # add column to callback
626 $callback .= "&l_$item=Y";
630 if ($form->{l_subtotal} eq 'Y') {
631 $callback .= "&l_subtotal=Y";
634 $column_header{number} =
635 qq|<th class=listheading nowrap>| . $locale->text('number') . qq|</th>|;
636 $column_header{partnumber} =
637 qq|<th nowrap><a class=listheading href=$callback&sort=partnumber&revers=$form->{revers}&lastsort=$form->{lastsort}>|
638 . $locale->text('Part Number')
640 $column_header{description} =
641 qq|<th nowrap><a class=listheading href=$callback&sort=description&revers=$form->{revers}&lastsort=$form->{lastsort}>|
642 . $locale->text('Part Description')
644 $column_header{partsgroup} =
645 qq|<th nowrap><a class=listheading href=$callback&sort=partsgroup>|
646 . $locale->text('Group')
648 $column_header{bin} =
649 qq|<th><a class=listheading href=$callback&sort=bin>|
650 . $locale->text('Bin')
652 $column_header{priceupdate} =
653 qq|<th nowrap><a class=listheading href=$callback&sort=priceupdate>|
654 . $locale->text('Updated')
656 $column_header{onhand} =
657 qq|<th nowrap><a class=listheading href=$callback&sort=onhand&revers=$form->{revers}&lastsort=$form->{lastsort}>|
658 . $locale->text('Qty')
660 $column_header{unit} =
661 qq|<th class=listheading nowrap>| . $locale->text('Unit') . qq|</th>|;
662 $column_header{listprice} =
663 qq|<th class=listheading nowrap>|
664 . $locale->text('List Price')
666 $column_header{lastcost} =
667 qq|<th class=listheading nowrap>| . $locale->text('Last Cost') . qq|</th>|;
668 $column_header{rop} =
669 qq|<th class=listheading nowrap>| . $locale->text('ROP') . qq|</th>|;
670 $column_header{weight} =
671 qq|<th class=listheading nowrap>| . $locale->text('Weight') . qq|</th>|;
673 $column_header{invnumber} =
674 qq|<th nowrap><a class=listheading href=$callback&sort=invnumber>|
675 . $locale->text('Invoice Number')
677 $column_header{ordnumber} =
678 qq|<th nowrap><a class=listheading href=$callback&sort=ordnumber>|
679 . $locale->text('Order Number')
681 $column_header{quonumber} =
682 qq|<th nowrap><a class=listheading href=$callback&sort=quonumber>|
683 . $locale->text('Quotation')
686 $column_header{name} =
687 qq|<th nowrap><a class=listheading href=$callback&sort=name>|
688 . $locale->text('Name')
691 $column_header{sellprice} =
692 qq|<th class=listheading nowrap>|
693 . $locale->text('Sell Price')
695 $column_header{linetotalsellprice} =
696 qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
697 $column_header{linetotallastcost} =
698 qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
699 $column_header{linetotallistprice} =
700 qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
702 $column_header{image} =
703 qq|<th class=listheading nowrap>| . $locale->text('Image') . qq|</a></th>|;
704 $column_header{drawing} =
705 qq|<th nowrap><a class=listheading href=$callback&sort=drawing>|
706 . $locale->text('Drawing')
708 $column_header{microfiche} =
709 qq|<th nowrap><a class=listheading href=$callback&sort=microfiche>|
710 . $locale->text('Microfiche')
713 $column_header{serialnumber} =
714 qq|<th nowrap><a class=listheading href=$callback&sort=serialnumber>|
715 . $locale->text('Serial Number')
717 $column_header{soldtotal} =
718 qq|<th nowrap><a class=listheading href=$callback&sort=soldtotal&revers=$form->{revers}&lastsort=$form->{lastsort}>|
719 . $locale->text('soldtotal')
723 my $colspan = $#column_index + 1;
730 <th class=listtop colspan=$colspan>$form->{title}</th>
734 <tr><td colspan=$colspan>$option</td></tr>
736 <tr class=listheading>
739 map { print "\n$column_header{$_}" } @column_index;
745 # add order to callback
746 $form->{callback} = $callback .= "&sort=$form->{sort}";
748 # escape callback for href
749 $callback = $form->escape($callback);
751 if (@{ $form->{parts} }) {
752 $sameitem = $form->{parts}->[0]->{ $form->{sort} };
755 # insert numbers for top100
757 foreach my $ref (@{ $form->{parts} }) {
762 # if avaible -> insert choice here
763 if (($form->{ndxs_counter}) > 0) {
764 for (my $i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
765 $partnumber = $form->{"totop100_partnumber_$i"};
766 $description = $form->{"totop100_description_$i"};
767 $unit = $form->{"totop100_unit_$i"};
768 $sellprice = $form->{"totop100_sellprice_$i"};
769 $soldtotal = $form->{"totop100_soldtotal_$i"};
772 <input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
773 <input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
774 <input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
775 <input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
776 <input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
780 push @{ $form->{parts} },
782 partnumber => "$partnumber",
783 description => "$description",
785 sellprice => "$sellprice",
786 soldtotal => "$soldtotal" };
789 # build data for columns
791 foreach my $ref (@{ $form->{parts} }) {
793 if ($form->{l_subtotal} eq 'Y' && !$ref->{assemblyitem}) {
794 if ($sameitem ne $ref->{ $form->{sort} }) {
795 parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
796 $sameitem = $ref->{ $form->{sort} };
800 $ref->{exchangerate} = 1 unless $ref->{exchangerate};
801 $ref->{sellprice} *= $ref->{exchangerate};
802 $ref->{listprice} *= $ref->{exchangerate};
803 $ref->{lastcost} *= $ref->{exchangerate};
805 # use this for assemblies
806 $onhand = $ref->{onhand};
809 if ($ref->{assemblyitem}) {
811 $onhand = 0 if ($form->{sold});
814 $ref->{description} =~ s/\n/<br>/g;
816 $column_data{number} =
818 . $form->format_amount(\%myconfig, $ref->{number})
820 $column_data{partnumber} =
821 "<td align=$align>$ref->{partnumber} </a></td>";
822 $column_data{description} = "<td>$ref->{description} </td>";
823 $column_data{partsgroup} = "<td>$ref->{partsgroup} </td>";
825 $column_data{onhand} =
827 . $form->format_amount(\%myconfig, $ref->{onhand})
829 $column_data{sellprice} =
831 . $form->format_amount(\%myconfig, $ref->{sellprice})
833 $column_data{listprice} =
835 . $form->format_amount(\%myconfig, $ref->{listprice})
837 $column_data{lastcost} =
839 . $form->format_amount(\%myconfig, $ref->{lastcost})
842 $column_data{linetotalsellprice} = "<td align=right>"
843 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{sellprice}, 2)
845 $column_data{linetotallastcost} = "<td align=right>"
846 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{lastcost}, 2)
848 $column_data{linetotallistprice} = "<td align=right>"
849 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{listprice}, 2)
852 if (!$ref->{assemblyitem}) {
853 $totalsellprice += $onhand * $ref->{sellprice};
854 $totallastcost += $onhand * $ref->{lastcost};
855 $totallistprice += $onhand * $ref->{listprice};
857 $subtotalonhand += $onhand;
858 $subtotalsellprice += $onhand * $ref->{sellprice};
859 $subtotallastcost += $onhand * $ref->{lastcost};
860 $subtotallistprice += $onhand * $ref->{listprice};
865 . $form->format_amount(\%myconfig, $ref->{rop}) . "</td>";
866 $column_data{weight} =
868 . $form->format_amount(\%myconfig, $ref->{weight})
870 $column_data{unit} = "<td>$ref->{unit} </td>";
871 $column_data{bin} = "<td>$ref->{bin} </td>";
872 $column_data{priceupdate} = "<td>$ref->{priceupdate} </td>";
874 $column_data{invnumber} =
875 ($ref->{module} ne 'oe')
876 ? "<td><a href=$ref->{module}.pl?action=edit&type=invoice&id=$ref->{trans_id}&callback=$callback>$ref->{invnumber}</a></td>"
877 : "<td>$ref->{invnumber}</td>";
878 $column_data{ordnumber} =
879 ($ref->{module} eq 'oe')
880 ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{ordnumber}</a></td>"
881 : "<td>$ref->{ordnumber}</td>";
882 $column_data{quonumber} =
883 ($ref->{module} eq 'oe' && !$ref->{ordnumber})
884 ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{quonumber}</a></td>"
885 : "<td>$ref->{quonumber}</td>";
887 $column_data{name} = "<td>$ref->{name}</td>";
889 $column_data{image} =
891 ? "<td><a href=$ref->{image}><img src=$ref->{image} height=32 border=0></a></td>"
893 $column_data{drawing} =
895 ? "<td><a href=$ref->{drawing}>$ref->{drawing}</a></td>"
897 $column_data{microfiche} =
899 ? "<td><a href=$ref->{microfiche}>$ref->{microfiche}</a></td>"
902 $column_data{serialnumber} = "<td>$ref->{serialnumber}</td>";
904 $column_data{soldtotal} = "<td align=right>$ref->{soldtotal}</td>";
908 print "<tr class=listrow$i>";
910 map { print "\n$column_data{$_}" } @column_index;
917 if ($form->{l_subtotal} eq 'Y') {
918 parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
921 if ($form->{"l_linetotal"}) {
922 map { $column_data{$_} = "<td> </td>" } @column_index;
923 $column_data{linetotalsellprice} =
924 "<th class=listtotal align=right>"
925 . $form->format_amount(\%myconfig, $totalsellprice, 2)
927 $column_data{linetotallastcost} =
928 "<th class=listtotal align=right>"
929 . $form->format_amount(\%myconfig, $totallastcost, 2)
931 $column_data{linetotallistprice} =
932 "<th class=listtotal align=right>"
933 . $form->format_amount(\%myconfig, $totallistprice, 2)
936 print "<tr class=listtotal>";
938 map { print "\n$column_data{$_}" } @column_index;
945 <tr><td colspan=$colspan><hr size=3 noshade></td></tr>
954 <form method=post action=$form->{script}>
956 <input type=hidden name=itemstatus value="$form->{itemstatus}">
957 <input type=hidden name=l_linetotal value="$form->{l_linetotal}">
958 <input type=hidden name=l_partnumber value="$form->{l_partnumber}">
959 <input type=hidden name=l_description value="$form->{l_description}">
960 <input type=hidden name=l_onhand value="$form->{l_onhand}">
961 <input type=hidden name=l_unit value="$form->{l_unit}">
962 <input type=hidden name=l_sellprice value="$form->{l_sellprice}">
963 <input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
964 <input type=hidden name=sort value="$form->{sort}">
965 <input type=hidden name=revers value="$form->{revers}">
966 <input type=hidden name=lastsort value="$form->{lastsort}">
967 <input type=hidden name=parts value="$form->{parts}">
969 <input type=hidden name=bom value="$form->{bom}">
970 <input type=hidden name=titel value="$form->{titel}">
971 <input type=hidden name=searchitems value="$form->{searchitems}">|;
976 <!-- <input type=hidden name=ndxs_counter value="$form->{ndxs_counter}">-->
978 <input class=submit type=submit name=action value="|
979 . $locale->text('choice') . qq|">
984 $lxdebug->leave_sub();
989 # Warning, deep magic ahead.
990 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
992 # flags coming from the form:
994 # searchitems=part revers=0 lastsort=''
997 # partnumber ean description partsgroup serialnumber make model drawing microfiche
998 # transdatefrom transdateto
1001 # itemstatus = active | onhand | short | obsolete | orphaned
1002 # action = continue | top100
1005 # bought sold onorder ordered rfq quoted
1006 # l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
1007 # l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
1008 # l_partsgroup l_subtotal l_soldtotal l_deliverydate l_pricegroups
1011 # nextsub revers lastsort sort ndxs_counter
1013 sub generate_report {
1014 $lxdebug->enter_sub();
1016 $auth->assert('part_service_assembly_edit');
1018 my ($revers, $lastsort, $description);
1020 my $cvar_configs = CVar->get_configs('module' => 'IC');
1022 $form->{title} = (ucfirst $form->{searchitems}) . "s";
1023 $form->{title} =~ s/ys$/ies/;
1024 $form->{title} = $locale->text($form->{title});
1027 'bin' => { 'text' => $locale->text('Bin'), },
1028 'deliverydate' => { 'text' => $locale->text('deliverydate'), },
1029 'description' => { 'text' => $locale->text('Part Description'), },
1030 'drawing' => { 'text' => $locale->text('Drawing'), },
1031 'ean' => { 'text' => $locale->text('EAN'), },
1032 'image' => { 'text' => $locale->text('Image'), },
1033 'invnumber' => { 'text' => $locale->text('Invoice Number'), },
1034 'lastcost' => { 'text' => $locale->text('Last Cost'), },
1035 'linetotallastcost' => { 'text' => $locale->text('Extended'), },
1036 'linetotallistprice' => { 'text' => $locale->text('Extended'), },
1037 'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
1038 'listprice' => { 'text' => $locale->text('List Price'), },
1039 'microfiche' => { 'text' => $locale->text('Microfiche'), },
1040 'name' => { 'text' => $locale->text('Name'), },
1041 'onhand' => { 'text' => $locale->text('Stocked Qty'), },
1042 'ordnumber' => { 'text' => $locale->text('Order Number'), },
1043 'partnumber' => { 'text' => $locale->text('Part Number'), },
1044 'partsgroup' => { 'text' => $locale->text('Group'), },
1045 'priceupdate' => { 'text' => $locale->text('Updated'), },
1046 'quonumber' => { 'text' => $locale->text('Quotation'), },
1047 'rop' => { 'text' => $locale->text('ROP'), },
1048 'sellprice' => { 'text' => $locale->text('Sell Price'), },
1049 'serialnumber' => { 'text' => $locale->text('Serial Number'), },
1050 'soldtotal' => { 'text' => $locale->text('Qty in Selected Records'), },
1051 'transdate' => { 'text' => $locale->text('Transdate'), },
1052 'unit' => { 'text' => $locale->text('Unit'), },
1053 'weight' => { 'text' => $locale->text('Weight'), },
1054 'projectnumber' => { 'text' => $locale->text('Project Number'), },
1055 'projectdescription' => { 'text' => $locale->text('Project Description'), },
1058 $revers = $form->{revers};
1059 $lastsort = $form->{lastsort};
1061 # sorting and direction of sorting
1062 # ToDO: change this to the simpler field+direction method
1063 if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
1064 $form->{revers} = 0;
1065 $form->{lastsort} = "partnumber";
1066 $form->{sort} = "partnumber";
1068 if ($form->{lastsort} eq $form->{sort}) {
1069 $form->{revers} = 1 - $form->{revers};
1071 $form->{revers} = 0;
1072 $form->{lastsort} = $form->{sort};
1076 # special case if we have a serialnumber limit search
1077 # serialnumbers are only given in invoices and orders,
1078 # so they can only pop up in bought, sold, rfq, and quoted stuff
1079 $form->{no_sn_joins} = 'Y' if ( !$form->{bought} && !$form->{sold}
1080 && !$form->{rfq} && !$form->{quoted}
1081 && ($form->{l_serialnumber} || $form->{serialnumber}));
1083 # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
1084 # if any of these are ticked the behavior changes slightly for lastcost
1085 # since all those are aggregation checks for the legder tables this is an internal switch
1086 # refered to as ledgerchecks
1087 $form->{ledgerchecks} = 'Y' if ( $form->{bought} || $form->{sold} || $form->{onorder}
1088 || $form->{ordered} || $form->{rfq} || $form->{quoted});
1090 # if something should be activated if something else is active, enter it here
1091 my %dependencies = (
1092 onhand => [ qw(l_onhand) ],
1093 short => [ qw(l_onhand) ],
1094 onorder => [ qw(l_ordnumber) ],
1095 ordered => [ qw(l_ordnumber) ],
1096 rfq => [ qw(l_quonumber) ],
1097 quoted => [ qw(l_quonumber) ],
1098 bought => [ qw(l_invnumber) ],
1099 sold => [ qw(l_invnumber) ],
1100 ledgerchecks => [ qw(l_name) ],
1101 serialnumber => [ qw(l_serialnumber) ],
1102 no_sn_joins => [ qw(bought sold) ],
1105 # get name of partsgroup if id is given
1107 if ($form->{partsgroup_id}) {
1108 my $pg = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load;
1109 $pg_name = $pg->{'partsgroup'};
1112 # these strings get displayed at the top of the results to indicate the user which switches were used
1114 active => $locale->text('Active'),
1115 obsolete => $locale->text('Obsolete'),
1116 orphaned => $locale->text('Orphaned'),
1117 onhand => $locale->text('On Hand'),
1118 short => $locale->text('Short'),
1119 onorder => $locale->text('On Order'),
1120 ordered => $locale->text('Ordered'),
1121 rfq => $locale->text('RFQ'),
1122 quoted => $locale->text('Quoted'),
1123 bought => $locale->text('Bought'),
1124 sold => $locale->text('Sold'),
1125 transdatefrom => $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
1126 transdateto => $locale->text('To (time)') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
1127 partnumber => $locale->text('Part Number') . ": '$form->{partnumber}'",
1128 partsgroup => $locale->text('Group') . ": '$form->{partsgroup}'",
1129 partsgroup_id => $locale->text('Group') . ": '$pg_name'",
1130 serialnumber => $locale->text('Serial Number') . ": '$form->{serialnumber}'",
1131 description => $locale->text('Part Description') . ": '$form->{description}'",
1132 make => $locale->text('Make') . ": '$form->{make}'",
1133 model => $locale->text('Model') . ": '$form->{model}'",
1134 drawing => $locale->text('Drawing') . ": '$form->{drawing}'",
1135 microfiche => $locale->text('Microfiche') . ": '$form->{microfiche}'",
1136 l_soldtotal => $locale->text('Qty in Selected Records'),
1137 ean => $locale->text('EAN') . ": '$form->{ean}'",
1140 my @itemstatus_keys = qw(active obsolete orphaned onhand short);
1141 my @callback_keys = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
1142 drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto ean);
1144 # calculate dependencies
1145 for (@itemstatus_keys, @callback_keys) {
1146 next if ($form->{itemstatus} ne $_ && !$form->{$_});
1147 map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
1150 # generate callback and optionstrings
1152 for my $key (@itemstatus_keys, @callback_keys) {
1153 next if ($form->{itemstatus} ne $key && !$form->{$key});
1154 push @options, $optiontexts{$key};
1157 # special case for lastcost
1158 if ($form->{ledgerchecks}){
1159 # ledgerchecks don't know about sellprice or lastcost. they just return a
1160 # price. so rename sellprice to price, and drop lastcost.
1161 $column_defs{sellprice}{text} = $locale->text('Price');
1162 $form->{l_lastcost} = ""
1165 if ($form->{description}) {
1166 $description = $form->{description};
1167 $description =~ s/\n/<br>/g;
1170 if ($form->{l_linetotal}) {
1171 $form->{l_qty} = "Y";
1172 $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
1173 $form->{l_linetotallastcost} = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if $form->{l_lastcost};
1174 $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
1177 if ($form->{searchitems} eq 'service') {
1179 # remove bin, weight and rop from list
1180 map { $form->{"l_$_"} = "" } qw(bin weight rop);
1182 $form->{l_onhand} = "";
1184 # qty is irrelevant unless bought or sold
1185 if ( $form->{bought}
1190 || $form->{quoted}) {
1191 # $form->{l_onhand} = "Y";
1193 $form->{l_linetotalsellprice} = "";
1194 $form->{l_linetotallastcost} = "";
1198 # soldtotal doesn't make sense with more than one bsooqr option.
1199 # so reset it to sold (the most common option), and issue a warning
1200 my @bsooqr = qw(sold bought onorder ordered rfq quoted);
1201 if ($form->{l_subtotal} && 1 < grep { $form->{$_} } @bsooqr) {
1202 my $enabled = first { $form->{$_} } @bsooqr;
1203 $form->{$_} = '' for @bsooqr;
1204 $form->{$enabled} = 'Y';
1206 push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
1209 IC->all_parts(\%myconfig, \%$form);
1212 partnumber description partsgroup bin onhand rop soldtotal unit listprice
1213 linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
1214 priceupdate weight image drawing microfiche invnumber ordnumber quonumber
1215 transdate name serialnumber deliverydate ean projectnumber projectdescription
1218 my $pricegroups = SL::DB::Manager::Pricegroup->get_all(sort => 'id');
1219 my @pricegroup_columns;
1220 my %column_defs_pricegroups;
1221 if ($form->{l_pricegroups}) {
1222 @pricegroup_columns = map { "pricegroup_" . $_->id } @{ $pricegroups };
1223 %column_defs_pricegroups = map {
1224 "pricegroup_" . $_->id => {
1225 text => $::locale->text('Pricegroup') . ' ' . $_->pricegroup,
1228 } @{ $pricegroups };
1230 push @columns, @pricegroup_columns;
1232 my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
1233 my @searchable_custom_variables = grep { $_->{searchable} } @{ $cvar_configs };
1234 my %column_defs_cvars = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
1236 push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
1238 %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
1239 map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
1240 map { $column_defs{$_}->{align} = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal), @pricegroup_columns;
1242 my @hidden_variables = (qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups), @itemstatus_keys, @callback_keys,
1243 map({ "cvar_$_->{name}" } @searchable_custom_variables), map { "l_$_" } @columns);
1245 my $callback = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
1247 my @sort_full = qw(partnumber description onhand soldtotal deliverydate);
1248 my @sort_no_revers = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
1250 foreach my $col (@sort_full) {
1251 $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
1253 map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
1255 # add order to callback
1256 $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
1258 my $report = SL::ReportGenerator->new(\%myconfig, $form);
1260 my %attachment_basenames = (
1261 'part' => $locale->text('part_list'),
1262 'service' => $locale->text('service_list'),
1263 'assembly' => $locale->text('assembly_list'),
1266 $report->set_options('top_info_text' => $locale->text('Options') . ': ' . join(', ', grep $_, @options),
1267 'raw_bottom_info_text' => $form->parse_html_template('ic/generate_report_bottom'),
1268 'output_format' => 'HTML',
1269 'title' => $form->{title},
1270 'attachment_basename' => $attachment_basenames{$form->{searchitems}} . strftime('_%Y%m%d', localtime time),
1272 $report->set_options_from_form();
1273 $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
1275 $report->set_columns(%column_defs);
1276 $report->set_column_order(@columns);
1278 $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
1280 $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
1282 CVar->add_custom_variables_to_report('module' => 'IC',
1283 'trans_id_field' => 'id',
1284 'configs' => $cvar_configs,
1285 'column_defs' => \%column_defs,
1286 'data' => $form->{parts});
1288 CVar->add_custom_variables_to_report('module' => 'IC',
1289 'sub_module' => sub { $_[0]->{ioi} },
1290 'trans_id_field' => 'ioi_id',
1291 'configs' => $cvar_configs,
1292 'column_defs' => \%column_defs,
1293 'data' => $form->{parts});
1295 my @subtotal_columns = qw(sellprice listprice lastcost);
1296 my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
1297 my %totals = map { $_ => 0 } @subtotal_columns;
1299 my $same_item = @{ $form->{parts} } ? $form->{parts}[0]{ $form->{sort} } : undef;
1301 my $defaults = AM->get_defaults();
1304 foreach my $ref (@{ $form->{parts} }) {
1306 # fresh row, for inserting later
1307 my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
1309 $ref->{exchangerate} ||= 1;
1310 $ref->{price_factor} ||= 1;
1311 $ref->{sellprice} *= $ref->{exchangerate} / $ref->{price_factor};
1312 $ref->{listprice} *= $ref->{exchangerate} / $ref->{price_factor};
1313 $ref->{lastcost} *= $ref->{exchangerate} / $ref->{price_factor};
1315 # use this for assemblies
1316 my $soldtotal = $ref->{soldtotal};
1318 if ($ref->{assemblyitem}) {
1319 $row->{partnumber}{align} = 'right';
1320 $row->{soldtotal}{data} = 0;
1321 $soldtotal = 0 if ($form->{sold});
1324 my $edit_link = build_std_url('action=edit', 'id=' . E($ref->{id}), 'callback');
1325 $row->{partnumber}->{link} = $edit_link;
1326 $row->{description}->{link} = $edit_link;
1328 foreach (qw(sellprice listprice lastcost)) {
1329 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}, 2);
1330 $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
1332 foreach ( @pricegroup_columns ) {
1333 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{"$_"}, 2);
1337 map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
1339 $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
1341 if (!$ref->{assemblyitem}) {
1342 foreach my $col (@subtotal_columns) {
1343 $totals{$col} += $soldtotal * $ref->{$col};
1344 $subtotals{$col} += $soldtotal * $ref->{$col};
1347 $subtotals{soldtotal} += $soldtotal;
1351 if ($ref->{module} eq 'oe') {
1352 # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
1354 # | ist bestellt | Vom Kunde bestellt | -> edit_oe_ord_link
1355 # | Anfrage | Angebot | -> edit_oe_quo_link
1357 my $edit_oe_ord_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'purchase_order' : 'sales_order'), 'id=' . E($ref->{trans_id}), 'callback');
1358 my $edit_oe_quo_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'request_quotation' : 'sales_quotation'), 'id=' . E($ref->{trans_id}), 'callback');
1360 $row->{ordnumber}{link} = $edit_oe_ord_link;
1361 $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
1364 $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback');
1367 # set properties of images
1368 if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
1369 $row->{image}{data} = '';
1370 $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
1372 map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
1374 $report->add_data($row);
1376 my $next_ref = $form->{parts}[$idx + 1];
1378 # insert subtotal rows
1379 if (($form->{l_subtotal} eq 'Y') &&
1381 (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
1382 my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
1384 if (($form->{searchitems} ne 'assembly') || !$form->{bom}) {
1385 $row->{soldtotal}->{data} = $form->format_amount(\%myconfig, $subtotals{soldtotal});
1388 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
1389 map { $subtotals{$_} = 0 } ('soldtotal', @subtotal_columns);
1391 $report->add_data($row);
1393 $same_item = $next_ref->{ $form->{sort} };
1399 if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
1400 my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
1402 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
1404 $report->add_separator();
1405 $report->add_data($row);
1408 $report->generate_with_headers();
1410 $lxdebug->leave_sub();
1411 } #end generate_report
1413 sub parts_subtotal {
1414 $lxdebug->enter_sub();
1416 $auth->assert('part_service_assembly_edit');
1419 my ($column_index, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice) = @_;
1421 map { $column_data{$_} = "<td> </td>" } @{ $column_index };
1422 $$subtotalonhand = 0 if ($form->{searchitems} eq 'assembly' && $form->{bom});
1424 $column_data{onhand} =
1425 "<th class=listsubtotal align=right>"
1426 . $form->format_amount(\%myconfig, $$subtotalonhand)
1429 $column_data{linetotalsellprice} =
1430 "<th class=listsubtotal align=right>"
1431 . $form->format_amount(\%myconfig, $$subtotalsellprice, 2)
1433 $column_data{linetotallistprice} =
1434 "<th class=listsubtotal align=right>"
1435 . $form->format_amount(\%myconfig, $$subtotallistprice, 2)
1437 $column_data{linetotallastcost} =
1438 "<th class=listsubtotal align=right>"
1439 . $form->format_amount(\%myconfig, $$subtotallastcost, 2)
1442 $$subtotalonhand = 0;
1443 $$subtotalsellprice = 0;
1444 $$subtotallistprice = 0;
1445 $$subtotallastcost = 0;
1447 print "<tr class=listsubtotal>";
1449 map { print "\n$column_data{$_}" } @{ $column_index };
1455 $lxdebug->leave_sub();
1459 $lxdebug->enter_sub();
1461 $auth->assert('part_service_assembly_edit');
1463 # show history button
1464 $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
1465 #/show hhistory button
1466 IC->get_part(\%myconfig, \%$form);
1468 $form->{"original_partnumber"} = $form->{"partnumber"};
1470 my $title = 'Edit ' . ucfirst $form->{item};
1471 $form->{title} = $locale->text($title);
1476 $lxdebug->leave_sub();
1480 $lxdebug->enter_sub();
1482 $auth->assert('part_service_assembly_edit');
1484 IC->create_links("IC", \%myconfig, \%$form);
1487 map({ $form->{selectcurrency} .= "<option>$_\n" }
1488 split(/:/, $form->{currencies}));
1490 # parts and assemblies have the same links
1491 my $item = $form->{item};
1492 if ($form->{item} eq 'assembly') {
1496 # build the popup menus
1497 $form->{taxaccounts} = "";
1498 foreach my $key (keys %{ $form->{IC_links} }) {
1499 foreach my $ref (@{ $form->{IC_links}{$key} }) {
1501 # if this is a tax field
1502 if ($key =~ /IC_tax/) {
1503 if ($key =~ /\Q$item\E/) {
1504 $form->{taxaccounts} .= "$ref->{accno} ";
1505 $form->{"IC_tax_$ref->{accno}_description"} =
1506 "$ref->{accno}--$ref->{description}";
1509 if ($form->{amount}{ $ref->{accno} }) {
1510 $form->{"IC_tax_$ref->{accno}"} = "checked";
1513 $form->{"IC_tax_$ref->{accno}"} = "checked";
1518 $form->{"select$key"} .=
1519 "<option $ref->{selected}>$ref->{accno}--$ref->{description}\n";
1520 if ($form->{amount}{$key} eq $ref->{accno}) {
1521 $form->{$key} = "$ref->{accno}--$ref->{description}";
1527 chop $form->{taxaccounts};
1529 if (($form->{item} eq "part") || ($form->{item} eq "assembly")) {
1530 $form->{selectIC_income} = $form->{selectIC_sale};
1531 $form->{selectIC_expense} = $form->{selectIC_cogs};
1532 $form->{IC_income} = $form->{IC_sale};
1533 $form->{IC_expense} = $form->{IC_cogs};
1536 delete $form->{IC_links};
1537 delete $form->{amount};
1539 $form->get_partsgroup(\%myconfig, { all => 1 });
1541 $form->{partsgroup} = "$form->{partsgroup}--$form->{partsgroup_id}";
1543 if (@{ $form->{all_partsgroup} }) {
1544 $form->{selectpartsgroup} = qq|<option>\n|;
1545 map { $form->{selectpartsgroup} .= qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n| } @{ $form->{all_partsgroup} };
1548 if ($form->{item} eq 'assembly') {
1550 foreach my $i (1 .. $form->{assembly_rows}) {
1551 if ($form->{"partsgroup_id_$i"}) {
1552 $form->{"partsgroup_$i"} =
1553 qq|$form->{"partsgroup_$i"}--$form->{"partsgroup_id_$i"}|;
1556 $form->get_partsgroup(\%myconfig);
1558 if (@{ $form->{all_partsgroup} }) {
1559 $form->{selectassemblypartsgroup} = qq|<option>\n|;
1562 $form->{selectassemblypartsgroup} .=
1563 qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n|
1564 } @{ $form->{all_partsgroup} };
1567 $lxdebug->leave_sub();
1571 $lxdebug->enter_sub();
1573 $auth->assert('part_service_assembly_edit');
1575 $form->{pg_keys} = sub { "$_[0]->{partsgroup}--$_[0]->{id}" };
1576 $form->{description_area} = ($form->{rows} = $form->numtextrows($form->{description}, 40)) > 1;
1577 $form->{notes_rows} = max 4, $form->numtextrows($form->{notes}, 40), $form->numtextrows($form->{formel}, 40);
1579 map { $form->{"is_$_"} = ($form->{item} eq $_) } qw(part service assembly);
1580 map { $form->{$_} =~ s/"/"/g; } qw(unit);
1582 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
1583 'partsgroup' => 'all_partsgroup',
1584 'vendors' => 'ALL_VENDORS',);
1587 IC->retrieve_buchungsgruppen(\%myconfig, $form);
1588 @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
1590 # use JavaScript Calendar or not (yes!)
1591 $form->{jsscript} = 1;
1593 my $units = AM->retrieve_units(\%myconfig, $form);
1594 $form->{ALL_UNITS} = [ map +{ name => $_ }, sort { $units->{$a}{sortkey} <=> $units->{$b}{sortkey} } keys %$units ];
1596 $form->{defaults} = AM->get_defaults();
1598 $form->{fokus} = "ic.partnumber";
1600 $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'IC', 'trans_id' => $form->{id});
1602 CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}, show_disabled_message => 1)
1603 if (scalar @{ $form->{CUSTOM_VARIABLES} });
1606 #print $form->parse_html_template('ic/form_header', { ALL_PRICE_FACTORS => $form->{ALL_PRICE_FACTORS},
1607 # ALL_UNITS => $form->{ALL_UNITS},
1608 # BUCHUNGSGRUPPEN => $form->{BUCHUNGSGRUPPEN},
1609 # payment_terms => $form->{payment_terms},
1610 # all_partsgroup => $form->{all_partsgroup}});
1611 print $form->parse_html_template('ic/form_header');
1612 $lxdebug->leave_sub();
1616 $lxdebug->enter_sub();
1618 $auth->assert('part_service_assembly_edit');
1620 print $form->parse_html_template('ic/form_footer');
1622 $lxdebug->leave_sub();
1626 $lxdebug->enter_sub();
1629 my @mm_data = grep { any { $_ ne '' } @$_{qw(make model)} } map +{ make => $form->{"make_$_"}, model => $form->{"model_$_"}, lastcost => $form->{"lastcost_$_"}, lastupdate => $form->{"lastupdate_$_"}, sortorder => $form->{"sortorder_$_"} }, 1 .. $numrows;
1630 delete @{$form}{grep { m/^make_\d+/ || m/^model_\d+/ } keys %{ $form }};
1631 print $form->parse_html_template('ic/makemodel', { MM_DATA => [ @mm_data, {} ], mm_rows => scalar @mm_data + 1 });
1633 $lxdebug->leave_sub();
1637 $lxdebug->enter_sub();
1640 my ($nochange, $callback, $previousform, $linetotal, $line_purchase_price, $href);
1642 @column_index = qw(runningnumber qty unit bom partnumber description partsgroup lastcost total);
1644 if ($form->{previousform}) {
1646 @column_index = qw(qty unit bom partnumber description partsgroup total);
1650 $form->{old_callback} = $form->{callback};
1651 $callback = $form->{callback};
1652 $form->{callback} = "$form->{script}?action=display_form";
1655 map { delete $form->{$_} } qw(action header);
1657 # save form variables in a previousform variable
1658 my %form_to_save = map { ($_ => m/^ (?: listprice | sellprice | lastcost ) $/x ? $form->format_amount(\%myconfig, $form->{$_}) : $form->{$_}) }
1660 $previousform = $::auth->save_form_in_session(form => \%form_to_save);
1662 $form->{callback} = $callback;
1663 $form->{assemblytotal} = 0;
1664 $form->{assembly_purchase_price_total} = 0;
1665 $form->{weight} = 0;
1669 runningnumber => { text => $locale->text('No.'), nowrap => 1, width => '5%' },
1670 qty => { text => $locale->text('Qty'), nowrap => 1, width => '10%' },
1671 unit => { text => $locale->text('Unit'), nowrap => 1, width => '5%' },
1672 partnumber => { text => $locale->text('Part Number'), nowrap => 1, width => '20%' },
1673 description => { text => $locale->text('Part Description'), nowrap => 1, width => '50%' },
1674 lastcost => { text => $locale->text('Purchase Prices'), nowrap => 1, width => '50%' },
1675 total => { text => $locale->text('Sale Prices'), nowrap => 1, },
1676 bom => { text => $locale->text('BOM'), },
1677 partsgroup => { text => $locale->text('Group'), },
1682 for my $i (1 .. $numrows) {
1683 my (%row, @row_hiddens);
1685 $form->{"partnumber_$i"} =~ s/\"/"/g;
1687 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
1688 $line_purchase_price = $form->round_amount($form->{"lastcost_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
1689 $form->{assemblytotal} += $linetotal;
1690 $form->{assembly_purchase_price_total} += $line_purchase_price;
1691 $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
1692 $linetotal = $form->format_amount(\%myconfig, $linetotal, 2);
1693 $line_purchase_price = $form->format_amount(\%myconfig, $line_purchase_price, 2);
1694 $href = qq|$form->{script}?action=edit&id=$form->{"id_$i"}&rowcount=$i&previousform=$previousform|;
1695 map { $row{$_}{data} = "" } qw(qty unit partnumber description bom partsgroup runningnumber);
1698 if (($i >= 1) && ($i == $numrows)) {
1699 if (!$form->{previousform}) {
1700 $row{partnumber}{data} = qq|<input name="partnumber_$i" size=15 value="$form->{"partnumber_$i"}">|;
1701 $row{qty}{data} = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1702 $row{description}{data} = qq|<input name="description_$i" size=40 value="$form->{"description_$i"}">|;
1703 $row{partsgroup}{data} = qq|<input name="partsgroup_$i" size=10 value="$form->{"partsgroup_$i"}">|;
1707 if ($form->{previousform}) {
1708 push @row_hiddens, qw(qty bom);
1709 $row{partnumber}{data} = $form->{"partnumber_$i"};
1710 $row{qty}{data} = $form->{"qty_$i"};
1711 $row{bom}{data} = $form->{"bom_$i"} ? "x" : " ";
1712 $row{qty}{align} = 'right';
1714 $row{partnumber}{data} = qq|<a href=$href>$form->{"partnumber_$i"}</a>|;
1715 $row{qty}{data} = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1716 $row{runningnumber}{data} = qq|<input name="runningnumber_$i" size=3 value="$i">|;
1717 $row{bom}{data} = sprintf qq|<input name="bom_$i" type=checkbox class=checkbox value=1 %s>|,
1718 $form->{"bom_$i"} ? 'checked' : '';
1720 push @row_hiddens, qw(unit description partnumber partsgroup);
1721 $row{unit}{data} = $form->{"unit_$i"};
1722 #Bei der Artikelbeschreibung und Warengruppe können Sonderzeichen verwendet
1723 #werden, die den HTML Code stören. Daher sollen diese im Template escaped werden
1724 #dies geschieht, wenn die Variable escape gesetzt ist
1725 $row{description}{data} = $form->{"description_$i"};
1726 $row{description}{escape} = 1;
1727 $row{partsgroup}{data} = $form->{"partsgroup_$i"};
1728 $row{partsgroup}{escape} = 1;
1729 $row{bom}{align} = 'center';
1732 $row{lastcost}{data} = $line_purchase_price;
1733 $row{total}{data} = $linetotal;
1734 $row{lastcost}{align} = 'right';
1735 $row{total}{align} = 'right';
1736 $row{deliverydate}{align} = 'right';
1738 push @row_hiddens, qw(id sellprice lastcost weight price_factor_id price_factor);
1739 $row{hiddens} = [ map +{ name => "${_}_$i", value => $form->{"${_}_$i"} }, @row_hiddens ];
1744 print $form->parse_html_template('ic/assembly_row', { COLUMNS => \@column_index, ROWS => \@ROWS, HEADER => \%header });
1746 $lxdebug->leave_sub();
1750 $lxdebug->enter_sub();
1752 # parse pricegroups. and no, don't rely on check_form for this...
1753 map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
1755 # same for makemodel lastcosts
1756 # but parse_amount not necessary for assembly component lastcosts
1757 unless ($form->{item} eq "assembly") {
1758 map { $form->{"lastcost_$_"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$_"}) } 1 .. $form->{"makemodel_rows"};
1761 if ($form->{item} eq "assembly") {
1762 my $i = $form->{assembly_rows};
1764 # if last row is empty check the form otherwise retrieve item
1765 if ( ($form->{"partnumber_$i"} eq "")
1766 && ($form->{"description_$i"} eq "")
1767 && ($form->{"partsgroup_$i"} eq "")) {
1773 IC->assembly_item(\%myconfig, \%$form);
1775 my $rows = scalar @{ $form->{item_list} };
1778 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1781 $form->{makemodel_rows}--;
1782 select_item(mode => 'IC');
1785 map { $form->{item_list}[$i]{$_} =~ s/\"/"/g }
1786 qw(partnumber description unit partsgroup);
1787 map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} }
1788 keys %{ $form->{item_list}[0] };
1789 $form->{"runningnumber_$i"} = $form->{assembly_rows};
1790 $form->{assembly_rows}++;
1798 $form->{rowcount} = $i;
1799 $form->{assembly_rows}++;
1806 } elsif (($form->{item} eq 'part') || ($form->{item} eq 'service')) {
1810 $lxdebug->leave_sub();
1814 $lxdebug->enter_sub();
1816 $auth->assert('part_service_assembly_edit');
1818 my ($parts_id, %newform, $amount, $callback);
1820 # check if there is a part number - commented out, cause there is an automatic allocation of numbers
1821 # $form->isblank("partnumber", $locale->text(ucfirst $form->{item}." Part Number missing!"));
1823 # check if there is a description
1824 $form->isblank("description", $locale->text("Part Description missing!"));
1826 $form->error($locale->text("Inventory quantity must be zero before you can set this $form->{item} obsolete!"))
1827 if $form->{obsolete} && $form->{onhand} * 1 && $form->{item} ne 'service';
1829 if (!$form->{buchungsgruppen_id}) {
1830 $form->error($locale->text("Parts must have an entry type.") . " " .
1831 $locale->text("If you see this message, you most likely just setup your LX-Office and haven't added any entry types. If this is the case, the option is accessible for administrators in the System menu.")
1835 $form->error($locale->text('Description must not be empty!')) unless $form->{description};
1836 $form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
1839 if (IC->save(\%myconfig, \%$form) == 3) {
1840 $form->error($locale->text('Partnumber not unique!'));
1842 # saving the history
1843 if(!exists $form->{addition}) {
1844 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1845 $form->{addition} = "SAVED";
1846 $form->save_history;
1848 # /saving the history
1849 $parts_id = $form->{id};
1852 # load previous variables
1853 if ($form->{previousform}) {
1855 # save the new form variables before splitting previousform
1856 map { $newform{$_} = $form->{$_} } keys %$form;
1858 # don't trample on previous variables
1859 map { delete $form->{$_} } keys %newform;
1861 my $ic_cvar_configs = CVar->get_configs(module => 'IC');
1862 my @ic_cvar_fields = map { "cvar_$_->{name}" } @{ $ic_cvar_configs };
1864 # restore original values
1865 $::auth->restore_form_from_session($newform{previousform}, form => $form);
1866 $form->{taxaccounts} = $newform{taxaccount2};
1868 if ($form->{item} eq 'assembly') {
1870 # undo number formatting
1871 map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
1872 qw(weight listprice sellprice rop);
1874 $form->{assembly_rows}--;
1875 $i = $form->{assembly_rows};
1876 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1878 $form->{sellprice} -= $form->{"sellprice_$i"} * $form->{"qty_$i"};
1879 $form->{weight} -= $form->{"weight_$i"} * $form->{"qty_$i"};
1881 # change/add values for assembly item
1882 map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit weight listprice sellprice inventory_accno income_accno expense_accno price_factor_id);
1883 map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1885 # das ist __voll__ bekloppt, dass so auszurechnen jb 22.5.09
1886 #$form->{sellprice} += $form->{"sellprice_$i"} * $form->{"qty_$i"};
1887 $form->{weight} += $form->{"weight_$i"} * $form->{"qty_$i"};
1891 # set values for last invoice/order item
1892 $i = $form->{rowcount};
1893 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1895 map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit listprice inventory_accno income_accno expense_accno sellprice lastcost price_factor_id);
1896 map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1898 $form->{"longdescription_$i"} = $newform{notes};
1900 $form->{"sellprice_$i"} = $newform{lastcost} if ($form->{vendor_id});
1902 if ($form->{exchangerate} != 0) {
1903 $form->{"sellprice_$i"} /= $form->{exchangerate};
1906 map { $form->{"taxaccounts_$i"} .= "$_ " } split / /, $newform{taxaccount};
1907 chop $form->{"taxaccounts_$i"};
1908 foreach my $item (qw(description rate taxnumber)) {
1909 my $index = $form->{"taxaccounts_$i"} . "_$item";
1910 $form->{$index} = $newform{$index};
1913 # credit remaining calculation
1914 $amount = $form->{"sellprice_$i"} * (1 - $form->{"discount_$i"} / 100) * $form->{"qty_$i"};
1916 map { $form->{"${_}_base"} += $amount } (split / /, $form->{"taxaccounts_$i"});
1917 map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{"taxaccounts_$i"} if !$form->{taxincluded};
1919 $form->{creditremaining} -= $amount;
1921 # redo number formatting, because invoice parse them!
1922 map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}) } qw(weight listprice sellprice lastcost rop);
1925 $form->{"id_$i"} = $parts_id;
1927 # Get the actual price factor (not just the ID) for the marge calculation.
1928 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
1929 foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
1930 next if ($pfac->{id} != $newform{price_factor_id});
1931 $form->{"marge_price_factor_$i"} = $pfac->{factor};
1934 delete $form->{ALL_PRICE_FACTORS};
1936 delete $form->{action};
1938 # restore original callback
1939 $callback = $form->unescape($form->{callback});
1940 $form->{callback} = $form->unescape($form->{old_callback});
1941 delete $form->{old_callback};
1943 $form->{makemodel_rows}--;
1945 # put callback together
1946 foreach my $key (keys %$form) {
1948 # do single escape for Apache 2.0
1949 my $value = $form->escape($form->{$key}, 1);
1950 $callback .= qq|&$key=$value|;
1952 $form->{callback} = $callback;
1958 $lxdebug->leave_sub();
1962 $lxdebug->enter_sub();
1964 $auth->assert('part_service_assembly_edit');
1966 # saving the history
1967 if(!exists $form->{addition}) {
1968 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1969 $form->{addition} = "SAVED AS NEW";
1970 $form->save_history;
1972 # /saving the history
1974 if ($form->{"original_partnumber"} &&
1975 ($form->{"partnumber"} eq $form->{"original_partnumber"})) {
1976 $form->{partnumber} = "";
1979 $lxdebug->leave_sub();
1983 $lxdebug->enter_sub();
1985 $auth->assert('part_service_assembly_edit');
1987 # saving the history
1988 if(!exists $form->{addition}) {
1989 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1990 $form->{addition} = "DELETED";
1991 $form->save_history;
1993 # /saving the history
1994 my $rc = IC->delete(\%myconfig, \%$form);
1997 $form->redirect($locale->text('Item deleted!')) if ($rc > 0);
1998 $form->error($locale->text('Cannot delete item!'));
2000 $lxdebug->leave_sub();
2004 $lxdebug->enter_sub();
2006 $auth->assert('part_service_assembly_edit');
2011 pricegroup => $form->{"pricegroup_$_"},
2012 pricegroup_id => $form->{"pricegroup_id_$_"},
2013 price => $form->{"price_$_"},
2016 print $form->parse_html_template('ic/price_row', { PRICES => \@PRICES });
2018 $lxdebug->leave_sub();
2021 sub parts_language_selection {
2022 $lxdebug->enter_sub();
2024 $auth->assert('part_service_assembly_edit');
2026 my $languages = IC->retrieve_languages(\%myconfig, $form);
2028 if ($form->{language_values} ne "") {
2029 foreach my $item (split(/---\+\+\+---/, $form->{language_values})) {
2030 my ($language_id, $translation, $longdescription) = split(/--\+\+--/, $item);
2032 foreach my $language (@{ $languages }) {
2033 next unless ($language->{id} == $language_id);
2035 $language->{translation} = $translation;
2036 $language->{longdescription} = $longdescription;
2038 $language->{translation_area} = ($language->{translation_rows} = $form->numtextrows($language->{translation}, 40)) > 1;
2039 $language->{longdescription_rows} = max 4, $form->numtextrows($language->{longdescription}, 40);
2046 my @header_sort = qw(name longdescription);
2047 my %header_title = ( "name" => $locale->text("Name"),
2048 "longdescription" => $locale->text("Long Description"),
2052 map(+{ "column_title" => $header_title{$_},
2057 $form->{"title"} = $locale->text("Language Values");
2059 print $form->parse_html_template("ic/parts_language_selection", { "HEADER" => \@header,
2060 "LANGUAGES" => $languages, });
2062 $lxdebug->leave_sub();
2065 sub ajax_autocomplete {
2066 $main::lxdebug->enter_sub();
2068 my $form = $main::form;
2069 my %myconfig = %main::myconfig;
2071 $form->{column} = 'description' unless $form->{column} =~ /^partnumber|description$/;
2072 $form->{$form->{column}} = $form->{q} || '';
2073 $form->{limit} = ($form->{limit} * 1) || 10;
2074 $form->{searchitems} ||= '';
2076 my @results = IC->all_parts(\%myconfig, $form);
2078 print $form->ajax_response_header(),
2079 $form->parse_html_template('ic/ajax_autocomplete');
2081 $main::lxdebug->leave_sub();
2084 sub back_to_record {
2088 delete @{$::form}{qw(action action_add action_back_to_record back_sub description item notes partnumber sellprice taxaccount2 unit vc)};
2090 $::auth->restore_form_from_session($::form->{previousform}, clobber => 1);
2091 $::form->{rowcount}--;
2092 $::form->{action} = 'display_form';
2093 $::form->{callback} = $::form->{script} . '?' . join('&', map { $::form->escape($_) . '=' . $::form->escape($::form->{$_}) } sort keys %{ $::form });
2097 sub continue { call_sub($form->{"nextsub"}); }
2100 my $action = first { $::form->{"action_${_}"} } qw(add back_to_record);
2101 $::form->error($::locale->text('No action defined.')) unless $action;
2103 $::form->{dispatched_action} = $action;