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::Helper::Flash;
42 use SL::ReportGenerator;
50 our ($form, $locale, %myconfig, $lxdebug, $auth);
52 require "bin/mozilla/io.pl";
53 require "bin/mozilla/invoice_io.pl";
54 require "bin/mozilla/common.pl";
55 require "bin/mozilla/reportgenerator.pl";
60 # type=submit $locale->text('Add Part')
61 # type=submit $locale->text('Add Service')
62 # type=submit $locale->text('Add Assembly')
63 # type=submit $locale->text('Edit Part')
64 # type=submit $locale->text('Edit Service')
65 # type=submit $locale->text('Edit Assembly')
66 # $locale->text('Parts')
67 # $locale->text('Services')
68 # $locale->text('Inventory quantity must be zero before you can set this part obsolete!')
69 # $locale->text('Inventory quantity must be zero before you can set this assembly obsolete!')
70 # $locale->text('Part Number missing!')
71 # $locale->text('Service Number missing!')
72 # $locale->text('Assembly Number missing!')
73 # $locale->text('ea');
78 $lxdebug->enter_sub();
80 $auth->assert('part_service_assembly_edit');
82 my $title = 'Add ' . ucfirst $form->{item};
83 $form->{title} = $locale->text($title);
84 $form->{callback} = "$form->{script}?action=add&item=$form->{item}" unless $form->{callback};
85 $form->{unit_changeable} = 1;
87 IC->get_pricegroups(\%myconfig, \%$form);
91 $lxdebug->leave_sub();
95 $lxdebug->enter_sub();
97 $auth->assert('part_service_assembly_edit');
99 $form->{revers} = 0; # switch for backward sorting
100 $form->{lastsort} = ""; # memory for which table was sort at last time
101 $form->{ndxs_counter} = 0; # counter for added entries to top100
103 my %is_xyz = map { +"is_$_" => ($form->{searchitems} eq $_) } qw(part service assembly);
105 $form->{title} = (ucfirst $form->{searchitems}) . "s";
106 $form->{title} = $locale->text($form->{title});
107 $form->{title} = $locale->text('Assemblies') if ($is_xyz{is_assembly});
109 $form->{jsscript} = 1;
111 $form->{CUSTOM_VARIABLES} = CVar->get_configs('module' => 'IC');
112 ($form->{CUSTOM_VARIABLES_FILTER_CODE},
113 $form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables' => $form->{CUSTOM_VARIABLES},
114 'include_prefix' => 'l_',
115 'include_value' => 'Y');
119 $form->get_lists('partsgroup' => 'ALL_PARTSGROUPS');
120 print $form->parse_html_template('ic/search', { %is_xyz,
121 dateformat => $myconfig{dateformat},
122 limit => $myconfig{vclimit}, });
124 $lxdebug->leave_sub();
127 sub search_update_prices {
128 $lxdebug->enter_sub();
130 $auth->assert('part_service_assembly_edit');
132 my $pricegroups = IC->get_pricegroups(\%myconfig, \%$form);
134 $form->{title} = $locale->text('Update Prices');
138 print $form->parse_html_template('ic/search_update_prices', { PRICE_ROWS => $pricegroups });
140 $lxdebug->leave_sub();
143 sub confirm_price_update {
144 $lxdebug->enter_sub();
146 $auth->assert('part_service_assembly_edit');
149 my $value_found = undef;
151 foreach my $idx (qw(sellprice listprice), (1..$form->{price_rows})) {
152 my $name = $idx =~ m/\d/ ? $form->{"pricegroup_${idx}"} : $idx eq 'sellprice' ? $locale->text('Sell Price') : $locale->text('List Price');
153 my $type = $idx =~ m/\d/ ? $form->{"pricegroup_type_${idx}"} : $form->{"${idx}_type"};
154 my $value_idx = $idx =~ m/\d/ ? "price_${idx}" : $idx;
155 my $value = $form->parse_amount(\%myconfig, $form->{$value_idx});
157 if ((0 > $value) && ($type eq 'percent')) {
158 push @errors, $locale->text('You cannot adjust the price for pricegroup "#1" by a negative percentage.', $name);
160 } elsif (!$value && ($form->{$value_idx} ne '')) {
161 push @errors, $locale->text('No valid number entered for pricegroup "#1".', $name);
163 } elsif (0 < $value) {
168 push @errors, $locale->text('No prices will be updated because no prices have been entered.') if (!$value_found);
170 my $num_matches = IC->get_num_matches_for_priceupdate();
175 $form->show_generic_error(join('<br>', @errors), 'back_button' => 1);
178 $form->{nextsub} = "update_prices";
180 map { delete $form->{$_} } qw(action header);
182 print $form->parse_html_template('ic/confirm_price_update', { HIDDENS => [ map { name => $_, value => $form->{$_} }, keys %$form ],
183 num_matches => $num_matches });
185 $lxdebug->leave_sub();
189 $lxdebug->enter_sub();
191 $auth->assert('part_service_assembly_edit');
193 my $num_updated = IC->update_prices(\%myconfig, \%$form);
195 if (-1 != $num_updated) {
196 $form->redirect($locale->text('#1 prices were updated.', $num_updated));
198 $form->error($locale->text('Could not update prices!'));
201 $lxdebug->leave_sub();
205 # $lxdebug->enter_sub();
207 # $auth->assert('part_service_assembly_edit');
209 # our ($j, $lastndx);
212 # $form->{title} = $locale->text('Top 100 hinzufuegen');
216 # push @custom_hiddens, qw(searchitems title bom titel revers lastsort sort ndxs_counter extras);
217 # push @custom_hiddens, qw(itemstatus l_linetotal l_partnumber l_description l_onhand l_unit l_sellprice l_linetotalsellprice);
219 # +{ name => 'row', value => $j },
220 # +{ name => 'nextsub', value => 'item_selected' },
221 # +{ name => 'test', value => 'item_selected' },
222 # +{ name => 'lastndx', value => $lastndx },
223 # map(+{ name => $_, value => $form->{$_} }, @custom_hiddens),
226 # my ($partnumber, $description, $unit, $sellprice, $soldtotal);
227 # # if choice set data
228 ## if ($form->{ndx}) {
229 ## for my $i (0 .. $form->{ndxs_counter}) {
231 ## # insert data into top100
232 ## push @{ $form->{parts} },
234 ## partnumber => $form->{"totop100_partnumber_$j"},
235 ## description => $form->{"totop100_description_$j"},
236 ## unit => $form->{"totop100_unit_$j"},
237 ## sellprice => $form->{"totop100_sellprice_$j"},
238 ## soldtotal => $form->{"totop100_soldtotal_$j"},
245 # # set data for next page
246 # for my $i (1 .. $form->{ndxs_counter}) {
247 # $partnumber = $form->{"totop100_partnumber_$i"};
248 # $description = $form->{"totop100_description_$i"};
249 # $unit = $form->{"totop100_unit_$i"};
250 # $sellprice = $form->{"totop100_sellprice_$i"};
251 # $soldtotal = $form->{"totop100_soldtotal_$i"};
254 # totop100_partnumber => $form->{"totop100_partnumber_$i"},
255 # totop100_description => $form->{"totop100_description_$i"},
256 # totop100_unit => $form->{"totop100_unit_$i"},
257 # totop100_sellprice => $form->{"totop100_sellprice_$i"},
258 # totop100_soldtotal => $form->{"totop100_soldtotal_$i"},
262 ##<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
263 ##<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
264 ##<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
265 ##<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
266 ##<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
270 # print $form->parse_html_template('ic/choice', +{ HIDDENS => \@HIDDENS, PARTS => \@PARTS });
272 # $lxdebug->leave_sub();
276 # $lxdebug->enter_sub();
278 # $auth->assert('part_service_assembly_edit');
281 # our ($partnumber, $description, $unit, $sellprice, $soldtotal);
283 # my @sortorders = ("", "partnumber", "description", "all");
284 # my $sortorder = $sortorders[($form->{description} ? 2 : 0) + ($form->{partnumber} ? 1 : 0)];
285 # IC->get_parts(\%myconfig, \%$form, $sortorder);
287 # $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;
728 <th class=listtop colspan=$colspan>$form->{title}</th>
732 <tr><td colspan=$colspan>$option</td></tr>
734 <tr class=listheading>
737 map { print "\n$column_header{$_}" } @column_index;
743 # add order to callback
744 $form->{callback} = $callback .= "&sort=$form->{sort}";
746 # escape callback for href
747 $callback = $form->escape($callback);
749 if (@{ $form->{parts} }) {
750 $sameitem = $form->{parts}->[0]->{ $form->{sort} };
753 # insert numbers for top100
755 foreach my $ref (@{ $form->{parts} }) {
760 # if avaible -> insert choice here
761 if (($form->{ndxs_counter}) > 0) {
762 for (my $i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
763 $partnumber = $form->{"totop100_partnumber_$i"};
764 $description = $form->{"totop100_description_$i"};
765 $unit = $form->{"totop100_unit_$i"};
766 $sellprice = $form->{"totop100_sellprice_$i"};
767 $soldtotal = $form->{"totop100_soldtotal_$i"};
770 <input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
771 <input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
772 <input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
773 <input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
774 <input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
778 push @{ $form->{parts} },
780 partnumber => "$partnumber",
781 description => "$description",
783 sellprice => "$sellprice",
784 soldtotal => "$soldtotal" };
787 # build data for columns
789 foreach my $ref (@{ $form->{parts} }) {
791 if ($form->{l_subtotal} eq 'Y' && !$ref->{assemblyitem}) {
792 if ($sameitem ne $ref->{ $form->{sort} }) {
793 parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
794 $sameitem = $ref->{ $form->{sort} };
798 $ref->{exchangerate} = 1 unless $ref->{exchangerate};
799 $ref->{sellprice} *= $ref->{exchangerate};
800 $ref->{listprice} *= $ref->{exchangerate};
801 $ref->{lastcost} *= $ref->{exchangerate};
803 # use this for assemblies
804 $onhand = $ref->{onhand};
807 if ($ref->{assemblyitem}) {
809 $onhand = 0 if ($form->{sold});
812 $ref->{description} =~ s/\n/<br>/g;
814 $column_data{number} =
816 . $form->format_amount(\%myconfig, $ref->{number})
818 $column_data{partnumber} =
819 "<td align=$align>$ref->{partnumber} </a></td>";
820 $column_data{description} = "<td>$ref->{description} </td>";
821 $column_data{partsgroup} = "<td>$ref->{partsgroup} </td>";
823 $column_data{onhand} =
825 . $form->format_amount(\%myconfig, $ref->{onhand})
827 $column_data{sellprice} =
829 . $form->format_amount(\%myconfig, $ref->{sellprice})
831 $column_data{listprice} =
833 . $form->format_amount(\%myconfig, $ref->{listprice})
835 $column_data{lastcost} =
837 . $form->format_amount(\%myconfig, $ref->{lastcost})
840 $column_data{linetotalsellprice} = "<td align=right>"
841 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{sellprice}, 2)
843 $column_data{linetotallastcost} = "<td align=right>"
844 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{lastcost}, 2)
846 $column_data{linetotallistprice} = "<td align=right>"
847 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{listprice}, 2)
850 if (!$ref->{assemblyitem}) {
851 $totalsellprice += $onhand * $ref->{sellprice};
852 $totallastcost += $onhand * $ref->{lastcost};
853 $totallistprice += $onhand * $ref->{listprice};
855 $subtotalonhand += $onhand;
856 $subtotalsellprice += $onhand * $ref->{sellprice};
857 $subtotallastcost += $onhand * $ref->{lastcost};
858 $subtotallistprice += $onhand * $ref->{listprice};
863 . $form->format_amount(\%myconfig, $ref->{rop}) . "</td>";
864 $column_data{weight} =
866 . $form->format_amount(\%myconfig, $ref->{weight})
868 $column_data{unit} = "<td>$ref->{unit} </td>";
869 $column_data{bin} = "<td>$ref->{bin} </td>";
870 $column_data{priceupdate} = "<td>$ref->{priceupdate} </td>";
872 $column_data{invnumber} =
873 ($ref->{module} ne 'oe')
874 ? "<td><a href=$ref->{module}.pl?action=edit&type=invoice&id=$ref->{trans_id}&callback=$callback>$ref->{invnumber}</a></td>"
875 : "<td>$ref->{invnumber}</td>";
876 $column_data{ordnumber} =
877 ($ref->{module} eq 'oe')
878 ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{ordnumber}</a></td>"
879 : "<td>$ref->{ordnumber}</td>";
880 $column_data{quonumber} =
881 ($ref->{module} eq 'oe' && !$ref->{ordnumber})
882 ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{quonumber}</a></td>"
883 : "<td>$ref->{quonumber}</td>";
885 $column_data{name} = "<td>$ref->{name}</td>";
887 $column_data{image} =
889 ? "<td><a href=$ref->{image}><img src=$ref->{image} height=32 border=0></a></td>"
891 $column_data{drawing} =
893 ? "<td><a href=$ref->{drawing}>$ref->{drawing}</a></td>"
895 $column_data{microfiche} =
897 ? "<td><a href=$ref->{microfiche}>$ref->{microfiche}</a></td>"
900 $column_data{serialnumber} = "<td>$ref->{serialnumber}</td>";
902 $column_data{soldtotal} = "<td align=right>$ref->{soldtotal}</td>";
906 print "<tr class=listrow$i>";
908 map { print "\n$column_data{$_}" } @column_index;
915 if ($form->{l_subtotal} eq 'Y') {
916 parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
919 if ($form->{"l_linetotal"}) {
920 map { $column_data{$_} = "<td> </td>" } @column_index;
921 $column_data{linetotalsellprice} =
922 "<th class=listtotal align=right>"
923 . $form->format_amount(\%myconfig, $totalsellprice, 2)
925 $column_data{linetotallastcost} =
926 "<th class=listtotal align=right>"
927 . $form->format_amount(\%myconfig, $totallastcost, 2)
929 $column_data{linetotallistprice} =
930 "<th class=listtotal align=right>"
931 . $form->format_amount(\%myconfig, $totallistprice, 2)
934 print "<tr class=listtotal>";
936 map { print "\n$column_data{$_}" } @column_index;
943 <tr><td colspan=$colspan><hr size=3 noshade></td></tr>
952 <form method=post action=$form->{script}>
954 <input type=hidden name=itemstatus value="$form->{itemstatus}">
955 <input type=hidden name=l_linetotal value="$form->{l_linetotal}">
956 <input type=hidden name=l_partnumber value="$form->{l_partnumber}">
957 <input type=hidden name=l_description value="$form->{l_description}">
958 <input type=hidden name=l_onhand value="$form->{l_onhand}">
959 <input type=hidden name=l_unit value="$form->{l_unit}">
960 <input type=hidden name=l_sellprice value="$form->{l_sellprice}">
961 <input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
962 <input type=hidden name=sort value="$form->{sort}">
963 <input type=hidden name=revers value="$form->{revers}">
964 <input type=hidden name=lastsort value="$form->{lastsort}">
965 <input type=hidden name=parts value="$form->{parts}">
967 <input type=hidden name=bom value="$form->{bom}">
968 <input type=hidden name=titel value="$form->{titel}">
969 <input type=hidden name=searchitems value="$form->{searchitems}">|;
974 <!-- <input type=hidden name=ndxs_counter value="$form->{ndxs_counter}">-->
976 <!-- <input class=submit type=submit name=action value="|
977 . $locale->text('choice') . qq|"> -->
982 $lxdebug->leave_sub();
987 # Warning, deep magic ahead.
988 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
990 # flags coming from the form:
992 # searchitems=part revers=0 lastsort=''
995 # partnumber ean description partsgroup serialnumber make model drawing microfiche
996 # transdatefrom transdateto
999 # itemstatus = active | onhand | short | obsolete | orphaned
1000 # action = continue | top100
1003 # bought sold onorder ordered rfq quoted
1004 # l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
1005 # l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
1006 # l_partsgroup l_subtotal l_soldtotal l_deliverydate l_pricegroups
1009 # nextsub revers lastsort sort ndxs_counter
1011 sub generate_report {
1012 $lxdebug->enter_sub();
1014 $auth->assert('part_service_assembly_edit');
1016 my ($revers, $lastsort, $description);
1018 my $cvar_configs = CVar->get_configs('module' => 'IC');
1020 $form->{title} = (ucfirst $form->{searchitems}) . "s";
1021 $form->{title} =~ s/ys$/ies/;
1022 $form->{title} = $locale->text($form->{title});
1025 'bin' => { 'text' => $locale->text('Bin'), },
1026 'deliverydate' => { 'text' => $locale->text('deliverydate'), },
1027 'description' => { 'text' => $locale->text('Part Description'), },
1028 'notes' => { 'text' => $locale->text('Notes'), },
1029 'drawing' => { 'text' => $locale->text('Drawing'), },
1030 'ean' => { 'text' => $locale->text('EAN'), },
1031 'image' => { 'text' => $locale->text('Image'), },
1032 'invnumber' => { 'text' => $locale->text('Invoice Number'), },
1033 'lastcost' => { 'text' => $locale->text('Last Cost'), },
1034 'linetotallastcost' => { 'text' => $locale->text('Extended'), },
1035 'linetotallistprice' => { 'text' => $locale->text('Extended'), },
1036 'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
1037 'listprice' => { 'text' => $locale->text('List Price'), },
1038 'microfiche' => { 'text' => $locale->text('Microfiche'), },
1039 'name' => { 'text' => $locale->text('Name'), },
1040 'onhand' => { 'text' => $locale->text('Stocked Qty'), },
1041 'ordnumber' => { 'text' => $locale->text('Order Number'), },
1042 'partnumber' => { 'text' => $locale->text('Part Number'), },
1043 'partsgroup' => { 'text' => $locale->text('Group'), },
1044 'priceupdate' => { 'text' => $locale->text('Updated'), },
1045 'quonumber' => { 'text' => $locale->text('Quotation'), },
1046 'rop' => { 'text' => $locale->text('ROP'), },
1047 'sellprice' => { 'text' => $locale->text('Sell Price'), },
1048 'serialnumber' => { 'text' => $locale->text('Serial Number'), },
1049 'soldtotal' => { 'text' => $locale->text('Qty in Selected Records'), },
1050 'transdate' => { 'text' => $locale->text('Transdate'), },
1051 'unit' => { 'text' => $locale->text('Unit'), },
1052 'weight' => { 'text' => $locale->text('Weight'), },
1053 'projectnumber' => { 'text' => $locale->text('Project Number'), },
1054 'projectdescription' => { 'text' => $locale->text('Project Description'), },
1057 $revers = $form->{revers};
1058 $lastsort = $form->{lastsort};
1060 # sorting and direction of sorting
1061 # ToDO: change this to the simpler field+direction method
1062 if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
1063 $form->{revers} = 0;
1064 $form->{lastsort} = "partnumber";
1065 $form->{sort} = "partnumber";
1067 if ($form->{lastsort} eq $form->{sort}) {
1068 $form->{revers} = 1 - $form->{revers};
1070 $form->{revers} = 0;
1071 $form->{lastsort} = $form->{sort};
1075 # special case if we have a serialnumber limit search
1076 # serialnumbers are only given in invoices and orders,
1077 # so they can only pop up in bought, sold, rfq, and quoted stuff
1078 $form->{no_sn_joins} = 'Y' if ( !$form->{bought} && !$form->{sold}
1079 && !$form->{rfq} && !$form->{quoted}
1080 && ($form->{l_serialnumber} || $form->{serialnumber}));
1082 # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
1083 # if any of these are ticked the behavior changes slightly for lastcost
1084 # since all those are aggregation checks for the legder tables this is an internal switch
1085 # refered to as ledgerchecks
1086 $form->{ledgerchecks} = 'Y' if ( $form->{bought} || $form->{sold} || $form->{onorder}
1087 || $form->{ordered} || $form->{rfq} || $form->{quoted});
1089 # if something should be activated if something else is active, enter it here
1090 my %dependencies = (
1091 onhand => [ qw(l_onhand) ],
1092 short => [ qw(l_onhand) ],
1093 onorder => [ qw(l_ordnumber) ],
1094 ordered => [ qw(l_ordnumber) ],
1095 rfq => [ qw(l_quonumber) ],
1096 quoted => [ qw(l_quonumber) ],
1097 bought => [ qw(l_invnumber) ],
1098 sold => [ qw(l_invnumber) ],
1099 ledgerchecks => [ qw(l_name) ],
1100 serialnumber => [ qw(l_serialnumber) ],
1101 no_sn_joins => [ qw(bought sold) ],
1104 # get name of partsgroup if id is given
1106 if ($form->{partsgroup_id}) {
1107 my $pg = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load;
1108 $pg_name = $pg->{'partsgroup'};
1111 # these strings get displayed at the top of the results to indicate the user which switches were used
1113 active => $locale->text('Active'),
1114 obsolete => $locale->text('Obsolete'),
1115 orphaned => $locale->text('Orphaned'),
1116 onhand => $locale->text('On Hand'),
1117 short => $locale->text('Short'),
1118 onorder => $locale->text('On Order'),
1119 ordered => $locale->text('Ordered'),
1120 rfq => $locale->text('RFQ'),
1121 quoted => $locale->text('Quoted'),
1122 bought => $locale->text('Bought'),
1123 sold => $locale->text('Sold'),
1124 transdatefrom => $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
1125 transdateto => $locale->text('To (time)') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
1126 partnumber => $locale->text('Part Number') . ": '$form->{partnumber}'",
1127 partsgroup => $locale->text('Group') . ": '$form->{partsgroup}'",
1128 partsgroup_id => $locale->text('Group') . ": '$pg_name'",
1129 serialnumber => $locale->text('Serial Number') . ": '$form->{serialnumber}'",
1130 description => $locale->text('Part Description') . ": '$form->{description}'",
1131 make => $locale->text('Make') . ": '$form->{make}'",
1132 model => $locale->text('Model') . ": '$form->{model}'",
1133 drawing => $locale->text('Drawing') . ": '$form->{drawing}'",
1134 microfiche => $locale->text('Microfiche') . ": '$form->{microfiche}'",
1135 l_soldtotal => $locale->text('Qty in Selected Records'),
1136 ean => $locale->text('EAN') . ": '$form->{ean}'",
1139 my @itemstatus_keys = qw(active obsolete orphaned onhand short);
1140 my @callback_keys = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
1141 drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto ean);
1143 # calculate dependencies
1144 for (@itemstatus_keys, @callback_keys) {
1145 next if ($form->{itemstatus} ne $_ && !$form->{$_});
1146 map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
1149 # generate callback and optionstrings
1151 for my $key (@itemstatus_keys, @callback_keys) {
1152 next if ($form->{itemstatus} ne $key && !$form->{$key});
1153 push @options, $optiontexts{$key};
1156 # special case for lastcost
1157 if ($form->{ledgerchecks}){
1158 # ledgerchecks don't know about sellprice or lastcost. they just return a
1159 # price. so rename sellprice to price, and drop lastcost.
1160 $column_defs{sellprice}{text} = $locale->text('Price');
1161 $form->{l_lastcost} = ""
1164 if ($form->{description}) {
1165 $description = $form->{description};
1166 $description =~ s/\n/<br>/g;
1169 if ($form->{l_linetotal}) {
1170 $form->{l_qty} = "Y";
1171 $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
1172 $form->{l_linetotallastcost} = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if $form->{l_lastcost};
1173 $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
1176 if ($form->{searchitems} eq 'service') {
1178 # remove bin, weight and rop from list
1179 map { $form->{"l_$_"} = "" } qw(bin weight rop);
1181 $form->{l_onhand} = "";
1183 # qty is irrelevant unless bought or sold
1184 if ( $form->{bought}
1189 || $form->{quoted}) {
1190 # $form->{l_onhand} = "Y";
1192 $form->{l_linetotalsellprice} = "";
1193 $form->{l_linetotallastcost} = "";
1197 # soldtotal doesn't make sense with more than one bsooqr option.
1198 # so reset it to sold (the most common option), and issue a warning
1199 my @bsooqr = qw(sold bought onorder ordered rfq quoted);
1200 if ($form->{l_subtotal} && 1 < grep { $form->{$_} } @bsooqr) {
1201 my $enabled = first { $form->{$_} } @bsooqr;
1202 $form->{$_} = '' for @bsooqr;
1203 $form->{$enabled} = 'Y';
1205 push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
1208 IC->all_parts(\%myconfig, \%$form);
1211 partnumber description notes partsgroup bin onhand rop soldtotal unit listprice
1212 linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
1213 priceupdate weight image drawing microfiche invnumber ordnumber quonumber
1214 transdate name serialnumber deliverydate ean projectnumber projectdescription
1217 my $pricegroups = SL::DB::Manager::Pricegroup->get_all(sort => 'id');
1218 my @pricegroup_columns;
1219 my %column_defs_pricegroups;
1220 if ($form->{l_pricegroups}) {
1221 @pricegroup_columns = map { "pricegroup_" . $_->id } @{ $pricegroups };
1222 %column_defs_pricegroups = map {
1223 "pricegroup_" . $_->id => {
1224 text => $::locale->text('Pricegroup') . ' ' . $_->pricegroup,
1227 } @{ $pricegroups };
1229 push @columns, @pricegroup_columns;
1231 my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
1232 my @searchable_custom_variables = grep { $_->{searchable} } @{ $cvar_configs };
1233 my %column_defs_cvars = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
1235 push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
1237 %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
1238 map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
1239 map { $column_defs{$_}->{align} = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal), @pricegroup_columns;
1241 my @hidden_variables = (
1242 qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups),
1245 map({ "cvar_$_->{name}" } @searchable_custom_variables),
1246 map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
1247 map({ "l_$_" } @columns),
1250 my $callback = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
1252 my @sort_full = qw(partnumber description onhand soldtotal deliverydate);
1253 my @sort_no_revers = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
1255 foreach my $col (@sort_full) {
1256 $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
1258 map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
1260 # add order to callback
1261 $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
1263 my $report = SL::ReportGenerator->new(\%myconfig, $form);
1265 my %attachment_basenames = (
1266 'part' => $locale->text('part_list'),
1267 'service' => $locale->text('service_list'),
1268 'assembly' => $locale->text('assembly_list'),
1271 $report->set_options('top_info_text' => $locale->text('Options') . ': ' . join(', ', grep $_, @options),
1272 'raw_bottom_info_text' => $form->parse_html_template('ic/generate_report_bottom'),
1273 'output_format' => 'HTML',
1274 'title' => $form->{title},
1275 'attachment_basename' => $attachment_basenames{$form->{searchitems}} . strftime('_%Y%m%d', localtime time),
1277 $report->set_options_from_form();
1278 $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
1280 $report->set_columns(%column_defs);
1281 $report->set_column_order(@columns);
1283 $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
1285 $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
1287 CVar->add_custom_variables_to_report('module' => 'IC',
1288 'trans_id_field' => 'id',
1289 'configs' => $cvar_configs,
1290 'column_defs' => \%column_defs,
1291 'data' => $form->{parts});
1293 CVar->add_custom_variables_to_report('module' => 'IC',
1294 'sub_module' => sub { $_[0]->{ioi} },
1295 'trans_id_field' => 'ioi_id',
1296 'configs' => $cvar_configs,
1297 'column_defs' => \%column_defs,
1298 'data' => $form->{parts});
1300 my @subtotal_columns = qw(sellprice listprice lastcost);
1301 my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
1302 my %totals = map { $_ => 0 } @subtotal_columns;
1304 my $same_item = @{ $form->{parts} } ? $form->{parts}[0]{ $form->{sort} } : undef;
1306 my $defaults = AM->get_defaults();
1309 foreach my $ref (@{ $form->{parts} }) {
1311 # fresh row, for inserting later
1312 my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
1314 $ref->{exchangerate} ||= 1;
1315 $ref->{price_factor} ||= 1;
1316 $ref->{sellprice} *= $ref->{exchangerate} / $ref->{price_factor};
1317 $ref->{listprice} *= $ref->{exchangerate} / $ref->{price_factor};
1318 $ref->{lastcost} *= $ref->{exchangerate} / $ref->{price_factor};
1320 # use this for assemblies
1321 my $soldtotal = $ref->{soldtotal};
1323 if ($ref->{assemblyitem}) {
1324 $row->{partnumber}{align} = 'right';
1325 $row->{soldtotal}{data} = 0;
1326 $soldtotal = 0 if ($form->{sold});
1329 my $edit_link = build_std_url('action=edit', 'id=' . E($ref->{id}), 'callback');
1330 $row->{partnumber}->{link} = $edit_link;
1331 $row->{description}->{link} = $edit_link;
1333 foreach (qw(sellprice listprice lastcost)) {
1334 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}, 2);
1335 $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
1337 foreach ( @pricegroup_columns ) {
1338 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{"$_"}, 2);
1342 map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
1344 $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
1346 if (!$ref->{assemblyitem}) {
1347 foreach my $col (@subtotal_columns) {
1348 $totals{$col} += $soldtotal * $ref->{$col};
1349 $subtotals{$col} += $soldtotal * $ref->{$col};
1352 $subtotals{soldtotal} += $soldtotal;
1356 if ($ref->{module} eq 'oe') {
1357 # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
1359 # | ist bestellt | Vom Kunde bestellt | -> edit_oe_ord_link
1360 # | Anfrage | Angebot | -> edit_oe_quo_link
1362 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');
1363 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');
1365 $row->{ordnumber}{link} = $edit_oe_ord_link;
1366 $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
1369 $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback');
1372 # set properties of images
1373 if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
1374 $row->{image}{data} = '';
1375 $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
1377 map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
1379 $report->add_data($row);
1381 my $next_ref = $form->{parts}[$idx + 1];
1383 # insert subtotal rows
1384 if (($form->{l_subtotal} eq 'Y') &&
1386 (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
1387 my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
1389 if (($form->{searchitems} ne 'assembly') || !$form->{bom}) {
1390 $row->{soldtotal}->{data} = $form->format_amount(\%myconfig, $subtotals{soldtotal});
1393 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
1394 map { $subtotals{$_} = 0 } ('soldtotal', @subtotal_columns);
1396 $report->add_data($row);
1398 $same_item = $next_ref->{ $form->{sort} };
1404 if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
1405 my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
1407 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
1409 $report->add_separator();
1410 $report->add_data($row);
1413 $report->generate_with_headers();
1415 $lxdebug->leave_sub();
1416 } #end generate_report
1418 sub parts_subtotal {
1419 $lxdebug->enter_sub();
1421 $auth->assert('part_service_assembly_edit');
1424 my ($column_index, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice) = @_;
1426 map { $column_data{$_} = "<td> </td>" } @{ $column_index };
1427 $$subtotalonhand = 0 if ($form->{searchitems} eq 'assembly' && $form->{bom});
1429 $column_data{onhand} =
1430 "<th class=listsubtotal align=right>"
1431 . $form->format_amount(\%myconfig, $$subtotalonhand)
1434 $column_data{linetotalsellprice} =
1435 "<th class=listsubtotal align=right>"
1436 . $form->format_amount(\%myconfig, $$subtotalsellprice, 2)
1438 $column_data{linetotallistprice} =
1439 "<th class=listsubtotal align=right>"
1440 . $form->format_amount(\%myconfig, $$subtotallistprice, 2)
1442 $column_data{linetotallastcost} =
1443 "<th class=listsubtotal align=right>"
1444 . $form->format_amount(\%myconfig, $$subtotallastcost, 2)
1447 $$subtotalonhand = 0;
1448 $$subtotalsellprice = 0;
1449 $$subtotallistprice = 0;
1450 $$subtotallastcost = 0;
1452 print "<tr class=listsubtotal>";
1454 map { print "\n$column_data{$_}" } @{ $column_index };
1460 $lxdebug->leave_sub();
1464 $lxdebug->enter_sub();
1466 $auth->assert('part_service_assembly_edit');
1468 # show history button
1469 $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
1470 #/show hhistory button
1471 IC->get_part(\%myconfig, \%$form);
1473 $form->{"original_partnumber"} = $form->{"partnumber"};
1475 my $title = 'Edit ' . ucfirst $form->{item};
1476 $form->{title} = $locale->text($title);
1481 $lxdebug->leave_sub();
1485 $lxdebug->enter_sub();
1487 $auth->assert('part_service_assembly_edit');
1489 IC->create_links("IC", \%myconfig, \%$form);
1492 map({ $form->{selectcurrency} .= "<option>$_\n" } $::form->get_all_currencies());
1494 # parts and assemblies have the same links
1495 my $item = $form->{item};
1496 if ($form->{item} eq 'assembly') {
1500 # build the popup menus
1501 $form->{taxaccounts} = "";
1502 foreach my $key (keys %{ $form->{IC_links} }) {
1503 foreach my $ref (@{ $form->{IC_links}{$key} }) {
1505 # if this is a tax field
1506 if ($key =~ /IC_tax/) {
1507 if ($key =~ /\Q$item\E/) {
1508 $form->{taxaccounts} .= "$ref->{accno} ";
1509 $form->{"IC_tax_$ref->{accno}_description"} =
1510 "$ref->{accno}--$ref->{description}";
1513 if ($form->{amount}{ $ref->{accno} }) {
1514 $form->{"IC_tax_$ref->{accno}"} = "checked";
1517 $form->{"IC_tax_$ref->{accno}"} = "checked";
1522 $form->{"select$key"} .=
1523 "<option $ref->{selected}>$ref->{accno}--$ref->{description}\n";
1524 if ($form->{amount}{$key} eq $ref->{accno}) {
1525 $form->{$key} = "$ref->{accno}--$ref->{description}";
1531 chop $form->{taxaccounts};
1533 if (($form->{item} eq "part") || ($form->{item} eq "assembly")) {
1534 $form->{selectIC_income} = $form->{selectIC_sale};
1535 $form->{selectIC_expense} = $form->{selectIC_cogs};
1536 $form->{IC_income} = $form->{IC_sale};
1537 $form->{IC_expense} = $form->{IC_cogs};
1540 delete $form->{IC_links};
1541 delete $form->{amount};
1543 $form->get_partsgroup(\%myconfig, { all => 1 });
1545 $form->{partsgroup} = "$form->{partsgroup}--$form->{partsgroup_id}";
1547 if (@{ $form->{all_partsgroup} }) {
1548 $form->{selectpartsgroup} = qq|<option>\n|;
1549 map { $form->{selectpartsgroup} .= qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n| } @{ $form->{all_partsgroup} };
1552 if ($form->{item} eq 'assembly') {
1554 foreach my $i (1 .. $form->{assembly_rows}) {
1555 if ($form->{"partsgroup_id_$i"}) {
1556 $form->{"partsgroup_$i"} =
1557 qq|$form->{"partsgroup_$i"}--$form->{"partsgroup_id_$i"}|;
1560 $form->get_partsgroup(\%myconfig);
1562 if (@{ $form->{all_partsgroup} }) {
1563 $form->{selectassemblypartsgroup} = qq|<option>\n|;
1566 $form->{selectassemblypartsgroup} .=
1567 qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n|
1568 } @{ $form->{all_partsgroup} };
1571 $lxdebug->leave_sub();
1575 $lxdebug->enter_sub();
1577 $auth->assert('part_service_assembly_edit');
1579 $form->{pg_keys} = sub { "$_[0]->{partsgroup}--$_[0]->{id}" };
1580 $form->{description_area} = ($form->{rows} = $form->numtextrows($form->{description}, 40)) > 1;
1581 $form->{notes_rows} = max 4, $form->numtextrows($form->{notes}, 40), $form->numtextrows($form->{formel}, 40);
1583 map { $form->{"is_$_"} = ($form->{item} eq $_) } qw(part service assembly);
1584 map { $form->{$_} =~ s/"/"/g; } qw(unit);
1586 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
1587 'partsgroup' => 'all_partsgroup',
1588 'vendors' => 'ALL_VENDORS',
1589 'warehouses' => { 'key' => 'WAREHOUSES',
1590 'bins' => 'BINS', });
1591 # leerer wert für Lager und Lagerplatz korrekt einstellt
1592 # ID 0 sollte in Ordnung sein, da der Zähler sowieso höher ist
1593 my $no_default_bin_entry = { 'id' => '0', description => '--', 'BINS' => [ { id => '0', description => ''} ] };
1594 push @ { $form->{WAREHOUSES} }, $no_default_bin_entry;
1595 if (my $max = scalar @{ $form->{WAREHOUSES} }) {
1596 my ($default_warehouse_id, $default_bin_id);
1597 if ($form->{action} eq 'add') { # default only for new entries
1598 $default_warehouse_id = $::instance_conf->get_default_warehouse_id;
1599 $default_bin_id = $::instance_conf->get_default_bin_id;
1601 $form->{warehouse_id} ||= $default_warehouse_id || $form->{WAREHOUSES}->[$max -1]->{id};
1602 $form->{bin_id} ||= $default_bin_id || $form->{WAREHOUSES}->[$max -1]->{BINS}->[0]->{id};
1606 IC->retrieve_buchungsgruppen(\%myconfig, $form);
1607 @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
1609 if (($form->{partnumber} ne '') && !SL::TransNumber->new(number => $form->{partnumber}, type => $form->{item}, id => $form->{id})->is_unique) {
1610 flash('info', $::locale->text('This partnumber is not unique. You should change it.'));
1613 # use JavaScript Calendar or not (yes!)
1614 $form->{jsscript} = 1;
1616 my $units = AM->retrieve_units(\%myconfig, $form);
1617 $form->{ALL_UNITS} = [ map +{ name => $_ }, sort { $units->{$a}{sortkey} <=> $units->{$b}{sortkey} } keys %$units ];
1619 $form->{defaults} = AM->get_defaults();
1621 $::request->{layout}->focus("#partnumber");
1623 $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'IC', 'trans_id' => $form->{id});
1625 CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}, show_disabled_message => 1)
1626 if (scalar @{ $form->{CUSTOM_VARIABLES} });
1629 #print $form->parse_html_template('ic/form_header', { ALL_PRICE_FACTORS => $form->{ALL_PRICE_FACTORS},
1630 # ALL_UNITS => $form->{ALL_UNITS},
1631 # BUCHUNGSGRUPPEN => $form->{BUCHUNGSGRUPPEN},
1632 # payment_terms => $form->{payment_terms},
1633 # all_partsgroup => $form->{all_partsgroup}});
1634 print $form->parse_html_template('ic/form_header');
1635 $lxdebug->leave_sub();
1639 $lxdebug->enter_sub();
1641 $auth->assert('part_service_assembly_edit');
1643 print $form->parse_html_template('ic/form_footer');
1645 $lxdebug->leave_sub();
1649 $lxdebug->enter_sub();
1652 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;
1653 delete @{$form}{grep { m/^make_\d+/ || m/^model_\d+/ } keys %{ $form }};
1654 print $form->parse_html_template('ic/makemodel', { MM_DATA => [ @mm_data, {} ], mm_rows => scalar @mm_data + 1 });
1656 $lxdebug->leave_sub();
1660 $lxdebug->enter_sub();
1663 my ($nochange, $callback, $previousform, $linetotal, $line_purchase_price, $href);
1665 @column_index = qw(runningnumber qty unit bom partnumber description partsgroup lastcost total);
1667 if ($form->{previousform}) {
1669 @column_index = qw(qty unit bom partnumber description partsgroup total);
1673 $form->{old_callback} = $form->{callback};
1674 $callback = $form->{callback};
1675 $form->{callback} = "$form->{script}?action=display_form";
1678 map { delete $form->{$_} } qw(action header);
1680 # save form variables in a previousform variable
1681 my %form_to_save = map { ($_ => m/^ (?: listprice | sellprice | lastcost ) $/x ? $form->format_amount(\%myconfig, $form->{$_}) : $form->{$_}) }
1683 $previousform = $::auth->save_form_in_session(form => \%form_to_save);
1685 $form->{callback} = $callback;
1686 $form->{assemblytotal} = 0;
1687 $form->{assembly_purchase_price_total} = 0;
1688 $form->{weight} = 0;
1692 runningnumber => { text => $locale->text('No.'), nowrap => 1, width => '5%', align => 'left',},
1693 qty => { text => $locale->text('Qty'), nowrap => 1, width => '10%', align => 'left',},
1694 unit => { text => $locale->text('Unit'), nowrap => 1, width => '5%', align => 'left',},
1695 partnumber => { text => $locale->text('Part Number'), nowrap => 1, width => '20%', align => 'left',},
1696 description => { text => $locale->text('Part Description'), nowrap => 1, width => '50%', align => 'left',},
1697 lastcost => { text => $locale->text('Purchase Prices'), nowrap => 1, width => '50%', align => 'right',},
1698 total => { text => $locale->text('Sale Prices'), nowrap => 1, align => 'right',},
1699 bom => { text => $locale->text('BOM'), align => 'center',},
1700 partsgroup => { text => $locale->text('Group'), align => 'left',},
1705 for my $i (1 .. $numrows) {
1706 my (%row, @row_hiddens);
1708 $form->{"partnumber_$i"} =~ s/\"/"/g;
1710 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
1711 $line_purchase_price = $form->round_amount($form->{"lastcost_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
1712 $form->{assemblytotal} += $linetotal;
1713 $form->{assembly_purchase_price_total} += $line_purchase_price;
1714 $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
1715 $linetotal = $form->format_amount(\%myconfig, $linetotal, 2);
1716 $line_purchase_price = $form->format_amount(\%myconfig, $line_purchase_price, 2);
1717 $href = build_std_url("action=edit", qq|id=$form->{"id_$i"}|, "rowcount=$numrows", "currow=$i", "previousform=$previousform");
1718 map { $row{$_}{data} = "" } qw(qty unit partnumber description bom partsgroup runningnumber);
1721 if (($i >= 1) && ($i == $numrows)) {
1722 if (!$form->{previousform}) {
1723 $row{partnumber}{data} = qq|<input name="partnumber_$i" size=15 value="$form->{"partnumber_$i"}">|;
1724 $row{qty}{data} = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1725 $row{description}{data} = qq|<input name="description_$i" size=40 value="$form->{"description_$i"}">|;
1726 $row{partsgroup}{data} = qq|<input name="partsgroup_$i" size=10 value="$form->{"partsgroup_$i"}">|;
1730 if ($form->{previousform}) {
1731 push @row_hiddens, qw(qty bom);
1732 $row{partnumber}{data} = $form->{"partnumber_$i"};
1733 $row{qty}{data} = $form->{"qty_$i"};
1734 $row{bom}{data} = $form->{"bom_$i"} ? "x" : " ";
1735 $row{qty}{align} = 'right';
1737 $row{partnumber}{data} = qq|$form->{"partnumber_$i"}|;
1738 $row{partnumber}{link} = $href;
1739 $row{qty}{data} = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1740 $row{runningnumber}{data} = qq|<input name="runningnumber_$i" size=3 value="$i">|;
1741 $row{bom}{data} = sprintf qq|<input name="bom_$i" type=checkbox class=checkbox value=1 %s>|,
1742 $form->{"bom_$i"} ? 'checked' : '';
1744 push @row_hiddens, qw(unit description partnumber partsgroup);
1745 $row{unit}{data} = $form->{"unit_$i"};
1746 #Bei der Artikelbeschreibung und Warengruppe können Sonderzeichen verwendet
1747 #werden, die den HTML Code stören. Daher sollen diese im Template escaped werden
1748 #dies geschieht, wenn die Variable escape gesetzt ist
1749 $row{description}{data} = $form->{"description_$i"};
1750 $row{description}{escape} = 1;
1751 $row{partsgroup}{data} = $form->{"partsgroup_$i"};
1752 $row{partsgroup}{escape} = 1;
1753 $row{bom}{align} = 'center';
1756 $row{lastcost}{data} = $line_purchase_price;
1757 $row{total}{data} = $linetotal;
1758 $row{lastcost}{align} = 'right';
1759 $row{total}{align} = 'right';
1760 $row{deliverydate}{align} = 'right';
1762 push @row_hiddens, qw(id sellprice lastcost weight price_factor_id price_factor);
1763 $row{hiddens} = [ map +{ name => "${_}_$i", value => $form->{"${_}_$i"} }, @row_hiddens ];
1768 print $form->parse_html_template('ic/assembly_row', { COLUMNS => \@column_index, ROWS => \@ROWS, HEADER => \%header });
1770 $lxdebug->leave_sub();
1774 $lxdebug->enter_sub();
1776 # parse pricegroups. and no, don't rely on check_form for this...
1777 map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
1778 $form->{sellprice} = $form->parse_amount(\%myconfig, $form->{sellprice});
1780 # same for makemodel lastcosts
1781 # but parse_amount not necessary for assembly component lastcosts
1782 unless ($form->{item} eq "assembly") {
1783 map { $form->{"lastcost_$_"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$_"}) } 1 .. $form->{"makemodel_rows"};
1785 $form->{listprice} = $form->parse_amount(\%myconfig, $form->{listprice});
1787 if ($form->{item} eq "assembly") {
1788 my $i = $form->{assembly_rows};
1790 # if last row is empty check the form otherwise retrieve item
1791 if ( ($form->{"partnumber_$i"} eq "")
1792 && ($form->{"description_$i"} eq "")
1793 && ($form->{"partsgroup_$i"} eq "")) {
1799 IC->assembly_item(\%myconfig, \%$form);
1801 my $rows = scalar @{ $form->{item_list} };
1804 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1807 $form->{makemodel_rows}--;
1808 select_item(mode => 'IC');
1811 map { $form->{item_list}[$i]{$_} =~ s/\"/"/g }
1812 qw(partnumber description unit partsgroup);
1813 map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} }
1814 keys %{ $form->{item_list}[0] };
1815 $form->{"runningnumber_$i"} = $form->{assembly_rows};
1816 $form->{assembly_rows}++;
1824 $form->{rowcount} = $i;
1825 $form->{assembly_rows}++;
1832 } elsif (($form->{item} eq 'part') || ($form->{item} eq 'service')) {
1836 $lxdebug->leave_sub();
1840 $lxdebug->enter_sub();
1842 $auth->assert('part_service_assembly_edit');
1844 my ($parts_id, %newform, $amount, $callback);
1846 # check if there is a part number - commented out, cause there is an automatic allocation of numbers
1847 # $form->isblank("partnumber", $locale->text(ucfirst $form->{item}." Part Number missing!"));
1849 # check if there is a description
1850 $form->isblank("description", $locale->text("Part Description missing!"));
1852 $form->error($locale->text("Inventory quantity must be zero before you can set this $form->{item} obsolete!"))
1853 if $form->{obsolete} && $form->{onhand} * 1 && $form->{item} ne 'service';
1855 if (!$form->{buchungsgruppen_id}) {
1856 $form->error($locale->text("Parts must have an entry type.") . " " .
1857 $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.")
1861 $form->error($locale->text('Description must not be empty!')) unless $form->{description};
1862 $form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
1864 # undef warehouse_id if the empty value is selected
1865 if ( ($form->{warehouse_id} == 0) && ($form->{bin_id} == 0) ) {
1866 undef $form->{warehouse_id};
1867 undef $form->{bin_id};
1870 if (IC->save(\%myconfig, \%$form) == 3) {
1871 $form->error($locale->text('Partnumber not unique!'));
1873 # saving the history
1874 if(!exists $form->{addition}) {
1875 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1876 $form->{addition} = "SAVED";
1877 $form->save_history;
1879 # /saving the history
1880 $parts_id = $form->{id};
1883 # load previous variables
1884 if ($form->{previousform}) {
1886 # save the new form variables before splitting previousform
1887 map { $newform{$_} = $form->{$_} } keys %$form;
1889 # don't trample on previous variables
1890 map { delete $form->{$_} } keys %newform;
1892 my $ic_cvar_configs = CVar->get_configs(module => 'IC');
1893 my @ic_cvar_fields = map { "cvar_$_->{name}" } @{ $ic_cvar_configs };
1895 # restore original values
1896 $::auth->restore_form_from_session($newform{previousform}, form => $form);
1897 $form->{taxaccounts} = $newform{taxaccount2};
1899 if ($form->{item} eq 'assembly') {
1901 # undo number formatting
1902 map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
1903 qw(weight listprice sellprice rop);
1905 $form->{assembly_rows}--;
1906 if ($newform{currow}) {
1907 $i = $newform{currow};
1909 $i = $form->{assembly_rows};
1911 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1913 $form->{sellprice} -= $form->{"sellprice_$i"} * $form->{"qty_$i"};
1914 $form->{weight} -= $form->{"weight_$i"} * $form->{"qty_$i"};
1916 # change/add values for assembly item
1917 map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit weight listprice sellprice inventory_accno income_accno expense_accno price_factor_id);
1918 map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1920 # das ist __voll__ bekloppt, dass so auszurechnen jb 22.5.09
1921 #$form->{sellprice} += $form->{"sellprice_$i"} * $form->{"qty_$i"};
1922 $form->{weight} += $form->{"weight_$i"} * $form->{"qty_$i"};
1926 # set values for last invoice/order item
1927 $i = $form->{rowcount};
1928 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1930 map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit listprice inventory_accno income_accno expense_accno sellprice lastcost price_factor_id);
1931 map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1933 $form->{"longdescription_$i"} = $newform{notes};
1935 $form->{"sellprice_$i"} = $newform{lastcost} if ($form->{vendor_id});
1937 if ($form->{exchangerate} != 0) {
1938 $form->{"sellprice_$i"} /= $form->{exchangerate};
1941 map { $form->{"taxaccounts_$i"} .= "$_ " } split / /, $newform{taxaccount};
1942 chop $form->{"taxaccounts_$i"};
1943 foreach my $item (qw(description rate taxnumber)) {
1944 my $index = $form->{"taxaccounts_$i"} . "_$item";
1945 $form->{$index} = $newform{$index};
1948 # credit remaining calculation
1949 $amount = $form->{"sellprice_$i"} * (1 - $form->{"discount_$i"} / 100) * $form->{"qty_$i"};
1951 map { $form->{"${_}_base"} += $amount } (split / /, $form->{"taxaccounts_$i"});
1952 map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{"taxaccounts_$i"} if !$form->{taxincluded};
1954 $form->{creditremaining} -= $amount;
1956 # redo number formatting, because invoice parse them!
1957 map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}) } qw(weight listprice sellprice lastcost rop);
1960 $form->{"id_$i"} = $parts_id;
1962 # Get the actual price factor (not just the ID) for the marge calculation.
1963 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
1964 foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
1965 next if ($pfac->{id} != $newform{price_factor_id});
1966 $form->{"marge_price_factor_$i"} = $pfac->{factor};
1969 delete $form->{ALL_PRICE_FACTORS};
1971 delete $form->{action};
1973 # restore original callback
1974 $callback = $form->unescape($form->{callback});
1975 $form->{callback} = $form->unescape($form->{old_callback});
1976 delete $form->{old_callback};
1978 $form->{makemodel_rows}--;
1980 # put callback together
1981 foreach my $key (keys %$form) {
1983 # do single escape for Apache 2.0
1984 my $value = $form->escape($form->{$key}, 1);
1985 $callback .= qq|&$key=$value|;
1987 $form->{callback} = $callback;
1993 $lxdebug->leave_sub();
1997 $lxdebug->enter_sub();
1999 $auth->assert('part_service_assembly_edit');
2001 # saving the history
2002 if(!exists $form->{addition}) {
2003 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
2004 $form->{addition} = "SAVED AS NEW";
2005 $form->save_history;
2007 # /saving the history
2009 if ($form->{"original_partnumber"} &&
2010 ($form->{"partnumber"} eq $form->{"original_partnumber"})) {
2011 $form->{partnumber} = "";
2014 $lxdebug->leave_sub();
2018 $lxdebug->enter_sub();
2020 $auth->assert('part_service_assembly_edit');
2022 # saving the history
2023 if(!exists $form->{addition}) {
2024 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
2025 $form->{addition} = "DELETED";
2026 $form->save_history;
2028 # /saving the history
2029 my $rc = IC->delete(\%myconfig, \%$form);
2032 $form->redirect($locale->text('Item deleted!')) if ($rc > 0);
2033 $form->error($locale->text('Cannot delete item!'));
2035 $lxdebug->leave_sub();
2039 $lxdebug->enter_sub();
2041 $auth->assert('part_service_assembly_edit');
2046 pricegroup => $form->{"pricegroup_$_"},
2047 pricegroup_id => $form->{"pricegroup_id_$_"},
2048 price => $form->{"price_$_"},
2051 print $form->parse_html_template('ic/price_row', { PRICES => \@PRICES });
2053 $lxdebug->leave_sub();
2056 sub parts_language_selection {
2057 $lxdebug->enter_sub();
2059 $auth->assert('part_service_assembly_edit');
2061 my $languages = IC->retrieve_languages(\%myconfig, $form);
2063 if ($form->{language_values} ne "") {
2064 foreach my $item (split(/---\+\+\+---/, $form->{language_values})) {
2065 my ($language_id, $translation, $longdescription) = split(/--\+\+--/, $item);
2067 foreach my $language (@{ $languages }) {
2068 next unless ($language->{id} == $language_id);
2070 $language->{translation} = $translation;
2071 $language->{longdescription} = $longdescription;
2073 $language->{translation_area} = ($language->{translation_rows} = $form->numtextrows($language->{translation}, 40)) > 1;
2074 $language->{longdescription_rows} = max 4, $form->numtextrows($language->{longdescription}, 40);
2081 my @header_sort = qw(name longdescription);
2082 my %header_title = ( "name" => $locale->text("Name"),
2083 "longdescription" => $locale->text("Long Description"),
2087 map(+{ "column_title" => $header_title{$_},
2092 $form->{"title"} = $locale->text("Language Values");
2094 print $form->parse_html_template("ic/parts_language_selection", { "HEADER" => \@header,
2095 "LANGUAGES" => $languages, });
2097 $lxdebug->leave_sub();
2100 sub ajax_autocomplete {
2101 $main::lxdebug->enter_sub();
2103 my $form = $main::form;
2104 my %myconfig = %main::myconfig;
2106 $form->{column} = 'description' unless $form->{column} =~ /^partnumber|description$/;
2107 $form->{$form->{column}} = $form->{q} || '';
2108 $form->{limit} = ($form->{limit} * 1) || 10;
2109 $form->{searchitems} ||= '';
2111 my @results = IC->all_parts(\%myconfig, $form);
2113 print $form->ajax_response_header(),
2114 $form->parse_html_template('ic/ajax_autocomplete');
2116 $main::lxdebug->leave_sub();
2119 sub back_to_record {
2123 delete @{$::form}{qw(action action_add action_back_to_record back_sub description item notes partnumber sellprice taxaccount2 unit vc)};
2125 $::auth->restore_form_from_session($::form->{previousform}, clobber => 1);
2126 $::form->{rowcount}--;
2127 $::form->{action} = 'display_form';
2128 $::form->{callback} = $::form->{script} . '?' . join('&', map { $::form->escape($_) . '=' . $::form->escape($::form->{$_}) } sort keys %{ $::form });
2132 sub continue { call_sub($form->{"nextsub"}); }
2135 my $action = first { $::form->{"action_${_}"} } qw(add back_to_record);
2136 $::form->error($::locale->text('No action defined.')) unless $action;
2138 $::form->{dispatched_action} = $action;