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;
43 use SL::ReportGenerator;
51 our ($form, $locale, %myconfig, $lxdebug, $auth);
53 require "bin/mozilla/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_details');
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->{CUSTOM_VARIABLES} = CVar->get_configs('module' => 'IC');
110 ($form->{CUSTOM_VARIABLES_FILTER_CODE},
111 $form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables' => $form->{CUSTOM_VARIABLES},
112 'include_prefix' => 'l_',
113 'include_value' => 'Y');
117 $form->get_lists('partsgroup' => 'ALL_PARTSGROUPS');
118 print $form->parse_html_template('ic/search', { %is_xyz,
119 dateformat => $myconfig{dateformat},
120 limit => $myconfig{vclimit}, });
122 $lxdebug->leave_sub();
125 sub search_update_prices {
126 $lxdebug->enter_sub();
128 $auth->assert('part_service_assembly_edit');
130 my $pricegroups = IC->get_pricegroups(\%myconfig, \%$form);
132 $form->{title} = $locale->text('Update Prices');
136 print $form->parse_html_template('ic/search_update_prices', { PRICE_ROWS => $pricegroups });
138 $lxdebug->leave_sub();
141 sub confirm_price_update {
142 $lxdebug->enter_sub();
144 $auth->assert('part_service_assembly_edit');
147 my $value_found = undef;
149 foreach my $idx (qw(sellprice listprice), (1..$form->{price_rows})) {
150 my $name = $idx =~ m/\d/ ? $form->{"pricegroup_${idx}"} : $idx eq 'sellprice' ? $locale->text('Sell Price') : $locale->text('List Price');
151 my $type = $idx =~ m/\d/ ? $form->{"pricegroup_type_${idx}"} : $form->{"${idx}_type"};
152 my $value_idx = $idx =~ m/\d/ ? "price_${idx}" : $idx;
153 my $value = $form->parse_amount(\%myconfig, $form->{$value_idx});
155 if ((0 > $value) && ($type eq 'percent')) {
156 push @errors, $locale->text('You cannot adjust the price for pricegroup "#1" by a negative percentage.', $name);
158 } elsif (!$value && ($form->{$value_idx} ne '')) {
159 push @errors, $locale->text('No valid number entered for pricegroup "#1".', $name);
161 } elsif (0 < $value) {
166 push @errors, $locale->text('No prices will be updated because no prices have been entered.') if (!$value_found);
168 my $num_matches = IC->get_num_matches_for_priceupdate();
173 $form->show_generic_error(join('<br>', @errors), 'back_button' => 1);
176 $form->{nextsub} = "update_prices";
178 map { delete $form->{$_} } qw(action header);
180 print $form->parse_html_template('ic/confirm_price_update', { HIDDENS => [ map { name => $_, value => $form->{$_} }, keys %$form ],
181 num_matches => $num_matches });
183 $lxdebug->leave_sub();
187 $lxdebug->enter_sub();
189 $auth->assert('part_service_assembly_edit');
191 my $num_updated = IC->update_prices(\%myconfig, \%$form);
193 if (-1 != $num_updated) {
194 $form->redirect($locale->text('#1 prices were updated.', $num_updated));
196 $form->error($locale->text('Could not update prices!'));
199 $lxdebug->leave_sub();
203 # $lxdebug->enter_sub();
205 # $auth->assert('part_service_assembly_edit');
207 # our ($j, $lastndx);
210 # $form->{title} = $locale->text('Top 100 hinzufuegen');
214 # push @custom_hiddens, qw(searchitems title bom titel revers lastsort sort ndxs_counter extras);
215 # push @custom_hiddens, qw(itemstatus l_linetotal l_partnumber l_description l_onhand l_unit l_sellprice l_linetotalsellprice);
217 # +{ name => 'row', value => $j },
218 # +{ name => 'nextsub', value => 'item_selected' },
219 # +{ name => 'test', value => 'item_selected' },
220 # +{ name => 'lastndx', value => $lastndx },
221 # map(+{ name => $_, value => $form->{$_} }, @custom_hiddens),
224 # my ($partnumber, $description, $unit, $sellprice, $soldtotal);
225 # # if choice set data
226 ## if ($form->{ndx}) {
227 ## for my $i (0 .. $form->{ndxs_counter}) {
229 ## # insert data into top100
230 ## push @{ $form->{parts} },
232 ## partnumber => $form->{"totop100_partnumber_$j"},
233 ## description => $form->{"totop100_description_$j"},
234 ## unit => $form->{"totop100_unit_$j"},
235 ## sellprice => $form->{"totop100_sellprice_$j"},
236 ## soldtotal => $form->{"totop100_soldtotal_$j"},
243 # # set data for next page
244 # for my $i (1 .. $form->{ndxs_counter}) {
245 # $partnumber = $form->{"totop100_partnumber_$i"};
246 # $description = $form->{"totop100_description_$i"};
247 # $unit = $form->{"totop100_unit_$i"};
248 # $sellprice = $form->{"totop100_sellprice_$i"};
249 # $soldtotal = $form->{"totop100_soldtotal_$i"};
252 # totop100_partnumber => $form->{"totop100_partnumber_$i"},
253 # totop100_description => $form->{"totop100_description_$i"},
254 # totop100_unit => $form->{"totop100_unit_$i"},
255 # totop100_sellprice => $form->{"totop100_sellprice_$i"},
256 # totop100_soldtotal => $form->{"totop100_soldtotal_$i"},
260 ##<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
261 ##<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
262 ##<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
263 ##<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
264 ##<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
268 # print $form->parse_html_template('ic/choice', +{ HIDDENS => \@HIDDENS, PARTS => \@PARTS });
270 # $lxdebug->leave_sub();
274 # $lxdebug->enter_sub();
276 # $auth->assert('part_service_assembly_edit');
279 # our ($partnumber, $description, $unit, $sellprice, $soldtotal);
281 # my @sortorders = ("", "partnumber", "description", "all");
282 # my $sortorder = $sortorders[($form->{description} ? 2 : 0) + ($form->{partnumber} ? 1 : 0)];
283 # IC->get_parts(\%myconfig, \%$form, $sortorder);
285 # $form->{title} = $locale->text('Top 100 hinzufuegen');
290 # <h1>| . $locale->text('choice part') . qq|</h1>
291 # <form method=post action=ic.pl>
293 # <tr class=listheading>
295 # <th class=listheading>| . $locale->text('Part Number') . qq|</th>
296 # <th class=listheading>| . $locale->text('Part Description') . qq|</th>
297 # <th class=listheading>| . $locale->text('Unit of measure') . qq|</th>
298 # <th class=listheading>| . $locale->text('Sell Price') . qq|</th>
299 # <th class=listheading>| . $locale->text('soldtotal') . qq|</th>
303 # my $i = $form->{rows};
305 # for ($j = 1; $j <= $i; $j++) {
308 # <tr class=listrow| . ($j % 2) . qq|>|;
311 # <td><input name=ndx class=radio type=radio value=$j checked></td>|;
314 # <td><input name=ndx class=radio type=radio value=$j></td>|;
317 # <td><input name="new_partnumber_$j" type=hidden value="$form->{"partnumber_$j"}">$form->{"partnumber_$j"}</td>
318 # <td><input name="new_description_$j" type=hidden value="$form->{"description_$j"}">$form->{"description_$j"}</td>
319 # <td><input name="new_unit_$j" type=hidden value="$form->{"unit_$j"}">$form->{"unit_$j"}</td>
320 # <td><input name="new_sellprice_$j" type=hidden value="$form->{"sellprice_$j"}">$form->{"sellprice_$j"}</td>
321 # <td><input name="new_soldtotal_$j" type=hidden value="$form->{"soldtotal_$j"}">$form->{"soldtotal_$j"}</td>
324 # <input name="new_id_$j" type=hidden value="$form->{"id_$j"}">|;
334 #<input type=hidden name=itemstatus value="$form->{itemstatus}">
335 #<input type=hidden name=l_linetotal value="$form->{l_linetotal}">
336 #<input type=hidden name=l_partnumber value="$form->{l_partnumber}">
337 #<input type=hidden name=l_description value="$form->{l_description}">
338 #<input type=hidden name=l_onhand value="$form->{l_onhand}">
339 #<input type=hidden name=l_unit value="$form->{l_unit}">
340 #<input type=hidden name=l_sellprice value="$form->{l_sellprice}">
341 #<input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
342 #<input type=hidden name=sort value="$form->{sort}">
343 #<input type=hidden name=revers value="$form->{revers}">
344 #<input type=hidden name=lastsort value="$form->{lastsort}">
346 #<input type=hidden name=bom value="$form->{bom}">
347 #<input type=hidden name=titel value="$form->{titel}">
348 #<input type=hidden name=searchitems value="$form->{searchitems}">
350 #<input type=hidden name=row value=$j>
352 #<input type=hidden name=nextsub value=item_selected>
354 #<input name=lastndx type=hidden value=$lastndx>
356 #<input name=ndxs_counter type=hidden value=$form->{ndxs_counter}>|;
360 # if (($form->{ndxs_counter}) > 0) {
361 # for ($i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
363 # $partnumber = $form->{"totop100_partnumber_$i"};
364 # $description = $form->{"totop100_description_$i"};
365 # $unit = $form->{"totop100_unit_$i"};
366 # $sellprice = $form->{"totop100_sellprice_$i"};
367 # $soldtotal = $form->{"totop100_soldtotal_$i"};
370 #<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
371 #<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
372 #<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
373 #<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
374 #<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
382 #<input class=submit type=submit name=action value="|
383 # . $locale->text('TOP100') . qq|">
387 # $lxdebug->leave_sub();
391 $lxdebug->enter_sub();
393 $auth->assert('part_service_assembly_edit');
396 $form->{ndxs_counter}++;
398 if ($form->{ndxs_counter} > 0) {
400 my $index = $form->{ndx};
402 $form->{"totop100_partnumber_$form->{ndxs_counter}"} = $form->{"new_partnumber_$index"};
403 $form->{"totop100_description_$form->{ndxs_counter}"} = $form->{"new_description_$index"};
404 $form->{"totop100_unit_$form->{ndxs_counter}"} = $form->{"new_unit_$index"};
405 $form->{"totop100_sellprice_$form->{ndxs_counter}"} = $form->{"new_sellprice_$index"};
406 $form->{"totop100_soldtotal_$form->{ndxs_counter}"} = $form->{"new_soldtotal_$index"};
410 $lxdebug->leave_sub();
414 $lxdebug->enter_sub();
416 $auth->assert('part_service_assembly_edit');
418 my ($revers, $lastsort, $callback, $option, $description, $sameitem,
419 $partnumber, $unit, $sellprice, $soldtotal, $totop100, $onhand, $align);
420 my (@column_index, %column_header, %column_data);
421 my ($totalsellprice, $totallastcost, $totallistprice, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice);
423 $form->{top100} = "top100";
424 $form->{l_soldtotal} = "Y";
425 $form->{soldtotal} = "soldtotal";
426 $form->{sort} = "soldtotal";
427 $form->{l_qty} = "N";
428 $form->{l_linetotal} = "";
430 $form->{number} = "position";
431 $form->{l_number} = "Y";
435 $form->{title} = $locale->text('Top 100');
437 $revers = $form->{revers};
438 $lastsort = $form->{lastsort};
440 if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
442 $form->{lastsort} = "partnumber";
443 $form->{sort} = "partnumber";
447 "$form->{script}?action=top100&searchitems=$form->{searchitems}&itemstatus=$form->{itemstatus}&bom=$form->{bom}&l_linetotal=$form->{l_linetotal}&title="
448 . $form->escape($form->{title}, 1);
450 # if we have a serialnumber limit search
451 if ($form->{serialnumber} || $form->{l_serialnumber}) {
452 $form->{l_serialnumber} = "Y";
453 unless ( $form->{bought}
456 || $form->{quoted}) {
457 $form->{bought} = $form->{sold} = 1;
460 IC->all_parts(\%myconfig, \%$form);
462 if ($form->{itemstatus} eq 'active') {
463 $option .= $locale->text('Active') . " : ";
465 if ($form->{itemstatus} eq 'obsolete') {
466 $option .= $locale->text('Obsolete') . " : ";
468 if ($form->{itemstatus} eq 'orphaned') {
469 $option .= $locale->text('Orphaned') . " : ";
471 if ($form->{itemstatus} eq 'onhand') {
472 $option .= $locale->text('On Hand') . " : ";
473 $form->{l_onhand} = "Y";
475 if ($form->{itemstatus} eq 'short') {
476 $option .= $locale->text('Short') . " : ";
477 $form->{l_onhand} = "Y";
479 if ($form->{onorder}) {
480 $form->{l_ordnumber} = "Y";
481 $callback .= "&onorder=$form->{onorder}";
482 $option .= $locale->text('On Order') . " : ";
484 if ($form->{ordered}) {
485 $form->{l_ordnumber} = "Y";
486 $callback .= "&ordered=$form->{ordered}";
487 $option .= $locale->text('Ordered') . " : ";
490 $form->{l_quonumber} = "Y";
491 $callback .= "&rfq=$form->{rfq}";
492 $option .= $locale->text('RFQ') . " : ";
494 if ($form->{quoted}) {
495 $form->{l_quonumber} = "Y";
496 $callback .= ""ed=$form->{quoted}";
497 $option .= $locale->text('Quoted') . " : ";
499 if ($form->{bought}) {
500 $form->{l_invnumber} = "Y";
501 $callback .= "&bought=$form->{bought}";
502 $option .= $locale->text('Bought') . " : ";
505 $form->{l_invnumber} = "Y";
506 $callback .= "&sold=$form->{sold}";
507 $option .= $locale->text('Sold') . " : ";
514 || $form->{quoted}) {
516 $form->{l_lastcost} = "";
517 $form->{l_name} = "Y";
518 if ($form->{transdatefrom}) {
519 $callback .= "&transdatefrom=$form->{transdatefrom}";
521 . $locale->text('From')
523 . $locale->date(\%myconfig, $form->{transdatefrom}, 1);
525 if ($form->{transdateto}) {
526 $callback .= "&transdateto=$form->{transdateto}";
528 . $locale->text('To')
530 . $locale->date(\%myconfig, $form->{transdateto}, 1);
536 if ($form->{partnumber}) {
537 $callback .= "&partnumber=$form->{partnumber}";
538 $option .= $locale->text('Part Number') . qq| : $form->{partnumber}<br>|;
541 $callback .= "&partnumber=$form->{ean}";
542 $option .= $locale->text('EAN') . qq| : $form->{ean}<br>|;
544 if ($form->{partsgroup}) {
545 $callback .= "&partsgroup=$form->{partsgroup}";
546 $option .= $locale->text('Group') . qq| : $form->{partsgroup}<br>|;
548 if ($form->{serialnumber}) {
549 $callback .= "&serialnumber=$form->{serialnumber}";
550 $option .= $locale->text('Serial Number') . qq| : $form->{serialnumber}<br>|;
552 if ($form->{description}) {
553 $callback .= "&description=$form->{description}";
554 $description = $form->{description};
555 $description =~ s/\n/<br>/g;
556 $option .= $locale->text('Part Description') . qq| : $form->{description}<br>|;
559 $callback .= "&make=$form->{make}";
560 $option .= $locale->text('Make') . qq| : $form->{make}<br>|;
562 if ($form->{model}) {
563 $callback .= "&model=$form->{model}";
564 $option .= $locale->text('Model') . qq| : $form->{model}<br>|;
566 if ($form->{drawing}) {
567 $callback .= "&drawing=$form->{drawing}";
568 $option .= $locale->text('Drawing') . qq| : $form->{drawing}<br>|;
570 if ($form->{microfiche}) {
571 $callback .= "µfiche=$form->{microfiche}";
572 $option .= $locale->text('Microfiche') . qq| : $form->{microfiche}<br>|;
574 if ($form->{l_soldtotal}) {
575 $callback .= "&soldtotal=$form->{soldtotal}";
576 $option .= $locale->text('soldtotal') . qq| : $form->{soldtotal}<br>|;
579 my @columns = $form->sort_columns(
580 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)
583 if ($form->{l_linetotal}) {
584 $form->{l_onhand} = "Y";
585 $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
586 if ($form->{l_lastcost}) {
587 $form->{l_linetotallastcost} = "Y";
588 if (($form->{searchitems} eq 'assembly') && !$form->{bom}) {
589 $form->{l_linetotallastcost} = "";
592 $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
595 if ($form->{searchitems} eq 'service') {
597 # remove bin, weight and rop from list
598 map { $form->{"l_$_"} = "" } qw(bin weight rop);
600 $form->{l_onhand} = "";
602 # qty is irrelevant unless bought or sold
608 || $form->{quoted}) {
609 $form->{l_onhand} = "Y";
611 $form->{l_linetotalsellprice} = "";
612 $form->{l_linetotallastcost} = "";
616 foreach my $item (@columns) {
617 if ($form->{"l_$item"} eq "Y") {
618 push @column_index, $item;
620 # add column to callback
621 $callback .= "&l_$item=Y";
625 if ($form->{l_subtotal} eq 'Y') {
626 $callback .= "&l_subtotal=Y";
629 $column_header{number} =
630 qq|<th class=listheading nowrap>| . $locale->text('number') . qq|</th>|;
631 $column_header{partnumber} =
632 qq|<th nowrap><a class=listheading href=$callback&sort=partnumber&revers=$form->{revers}&lastsort=$form->{lastsort}>|
633 . $locale->text('Part Number')
635 $column_header{description} =
636 qq|<th nowrap><a class=listheading href=$callback&sort=description&revers=$form->{revers}&lastsort=$form->{lastsort}>|
637 . $locale->text('Part Description')
639 $column_header{partsgroup} =
640 qq|<th nowrap><a class=listheading href=$callback&sort=partsgroup>|
641 . $locale->text('Group')
643 $column_header{bin} =
644 qq|<th><a class=listheading href=$callback&sort=bin>|
645 . $locale->text('Bin')
647 $column_header{priceupdate} =
648 qq|<th nowrap><a class=listheading href=$callback&sort=priceupdate>|
649 . $locale->text('Updated')
651 $column_header{onhand} =
652 qq|<th nowrap><a class=listheading href=$callback&sort=onhand&revers=$form->{revers}&lastsort=$form->{lastsort}>|
653 . $locale->text('Qty')
655 $column_header{unit} =
656 qq|<th class=listheading nowrap>| . $locale->text('Unit') . qq|</th>|;
657 $column_header{listprice} =
658 qq|<th class=listheading nowrap>|
659 . $locale->text('List Price')
661 $column_header{lastcost} =
662 qq|<th class=listheading nowrap>| . $locale->text('Last Cost') . qq|</th>|;
663 $column_header{rop} =
664 qq|<th class=listheading nowrap>| . $locale->text('ROP') . qq|</th>|;
665 $column_header{weight} =
666 qq|<th class=listheading nowrap>| . $locale->text('Weight') . qq|</th>|;
668 $column_header{invnumber} =
669 qq|<th nowrap><a class=listheading href=$callback&sort=invnumber>|
670 . $locale->text('Invoice Number')
672 $column_header{ordnumber} =
673 qq|<th nowrap><a class=listheading href=$callback&sort=ordnumber>|
674 . $locale->text('Order Number')
676 $column_header{quonumber} =
677 qq|<th nowrap><a class=listheading href=$callback&sort=quonumber>|
678 . $locale->text('Quotation')
681 $column_header{name} =
682 qq|<th nowrap><a class=listheading href=$callback&sort=name>|
683 . $locale->text('Name')
686 $column_header{sellprice} =
687 qq|<th class=listheading nowrap>|
688 . $locale->text('Sell Price')
690 $column_header{linetotalsellprice} =
691 qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
692 $column_header{linetotallastcost} =
693 qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
694 $column_header{linetotallistprice} =
695 qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
697 $column_header{image} =
698 qq|<th class=listheading nowrap>| . $locale->text('Image') . qq|</a></th>|;
699 $column_header{drawing} =
700 qq|<th nowrap><a class=listheading href=$callback&sort=drawing>|
701 . $locale->text('Drawing')
703 $column_header{microfiche} =
704 qq|<th nowrap><a class=listheading href=$callback&sort=microfiche>|
705 . $locale->text('Microfiche')
708 $column_header{serialnumber} =
709 qq|<th nowrap><a class=listheading href=$callback&sort=serialnumber>|
710 . $locale->text('Serial Number')
712 $column_header{soldtotal} =
713 qq|<th nowrap><a class=listheading href=$callback&sort=soldtotal&revers=$form->{revers}&lastsort=$form->{lastsort}>|
714 . $locale->text('soldtotal')
718 my $colspan = $#column_index + 1;
721 <h1>$form->{title}</h1>
725 <tr><td colspan=$colspan>$option</td></tr>
727 <tr class=listheading>
730 map { print "\n$column_header{$_}" } @column_index;
736 # add order to callback
737 $form->{callback} = $callback .= "&sort=$form->{sort}";
739 # escape callback for href
740 $callback = $form->escape($callback);
742 if (@{ $form->{parts} }) {
743 $sameitem = $form->{parts}->[0]->{ $form->{sort} };
746 # insert numbers for top100
748 foreach my $ref (@{ $form->{parts} }) {
753 # if avaible -> insert choice here
754 if (($form->{ndxs_counter}) > 0) {
755 for (my $i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
756 $partnumber = $form->{"totop100_partnumber_$i"};
757 $description = $form->{"totop100_description_$i"};
758 $unit = $form->{"totop100_unit_$i"};
759 $sellprice = $form->{"totop100_sellprice_$i"};
760 $soldtotal = $form->{"totop100_soldtotal_$i"};
763 <input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
764 <input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
765 <input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
766 <input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
767 <input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
771 push @{ $form->{parts} },
773 partnumber => "$partnumber",
774 description => "$description",
776 sellprice => "$sellprice",
777 soldtotal => "$soldtotal" };
780 # build data for columns
782 foreach my $ref (@{ $form->{parts} }) {
784 if ($form->{l_subtotal} eq 'Y' && !$ref->{assemblyitem}) {
785 if ($sameitem ne $ref->{ $form->{sort} }) {
786 parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
787 $sameitem = $ref->{ $form->{sort} };
791 $ref->{exchangerate} = 1 unless $ref->{exchangerate};
792 $ref->{sellprice} *= $ref->{exchangerate};
793 $ref->{listprice} *= $ref->{exchangerate};
794 $ref->{lastcost} *= $ref->{exchangerate};
796 # use this for assemblies
797 $onhand = $ref->{onhand};
800 if ($ref->{assemblyitem}) {
802 $onhand = 0 if ($form->{sold});
805 $ref->{description} =~ s/\n/<br>/g;
807 $column_data{number} =
809 . $form->format_amount(\%myconfig, $ref->{number})
811 $column_data{partnumber} =
812 "<td align=$align>$ref->{partnumber} </a></td>";
813 $column_data{description} = "<td>$ref->{description} </td>";
814 $column_data{partsgroup} = "<td>$ref->{partsgroup} </td>";
816 $column_data{onhand} =
818 . $form->format_amount(\%myconfig, $ref->{onhand})
820 $column_data{sellprice} =
822 . $form->format_amount(\%myconfig, $ref->{sellprice})
824 $column_data{listprice} =
826 . $form->format_amount(\%myconfig, $ref->{listprice})
828 $column_data{lastcost} =
830 . $form->format_amount(\%myconfig, $ref->{lastcost})
833 $column_data{linetotalsellprice} = "<td align=right>"
834 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{sellprice}, 2)
836 $column_data{linetotallastcost} = "<td align=right>"
837 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{lastcost}, 2)
839 $column_data{linetotallistprice} = "<td align=right>"
840 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{listprice}, 2)
843 if (!$ref->{assemblyitem}) {
844 $totalsellprice += $onhand * $ref->{sellprice};
845 $totallastcost += $onhand * $ref->{lastcost};
846 $totallistprice += $onhand * $ref->{listprice};
848 $subtotalonhand += $onhand;
849 $subtotalsellprice += $onhand * $ref->{sellprice};
850 $subtotallastcost += $onhand * $ref->{lastcost};
851 $subtotallistprice += $onhand * $ref->{listprice};
856 . $form->format_amount(\%myconfig, $ref->{rop}) . "</td>";
857 $column_data{weight} =
859 . $form->format_amount(\%myconfig, $ref->{weight})
861 $column_data{unit} = "<td>$ref->{unit} </td>";
862 $column_data{bin} = "<td>$ref->{bin} </td>";
863 $column_data{priceupdate} = "<td>$ref->{priceupdate} </td>";
865 $column_data{invnumber} =
866 ($ref->{module} ne 'oe')
867 ? "<td><a href=$ref->{module}.pl?action=edit&type=invoice&id=$ref->{trans_id}&callback=$callback>$ref->{invnumber}</a></td>"
868 : "<td>$ref->{invnumber}</td>";
869 $column_data{ordnumber} =
870 ($ref->{module} eq 'oe')
871 ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{ordnumber}</a></td>"
872 : "<td>$ref->{ordnumber}</td>";
873 $column_data{quonumber} =
874 ($ref->{module} eq 'oe' && !$ref->{ordnumber})
875 ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{quonumber}</a></td>"
876 : "<td>$ref->{quonumber}</td>";
878 $column_data{name} = "<td>$ref->{name}</td>";
880 $column_data{image} =
882 ? "<td><a href=$ref->{image}><img src=$ref->{image} height=32 border=0></a></td>"
884 $column_data{drawing} =
886 ? "<td><a href=$ref->{drawing}>$ref->{drawing}</a></td>"
888 $column_data{microfiche} =
890 ? "<td><a href=$ref->{microfiche}>$ref->{microfiche}</a></td>"
893 $column_data{serialnumber} = "<td>$ref->{serialnumber}</td>";
895 $column_data{soldtotal} = "<td align=right>$ref->{soldtotal}</td>";
899 print "<tr class=listrow$i>";
901 map { print "\n$column_data{$_}" } @column_index;
908 if ($form->{l_subtotal} eq 'Y') {
909 parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
912 if ($form->{"l_linetotal"}) {
913 map { $column_data{$_} = "<td> </td>" } @column_index;
914 $column_data{linetotalsellprice} =
915 "<th class=listtotal align=right>"
916 . $form->format_amount(\%myconfig, $totalsellprice, 2)
918 $column_data{linetotallastcost} =
919 "<th class=listtotal align=right>"
920 . $form->format_amount(\%myconfig, $totallastcost, 2)
922 $column_data{linetotallistprice} =
923 "<th class=listtotal align=right>"
924 . $form->format_amount(\%myconfig, $totallistprice, 2)
927 print "<tr class=listtotal>";
929 map { print "\n$column_data{$_}" } @column_index;
936 <tr><td colspan=$colspan><hr size=3 noshade></td></tr>
945 <form method=post action=$form->{script}>
947 <input type=hidden name=itemstatus value="$form->{itemstatus}">
948 <input type=hidden name=l_linetotal value="$form->{l_linetotal}">
949 <input type=hidden name=l_partnumber value="$form->{l_partnumber}">
950 <input type=hidden name=l_description value="$form->{l_description}">
951 <input type=hidden name=l_onhand value="$form->{l_onhand}">
952 <input type=hidden name=l_unit value="$form->{l_unit}">
953 <input type=hidden name=l_sellprice value="$form->{l_sellprice}">
954 <input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
955 <input type=hidden name=sort value="$form->{sort}">
956 <input type=hidden name=revers value="$form->{revers}">
957 <input type=hidden name=lastsort value="$form->{lastsort}">
958 <input type=hidden name=parts value="$form->{parts}">
960 <input type=hidden name=bom value="$form->{bom}">
961 <input type=hidden name=titel value="$form->{titel}">
962 <input type=hidden name=searchitems value="$form->{searchitems}">|;
967 <!-- <input type=hidden name=ndxs_counter value="$form->{ndxs_counter}">-->
969 <!-- <input class=submit type=submit name=action value="|
970 . $locale->text('choice') . qq|"> -->
975 $lxdebug->leave_sub();
980 # Warning, deep magic ahead.
981 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
983 # flags coming from the form:
985 # searchitems=part revers=0 lastsort=''
988 # partnumber ean description partsgroup serialnumber make model drawing microfiche
989 # transdatefrom transdateto
992 # itemstatus = active | onhand | short | obsolete | orphaned
993 # action = continue | top100
996 # bought sold onorder ordered rfq quoted
997 # l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
998 # l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
999 # l_partsgroup l_subtotal l_soldtotal l_deliverydate l_pricegroups
1002 # nextsub revers lastsort sort ndxs_counter
1004 sub generate_report {
1005 $lxdebug->enter_sub();
1007 $auth->assert('part_service_assembly_details');
1009 my ($revers, $lastsort, $description);
1011 my $cvar_configs = CVar->get_configs('module' => 'IC');
1013 $form->{title} = (ucfirst $form->{searchitems}) . "s";
1014 $form->{title} =~ s/ys$/ies/;
1015 $form->{title} = $locale->text($form->{title});
1018 'bin' => { 'text' => $locale->text('Bin'), },
1019 'deliverydate' => { 'text' => $locale->text('deliverydate'), },
1020 'description' => { 'text' => $locale->text('Part Description'), },
1021 'notes' => { 'text' => $locale->text('Notes'), },
1022 'drawing' => { 'text' => $locale->text('Drawing'), },
1023 'ean' => { 'text' => $locale->text('EAN'), },
1024 'image' => { 'text' => $locale->text('Image'), },
1025 'insertdate' => { 'text' => $locale->text('Insert Date'), },
1026 'invnumber' => { 'text' => $locale->text('Invoice Number'), },
1027 'lastcost' => { 'text' => $locale->text('Last Cost'), },
1028 'linetotallastcost' => { 'text' => $locale->text('Extended'), },
1029 'linetotallistprice' => { 'text' => $locale->text('Extended'), },
1030 'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
1031 'listprice' => { 'text' => $locale->text('List Price'), },
1032 'microfiche' => { 'text' => $locale->text('Microfiche'), },
1033 'name' => { 'text' => $locale->text('Name'), },
1034 'onhand' => { 'text' => $locale->text('Stocked Qty'), },
1035 'ordnumber' => { 'text' => $locale->text('Order Number'), },
1036 'partnumber' => { 'text' => $locale->text('Part Number'), },
1037 'partsgroup' => { 'text' => $locale->text('Group'), },
1038 'priceupdate' => { 'text' => $locale->text('Updated'), },
1039 'quonumber' => { 'text' => $locale->text('Quotation'), },
1040 'rop' => { 'text' => $locale->text('ROP'), },
1041 'sellprice' => { 'text' => $locale->text('Sell Price'), },
1042 'serialnumber' => { 'text' => $locale->text('Serial Number'), },
1043 'soldtotal' => { 'text' => $locale->text('Qty in Selected Records'), },
1044 'name' => { 'text' => $locale->text('Name in Selected Records'), },
1045 'transdate' => { 'text' => $locale->text('Transdate'), },
1046 'unit' => { 'text' => $locale->text('Unit'), },
1047 'weight' => { 'text' => $locale->text('Weight'), },
1048 'shop' => { 'text' => $locale->text('Shopartikel'), },
1049 'projectnumber' => { 'text' => $locale->text('Project Number'), },
1050 'projectdescription' => { 'text' => $locale->text('Project Description'), },
1053 $revers = $form->{revers};
1054 $lastsort = $form->{lastsort};
1056 # sorting and direction of sorting
1057 # ToDO: change this to the simpler field+direction method
1058 if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
1059 $form->{revers} = 0;
1060 $form->{lastsort} = "partnumber";
1061 $form->{sort} = "partnumber";
1063 if ($form->{lastsort} eq $form->{sort}) {
1064 $form->{revers} = 1 - $form->{revers};
1066 $form->{revers} = 0;
1067 $form->{lastsort} = $form->{sort};
1071 # special case if we have a serialnumber limit search
1072 # serialnumbers are only given in invoices and orders,
1073 # so they can only pop up in bought, sold, rfq, and quoted stuff
1074 $form->{no_sn_joins} = 'Y' if ( !$form->{bought} && !$form->{sold}
1075 && !$form->{rfq} && !$form->{quoted}
1076 && ($form->{l_serialnumber} || $form->{serialnumber}));
1078 # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
1079 # if any of these are ticked the behavior changes slightly for lastcost
1080 # since all those are aggregation checks for the legder tables this is an internal switch
1081 # refered to as ledgerchecks
1082 $form->{ledgerchecks} = 'Y' if ( $form->{bought} || $form->{sold} || $form->{onorder}
1083 || $form->{ordered} || $form->{rfq} || $form->{quoted});
1085 # if something should be activated if something else is active, enter it here
1086 my %dependencies = (
1087 onhand => [ qw(l_onhand) ],
1088 short => [ qw(l_onhand) ],
1089 onorder => [ qw(l_ordnumber) ],
1090 ordered => [ qw(l_ordnumber) ],
1091 rfq => [ qw(l_quonumber) ],
1092 quoted => [ qw(l_quonumber) ],
1093 bought => [ qw(l_invnumber) ],
1094 sold => [ qw(l_invnumber) ],
1095 ledgerchecks => [ qw(l_name) ],
1096 serialnumber => [ qw(l_serialnumber) ],
1097 no_sn_joins => [ qw(bought sold) ],
1100 # get name of partsgroup if id is given
1102 if ($form->{partsgroup_id}) {
1103 my $pg = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load;
1104 $pg_name = $pg->{'partsgroup'};
1107 # these strings get displayed at the top of the results to indicate the user which switches were used
1109 active => $locale->text('Active'),
1110 obsolete => $locale->text('Obsolete'),
1111 orphaned => $locale->text('Orphaned'),
1112 onhand => $locale->text('On Hand'),
1113 short => $locale->text('Short'),
1114 onorder => $locale->text('On Order'),
1115 ordered => $locale->text('Ordered'),
1116 rfq => $locale->text('RFQ'),
1117 quoted => $locale->text('Quoted'),
1118 bought => $locale->text('Bought'),
1119 sold => $locale->text('Sold'),
1120 transdatefrom => $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
1121 transdateto => $locale->text('To (time)') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
1122 partnumber => $locale->text('Part Number') . ": '$form->{partnumber}'",
1123 partsgroup => $locale->text('Group') . ": '$form->{partsgroup}'",
1124 partsgroup_id => $locale->text('Group') . ": '$pg_name'",
1125 serialnumber => $locale->text('Serial Number') . ": '$form->{serialnumber}'",
1126 description => $locale->text('Part Description') . ": '$form->{description}'",
1127 make => $locale->text('Make') . ": '$form->{make}'",
1128 model => $locale->text('Model') . ": '$form->{model}'",
1129 drawing => $locale->text('Drawing') . ": '$form->{drawing}'",
1130 microfiche => $locale->text('Microfiche') . ": '$form->{microfiche}'",
1131 l_soldtotal => $locale->text('Qty in Selected Records'),
1132 ean => $locale->text('EAN') . ": '$form->{ean}'",
1133 insertdatefrom => $locale->text('Insert Date') . ": " . $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1),
1134 insertdateto => $locale->text('Insert Date') . ": " . $locale->text('To (time)') . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1),
1137 my @itemstatus_keys = qw(active obsolete orphaned onhand short);
1138 my @callback_keys = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
1139 drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto insertdatefrom insertdateto ean shop);
1141 # calculate dependencies
1142 for (@itemstatus_keys, @callback_keys) {
1143 next if ($form->{itemstatus} ne $_ && !$form->{$_});
1144 map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
1147 # generate callback and optionstrings
1149 for my $key (@itemstatus_keys, @callback_keys) {
1150 next if ($form->{itemstatus} ne $key && !$form->{$key});
1151 push @options, $optiontexts{$key};
1154 # special case for lastcost
1155 if ($form->{ledgerchecks}){
1156 # ledgerchecks don't know about sellprice or lastcost. they just return a
1157 # price. so rename sellprice to price, and drop lastcost.
1158 $column_defs{sellprice}{text} = $locale->text('Price');
1159 $form->{l_lastcost} = ""
1162 if ($form->{description}) {
1163 $description = $form->{description};
1164 $description =~ s/\n/<br>/g;
1167 if ($form->{l_linetotal}) {
1168 $form->{l_qty} = "Y";
1169 $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
1170 $form->{l_linetotallastcost} = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if $form->{l_lastcost};
1171 $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
1174 if ($form->{searchitems} eq 'service') {
1176 # remove bin, weight and rop from list
1177 map { $form->{"l_$_"} = "" } qw(bin weight rop);
1179 $form->{l_onhand} = "";
1181 # qty is irrelevant unless bought or sold
1182 if ( $form->{bought}
1187 || $form->{quoted}) {
1188 # $form->{l_onhand} = "Y";
1190 $form->{l_linetotalsellprice} = "";
1191 $form->{l_linetotallastcost} = "";
1195 # soldtotal doesn't make sense with more than one bsooqr option.
1196 # so reset it to sold (the most common option), and issue a warning
1198 # also it doesn't make sense without bsooqr. disable and issue a warning too
1199 my @bsooqr = qw(sold bought onorder ordered rfq quoted);
1200 my $bsooqr_mode = grep { $form->{$_} } @bsooqr;
1201 if ($form->{l_subtotal} && 1 < $bsooqr_mode) {
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});
1208 if ($form->{l_soldtotal} && !$bsooqr_mode) {
1209 delete $form->{l_soldtotal};
1211 flash('warning', $::locale->text('Soldtotal does not make sense without any bsooqr options'));
1213 if ($form->{l_name} && !$bsooqr_mode) {
1214 delete $form->{l_name};
1216 flash('warning', $::locale->text('Name does not make sense without any bsooqr options'));
1218 IC->all_parts(\%myconfig, \%$form);
1221 partnumber description notes partsgroup bin onhand rop soldtotal unit listprice
1222 linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
1223 priceupdate weight image drawing microfiche invnumber ordnumber quonumber
1224 transdate name serialnumber deliverydate ean projectnumber projectdescription
1228 my $pricegroups = SL::DB::Manager::Pricegroup->get_all(sort => 'id');
1229 my @pricegroup_columns;
1230 my %column_defs_pricegroups;
1231 if ($form->{l_pricegroups}) {
1232 @pricegroup_columns = map { "pricegroup_" . $_->id } @{ $pricegroups };
1233 %column_defs_pricegroups = map {
1234 "pricegroup_" . $_->id => {
1235 text => $::locale->text('Pricegroup') . ' ' . $_->pricegroup,
1238 } @{ $pricegroups };
1240 push @columns, @pricegroup_columns;
1242 my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
1243 my @searchable_custom_variables = grep { $_->{searchable} } @{ $cvar_configs };
1244 my %column_defs_cvars = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
1246 push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
1248 %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
1249 map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
1250 map { $column_defs{$_}->{align} = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal shop), @pricegroup_columns;
1252 my @hidden_variables = (
1253 qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups insertdatefrom insertdateto),
1256 map({ "cvar_$_->{name}" } @searchable_custom_variables),
1257 map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
1258 map({ "l_$_" } @columns),
1261 my $callback = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
1263 my @sort_full = qw(partnumber description onhand soldtotal deliverydate insertdate shop);
1264 my @sort_no_revers = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
1266 foreach my $col (@sort_full) {
1267 $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
1269 map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
1271 # add order to callback
1272 $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
1274 my $report = SL::ReportGenerator->new(\%myconfig, $form);
1276 my %attachment_basenames = (
1277 'part' => $locale->text('part_list'),
1278 'service' => $locale->text('service_list'),
1279 'assembly' => $locale->text('assembly_list'),
1282 $report->set_options('raw_top_info_text' => $form->parse_html_template('ic/generate_report_top', { options => \@options }),
1283 'raw_bottom_info_text' => $form->parse_html_template('ic/generate_report_bottom'),
1284 'output_format' => 'HTML',
1285 'title' => $form->{title},
1286 'attachment_basename' => $attachment_basenames{$form->{searchitems}} . strftime('_%Y%m%d', localtime time),
1288 $report->set_options_from_form();
1289 $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
1291 $report->set_columns(%column_defs);
1292 $report->set_column_order(@columns);
1294 $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
1296 $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
1298 CVar->add_custom_variables_to_report('module' => 'IC',
1299 'trans_id_field' => 'id',
1300 'configs' => $cvar_configs,
1301 'column_defs' => \%column_defs,
1302 'data' => $form->{parts});
1304 CVar->add_custom_variables_to_report('module' => 'IC',
1305 'sub_module' => sub { $_[0]->{ioi} },
1306 'trans_id_field' => 'ioi_id',
1307 'configs' => $cvar_configs,
1308 'column_defs' => \%column_defs,
1309 'data' => $form->{parts});
1311 my @subtotal_columns = qw(sellprice listprice lastcost);
1312 my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
1313 my %totals = map { $_ => 0 } @subtotal_columns;
1315 my $same_item = @{ $form->{parts} } ? $form->{parts}[0]{ $form->{sort} } : undef;
1317 my $defaults = AM->get_defaults();
1320 foreach my $ref (@{ $form->{parts} }) {
1322 # fresh row, for inserting later
1323 my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
1325 $ref->{exchangerate} ||= 1;
1326 $ref->{price_factor} ||= 1;
1327 $ref->{sellprice} *= $ref->{exchangerate} / $ref->{price_factor};
1328 $ref->{listprice} *= $ref->{exchangerate} / $ref->{price_factor};
1329 $ref->{lastcost} *= $ref->{exchangerate} / $ref->{price_factor};
1331 # use this for assemblies
1332 my $soldtotal = $bsooqr_mode ? $ref->{soldtotal} : $ref->{onhand};
1334 if ($ref->{assemblyitem}) {
1335 $row->{partnumber}{align} = 'right';
1336 $row->{soldtotal}{data} = 0;
1337 $soldtotal = 0 if ($form->{sold});
1340 my $edit_link = build_std_url('action=edit', 'id=' . E($ref->{id}), 'callback');
1341 $row->{partnumber}->{link} = $edit_link;
1342 $row->{description}->{link} = $edit_link;
1344 foreach (qw(sellprice listprice lastcost)) {
1345 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}, 2);
1346 $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
1348 foreach ( @pricegroup_columns ) {
1349 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{"$_"}, 2);
1353 map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
1355 $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
1357 # 'yes' and 'no' for boolean value shop
1358 if ($form->{l_shop}) {
1359 $row->{shop}{data} = $row->{shop}{data}? $::locale->text('yes') : $::locale->text('no');
1362 if (!$ref->{assemblyitem}) {
1363 foreach my $col (@subtotal_columns) {
1364 $totals{$col} += $soldtotal * $ref->{$col};
1365 $subtotals{$col} += $soldtotal * $ref->{$col};
1368 $subtotals{soldtotal} += $soldtotal;
1372 if ($ref->{module} eq 'oe') {
1373 # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
1375 # | ist bestellt | Von Kunden bestellt | -> edit_oe_ord_link
1376 # | Anfrage | Angebot | -> edit_oe_quo_link
1378 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');
1379 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');
1381 $row->{ordnumber}{link} = $edit_oe_ord_link;
1382 $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
1385 $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback');
1388 # set properties of images
1389 if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
1390 $row->{image}{data} = '';
1391 $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
1393 map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
1395 $row->{notes}{data} = SL::HTML::Util->strip($ref->{notes});
1397 $report->add_data($row);
1399 my $next_ref = $form->{parts}[$idx + 1];
1401 # insert subtotal rows
1402 if (($form->{l_subtotal} eq 'Y') &&
1404 (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
1405 my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
1407 if (($form->{searchitems} ne 'assembly') || !$form->{bom}) {
1408 $row->{soldtotal}->{data} = $form->format_amount(\%myconfig, $subtotals{soldtotal});
1411 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
1412 map { $subtotals{$_} = 0 } ('soldtotal', @subtotal_columns);
1414 $report->add_data($row);
1416 $same_item = $next_ref->{ $form->{sort} };
1422 if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
1423 my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
1425 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
1427 $report->add_separator();
1428 $report->add_data($row);
1431 $report->generate_with_headers();
1433 $lxdebug->leave_sub();
1434 } #end generate_report
1436 sub parts_subtotal {
1437 $lxdebug->enter_sub();
1439 $auth->assert('part_service_assembly_edit');
1442 my ($column_index, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice) = @_;
1444 map { $column_data{$_} = "<td> </td>" } @{ $column_index };
1445 $$subtotalonhand = 0 if ($form->{searchitems} eq 'assembly' && $form->{bom});
1447 $column_data{onhand} =
1448 "<th class=listsubtotal align=right>"
1449 . $form->format_amount(\%myconfig, $$subtotalonhand)
1452 $column_data{linetotalsellprice} =
1453 "<th class=listsubtotal align=right>"
1454 . $form->format_amount(\%myconfig, $$subtotalsellprice, 2)
1456 $column_data{linetotallistprice} =
1457 "<th class=listsubtotal align=right>"
1458 . $form->format_amount(\%myconfig, $$subtotallistprice, 2)
1460 $column_data{linetotallastcost} =
1461 "<th class=listsubtotal align=right>"
1462 . $form->format_amount(\%myconfig, $$subtotallastcost, 2)
1465 $$subtotalonhand = 0;
1466 $$subtotalsellprice = 0;
1467 $$subtotallistprice = 0;
1468 $$subtotallastcost = 0;
1470 print "<tr class=listsubtotal>";
1472 map { print "\n$column_data{$_}" } @{ $column_index };
1478 $lxdebug->leave_sub();
1482 $lxdebug->enter_sub();
1484 $auth->assert('part_service_assembly_details');
1486 # show history button
1487 $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
1488 #/show hhistory button
1489 IC->get_part(\%myconfig, \%$form);
1491 $form->{"original_partnumber"} = $form->{"partnumber"};
1493 my $title = 'Edit ' . ucfirst $form->{item};
1494 $form->{title} = $locale->text($title);
1499 $lxdebug->leave_sub();
1503 $lxdebug->enter_sub();
1505 $auth->assert('part_service_assembly_details');
1507 IC->create_links("IC", \%myconfig, \%$form);
1510 map({ $form->{selectcurrency} .= "<option>$_\n" } $::form->get_all_currencies());
1512 # parts and assemblies have the same links
1513 my $item = $form->{item};
1514 if ($form->{item} eq 'assembly') {
1518 # build the popup menus
1519 $form->{taxaccounts} = "";
1520 foreach my $key (keys %{ $form->{IC_links} }) {
1521 foreach my $ref (@{ $form->{IC_links}{$key} }) {
1523 # if this is a tax field
1524 if ($key =~ /IC_tax/) {
1525 if ($key =~ /\Q$item\E/) {
1526 $form->{taxaccounts} .= "$ref->{accno} ";
1527 $form->{"IC_tax_$ref->{accno}_description"} =
1528 "$ref->{accno}--$ref->{description}";
1531 if ($form->{amount}{ $ref->{accno} }) {
1532 $form->{"IC_tax_$ref->{accno}"} = "checked";
1535 $form->{"IC_tax_$ref->{accno}"} = "checked";
1540 $form->{"select$key"} .=
1541 "<option $ref->{selected}>$ref->{accno}--$ref->{description}\n";
1542 if ($form->{amount}{$key} eq $ref->{accno}) {
1543 $form->{$key} = "$ref->{accno}--$ref->{description}";
1549 chop $form->{taxaccounts};
1551 if (($form->{item} eq "part") || ($form->{item} eq "assembly")) {
1552 $form->{selectIC_income} = $form->{selectIC_sale};
1553 $form->{selectIC_expense} = $form->{selectIC_cogs};
1554 $form->{IC_income} = $form->{IC_sale};
1555 $form->{IC_expense} = $form->{IC_cogs};
1558 delete $form->{IC_links};
1559 delete $form->{amount};
1561 $form->get_partsgroup(\%myconfig, { all => 1 });
1563 $form->{partsgroup} = "$form->{partsgroup}--$form->{partsgroup_id}";
1565 if (@{ $form->{all_partsgroup} }) {
1566 $form->{selectpartsgroup} = qq|<option>\n|;
1567 map { $form->{selectpartsgroup} .= qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n| } @{ $form->{all_partsgroup} };
1570 if ($form->{item} eq 'assembly') {
1572 foreach my $i (1 .. $form->{assembly_rows}) {
1573 if ($form->{"partsgroup_id_$i"}) {
1574 $form->{"partsgroup_$i"} =
1575 qq|$form->{"partsgroup_$i"}--$form->{"partsgroup_id_$i"}|;
1578 $form->get_partsgroup(\%myconfig);
1580 if (@{ $form->{all_partsgroup} }) {
1581 $form->{selectassemblypartsgroup} = qq|<option>\n|;
1584 $form->{selectassemblypartsgroup} .=
1585 qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n|
1586 } @{ $form->{all_partsgroup} };
1589 $lxdebug->leave_sub();
1593 $lxdebug->enter_sub();
1595 $auth->assert('part_service_assembly_details');
1597 $form->{pg_keys} = sub { "$_[0]->{partsgroup}--$_[0]->{id}" };
1598 $form->{description_area} = ($form->{rows} = $form->numtextrows($form->{description}, 40)) > 1;
1599 $form->{notes_rows} = max 4, $form->numtextrows($form->{notes}, 40), $form->numtextrows($form->{formel}, 40);
1601 map { $form->{"is_$_"} = ($form->{item} eq $_) } qw(part service assembly);
1602 map { $form->{$_} =~ s/"/"/g; } qw(unit);
1604 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
1605 'partsgroup' => 'all_partsgroup',
1606 'vendors' => 'ALL_VENDORS',
1607 'warehouses' => { 'key' => 'WAREHOUSES',
1608 'bins' => 'BINS', });
1609 # leerer wert für Lager und Lagerplatz korrekt einstellt
1610 # ID 0 sollte in Ordnung sein, da der Zähler sowieso höher ist
1611 my $no_default_bin_entry = { 'id' => '0', description => '--', 'BINS' => [ { id => '0', description => ''} ] };
1612 push @ { $form->{WAREHOUSES} }, $no_default_bin_entry;
1613 if (my $max = scalar @{ $form->{WAREHOUSES} }) {
1614 my ($default_warehouse_id, $default_bin_id);
1615 if ($form->{action} eq 'add') { # default only for new entries
1616 $default_warehouse_id = $::instance_conf->get_warehouse_id;
1617 $default_bin_id = $::instance_conf->get_bin_id;
1619 $form->{warehouse_id} ||= $default_warehouse_id || $form->{WAREHOUSES}->[$max -1]->{id};
1620 $form->{bin_id} ||= $default_bin_id || $form->{WAREHOUSES}->[$max -1]->{BINS}->[0]->{id};
1623 $form->{LANGUAGES} = SL::DB::Manager::Language->get_all_sorted;
1624 $form->{translations_map} = { map { ($_->{language_id} => $_) } @{ $form->{translations} || [] } };
1626 IC->retrieve_buchungsgruppen(\%myconfig, $form);
1627 @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
1629 if (($form->{partnumber} ne '') && !SL::TransNumber->new(number => $form->{partnumber}, type => $form->{item}, id => $form->{id})->is_unique) {
1630 flash('info', $::locale->text('This partnumber is not unique. You should change it.'));
1633 my $units = AM->retrieve_units(\%myconfig, $form);
1634 $form->{ALL_UNITS} = [ map +{ name => $_ }, sort { $units->{$a}{sortkey} <=> $units->{$b}{sortkey} } keys %$units ];
1636 $form->{defaults} = AM->get_defaults();
1638 $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'IC', 'trans_id' => $form->{id});
1640 my ($null, $partsgroup_id) = split /--/, $form->{partsgroup};
1642 CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}, show_disabled_message => 1, partsgroup_id => $partsgroup_id)
1643 if (scalar @{ $form->{CUSTOM_VARIABLES} });
1645 $::request->layout->use_javascript("${_}.js") for qw(ckeditor/ckeditor ckeditor/adapters/jquery kivi.PriceRule);
1646 $::request->layout->add_javascripts_inline("\$(function(){kivi.PriceRule.load_price_rules_for_part(@{[ $::form->{id} * 1 ]})});") if $::form->{id};
1648 #print $form->parse_html_template('ic/form_header', { ALL_PRICE_FACTORS => $form->{ALL_PRICE_FACTORS},
1649 # ALL_UNITS => $form->{ALL_UNITS},
1650 # BUCHUNGSGRUPPEN => $form->{BUCHUNGSGRUPPEN},
1651 # payment_terms => $form->{payment_terms},
1652 # all_partsgroup => $form->{all_partsgroup}});
1654 $form->{show_edit_buttons} = $main::auth->check_right($::myconfig{login}, 'part_service_assembly_edit');
1656 print $form->parse_html_template('ic/form_header');
1657 $lxdebug->leave_sub();
1661 $lxdebug->enter_sub();
1663 $auth->assert('part_service_assembly_details');
1665 print $form->parse_html_template('ic/form_footer');
1667 $lxdebug->leave_sub();
1671 $lxdebug->enter_sub();
1674 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;
1675 delete @{$form}{grep { m/^make_\d+/ || m/^model_\d+/ } keys %{ $form }};
1676 print $form->parse_html_template('ic/makemodel', { MM_DATA => [ @mm_data, {} ], mm_rows => scalar @mm_data + 1 });
1678 $lxdebug->leave_sub();
1682 $lxdebug->enter_sub();
1685 my ($nochange, $callback, $previousform, $linetotal, $line_purchase_price, $href);
1687 @column_index = qw(runningnumber qty unit bom partnumber description partsgroup lastcost total);
1689 if ($form->{previousform}) {
1691 @column_index = qw(qty unit bom partnumber description partsgroup total);
1695 $form->{old_callback} = $form->{callback};
1696 $callback = $form->{callback};
1697 $form->{callback} = "$form->{script}?action=display_form";
1700 map { delete $form->{$_} } qw(action header);
1702 # save form variables in a previousform variable
1703 my %form_to_save = map { ($_ => m/^ (?: listprice | sellprice | lastcost ) $/x ? $form->format_amount(\%myconfig, $form->{$_}) : $form->{$_}) }
1705 $previousform = $::auth->save_form_in_session(form => \%form_to_save);
1707 $form->{callback} = $callback;
1708 $form->{assemblytotal} = 0;
1709 $form->{assembly_purchase_price_total} = 0;
1710 $form->{weight} = 0;
1714 runningnumber => { text => $locale->text('No.'), nowrap => 1, width => '5%', align => 'left',},
1715 qty => { text => $locale->text('Qty'), nowrap => 1, width => '10%', align => 'left',},
1716 unit => { text => $locale->text('Unit'), nowrap => 1, width => '5%', align => 'left',},
1717 partnumber => { text => $locale->text('Part Number'), nowrap => 1, width => '20%', align => 'left',},
1718 description => { text => $locale->text('Part Description'), nowrap => 1, width => '50%', align => 'left',},
1719 lastcost => { text => $locale->text('Purchase Prices'), nowrap => 1, width => '50%', align => 'right',},
1720 total => { text => $locale->text('Sale Prices'), nowrap => 1, align => 'right',},
1721 bom => { text => $locale->text('BOM'), align => 'center',},
1722 partsgroup => { text => $locale->text('Group'), align => 'left',},
1727 for my $i (1 .. $numrows) {
1728 my (%row, @row_hiddens);
1730 $form->{"partnumber_$i"} =~ s/\"/"/g;
1732 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
1733 $line_purchase_price = $form->round_amount($form->{"lastcost_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
1734 $form->{assemblytotal} += $linetotal;
1735 $form->{assembly_purchase_price_total} += $line_purchase_price;
1736 $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
1737 $linetotal = $form->format_amount(\%myconfig, $linetotal, 2);
1738 $line_purchase_price = $form->format_amount(\%myconfig, $line_purchase_price, 2);
1739 $href = build_std_url("action=edit", qq|id=$form->{"id_$i"}|, "rowcount=$numrows", "currow=$i", "previousform=$previousform");
1740 map { $row{$_}{data} = "" } qw(qty unit partnumber description bom partsgroup runningnumber);
1743 if (($i >= 1) && ($i == $numrows)) {
1744 if (!$form->{previousform}) {
1745 $row{partnumber}{data} = qq|<input name="partnumber_$i" size=15 value="$form->{"partnumber_$i"}">|;
1746 $row{qty}{data} = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1747 $row{description}{data} = qq|<input name="description_$i" size=40 value="$form->{"description_$i"}">|;
1748 $row{partsgroup}{data} = qq|<input name="partsgroup_$i" size=10 value="$form->{"partsgroup_$i"}">|;
1752 if ($form->{previousform}) {
1753 push @row_hiddens, qw(qty bom);
1754 $row{partnumber}{data} = $form->{"partnumber_$i"};
1755 $row{qty}{data} = $form->{"qty_$i"};
1756 $row{bom}{data} = $form->{"bom_$i"} ? "x" : " ";
1757 $row{qty}{align} = 'right';
1759 $row{partnumber}{data} = qq|$form->{"partnumber_$i"}|;
1760 $row{partnumber}{link} = $href;
1761 $row{qty}{data} = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1762 $row{runningnumber}{data} = qq|<input name="runningnumber_$i" size=3 value="$i">|;
1763 $row{bom}{data} = sprintf qq|<input name="bom_$i" type=checkbox class=checkbox value=1 %s>|,
1764 $form->{"bom_$i"} ? 'checked' : '';
1766 push @row_hiddens, qw(unit description partnumber partsgroup);
1767 $row{unit}{data} = $form->{"unit_$i"};
1768 #Bei der Artikelbeschreibung und Warengruppe können Sonderzeichen verwendet
1769 #werden, die den HTML Code stören. Daher sollen diese im Template escaped werden
1770 #dies geschieht, wenn die Variable escape gesetzt ist
1771 $row{description}{data} = $form->{"description_$i"};
1772 $row{description}{escape} = 1;
1773 $row{partsgroup}{data} = $form->{"partsgroup_$i"};
1774 $row{partsgroup}{escape} = 1;
1775 $row{bom}{align} = 'center';
1778 $row{lastcost}{data} = $line_purchase_price;
1779 $row{total}{data} = $linetotal;
1780 $row{lastcost}{align} = 'right';
1781 $row{total}{align} = 'right';
1782 $row{deliverydate}{align} = 'right';
1784 push @row_hiddens, qw(id sellprice lastcost weight price_factor_id price_factor);
1785 $row{hiddens} = [ map +{ name => "${_}_$i", value => $form->{"${_}_$i"} }, @row_hiddens ];
1790 print $form->parse_html_template('ic/assembly_row', { COLUMNS => \@column_index, ROWS => \@ROWS, HEADER => \%header });
1792 $lxdebug->leave_sub();
1796 $lxdebug->enter_sub();
1798 $auth->assert('part_service_assembly_edit');
1800 # update checks whether pricegroups, makemodels or assembly items have been changed/added
1801 # new items might have been added (and the original form might have been stored and restored)
1802 # so at the end the ic form is run through check_form in io.pl
1803 # The various combination of events can lead to problems with the order of parse_amount and format_amount
1804 # Currently check_form parses some variables in assembly mode, but not in article or service mode
1805 # This will only ever really be sanely resolved with a rewrite...
1807 # parse pricegroups. and no, don't rely on check_form for this...
1808 map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
1810 unless ($form->{item} eq 'assembly') {
1811 # for assemblies check_form will parse sellprice and listprice, but not for parts or services
1812 $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) for qw(sellprice listprice ve gv);
1815 if ($form->{item} eq 'part') {
1816 $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) for qw(weight rop);
1819 # same for makemodel lastcosts
1820 # but parse_amount not necessary for assembly component lastcosts
1821 unless ($form->{item} eq "assembly") {
1822 map { $form->{"lastcost_$_"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$_"}) } 1 .. $form->{"makemodel_rows"};
1823 $form->{lastcost} = $form->parse_amount(\%myconfig, $form->{lastcost});
1826 if ($form->{item} eq "assembly") {
1827 my $i = $form->{assembly_rows};
1829 # if last row is empty check the form otherwise retrieve item
1830 if ( ($form->{"partnumber_$i"} eq "")
1831 && ($form->{"description_$i"} eq "")
1832 && ($form->{"partsgroup_$i"} eq "")) {
1833 # no new assembly item was added
1838 # search db for newly added assemblyitems, via partnumber or description
1839 IC->assembly_item(\%myconfig, \%$form);
1841 # form->{item_list} contains the possible matches, next check whether the
1842 # match is unique or we need to call the page to select the item
1843 my $rows = scalar @{ $form->{item_list} };
1846 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1849 $form->{makemodel_rows}--;
1850 select_item(mode => 'IC', pre_entered_qty => $form->parse_amount(\%myconfig, $form->{"qty_$i"}));
1853 map { $form->{item_list}[$i]{$_} =~ s/\"/"/g }
1854 qw(partnumber description unit partsgroup);
1855 map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} }
1856 keys %{ $form->{item_list}[0] };
1857 $form->{"runningnumber_$i"} = $form->{assembly_rows};
1858 $form->{assembly_rows}++;
1866 $form->{rowcount} = $i;
1867 $form->{assembly_rows}++;
1874 } elsif (($form->{item} eq 'part') || ($form->{item} eq 'service')) {
1878 $lxdebug->leave_sub();
1882 $lxdebug->enter_sub();
1884 $auth->assert('part_service_assembly_edit');
1885 $::form->mtime_ischanged('parts');
1886 my ($parts_id, %newform, $amount, $callback);
1888 # check if there is a part number - commented out, cause there is an automatic allocation of numbers
1889 # $form->isblank("partnumber", $locale->text(ucfirst $form->{item}." Part Number missing!"));
1891 # check if there is a description
1892 $form->isblank("description", $locale->text("Part Description missing!"));
1894 $form->error($locale->text("Inventory quantity must be zero before you can set this $form->{item} obsolete!"))
1895 if $form->{obsolete} && $form->{onhand} * 1 && $form->{item} ne 'service';
1897 if (!$form->{buchungsgruppen_id}) {
1898 $form->error($locale->text("Parts must have an entry type.") . " " .
1899 $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.")
1903 $form->error($locale->text('Description must not be empty!')) unless $form->{description};
1904 $form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
1906 # undef warehouse_id if the empty value is selected
1907 if ( ($form->{warehouse_id} == 0) && ($form->{bin_id} == 0) ) {
1908 undef $form->{warehouse_id};
1909 undef $form->{bin_id};
1912 if (IC->save(\%myconfig, \%$form) == 3) {
1913 $form->error($locale->text('Partnumber not unique!'));
1915 # saving the history
1916 if(!exists $form->{addition}) {
1917 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1918 $form->{what_done} = "part";
1919 $form->{addition} = "SAVED";
1920 $form->save_history;
1922 # /saving the history
1923 $parts_id = $form->{id};
1926 # load previous variables
1927 if ($form->{previousform}) {
1929 # save the new form variables before splitting previousform
1930 map { $newform{$_} = $form->{$_} } keys %$form;
1932 # don't trample on previous variables
1933 map { delete $form->{$_} } keys %newform;
1935 my $ic_cvar_configs = CVar->get_configs(module => 'IC');
1936 my @ic_cvar_fields = map { "cvar_$_->{name}" } @{ $ic_cvar_configs };
1938 # restore original values
1939 $::auth->restore_form_from_session($newform{previousform}, form => $form);
1940 $form->{taxaccounts} = $newform{taxaccount2};
1942 if ($form->{item} eq 'assembly') {
1944 # undo number formatting
1945 map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
1946 qw(weight listprice sellprice rop);
1948 $form->{assembly_rows}--;
1949 if ($newform{currow}) {
1950 $i = $newform{currow};
1952 $i = $form->{assembly_rows};
1954 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"} > 0);
1956 $form->{sellprice} -= $form->{"sellprice_$i"} * $form->{"qty_$i"};
1957 $form->{weight} -= $form->{"weight_$i"} * $form->{"qty_$i"};
1959 # change/add values for assembly item
1960 map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit weight listprice sellprice inventory_accno income_accno expense_accno price_factor_id);
1961 map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1963 # das ist __voll__ bekloppt, dass so auszurechnen jb 22.5.09
1964 #$form->{sellprice} += $form->{"sellprice_$i"} * $form->{"qty_$i"};
1965 $form->{weight} += $form->{"weight_$i"} * $form->{"qty_$i"};
1969 # set values for last invoice/order item
1970 $i = $form->{rowcount};
1971 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"} > 0);
1973 map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit listprice inventory_accno income_accno expense_accno sellprice lastcost price_factor_id);
1974 map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1976 $form->{"longdescription_$i"} = $newform{notes};
1978 $form->{"sellprice_$i"} = $newform{lastcost} if ($form->{vendor_id});
1980 if ($form->{exchangerate} != 0) {
1981 $form->{"sellprice_$i"} /= $form->{exchangerate};
1984 map { $form->{"taxaccounts_$i"} .= "$_ " } split / /, $newform{taxaccount};
1985 chop $form->{"taxaccounts_$i"};
1986 foreach my $item (qw(description rate taxnumber)) {
1987 my $index = $form->{"taxaccounts_$i"} . "_$item";
1988 $form->{$index} = $newform{$index};
1991 # credit remaining calculation
1992 $amount = $form->{"sellprice_$i"} * (1 - $form->{"discount_$i"} / 100) * $form->{"qty_$i"};
1994 map { $form->{"${_}_base"} += $amount } (split / /, $form->{"taxaccounts_$i"});
1995 map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{"taxaccounts_$i"} if !$form->{taxincluded};
1997 $form->{creditremaining} -= $amount;
1999 # redo number formatting, because invoice parse them!
2000 map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}) } qw(weight listprice sellprice lastcost rop);
2003 $form->{"id_$i"} = $parts_id;
2005 # Get the actual price factor (not just the ID) for the marge calculation.
2006 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
2007 foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
2008 next if ($pfac->{id} != $newform{price_factor_id});
2009 $form->{"marge_price_factor_$i"} = $pfac->{factor};
2012 delete $form->{ALL_PRICE_FACTORS};
2014 delete $form->{action};
2016 # restore original callback
2017 $callback = $form->unescape($form->{callback});
2018 $form->{callback} = $form->unescape($form->{old_callback});
2019 delete $form->{old_callback};
2021 $form->{makemodel_rows}--;
2023 # put callback together
2024 foreach my $key (keys %$form) {
2026 # do single escape for Apache 2.0
2027 my $value = $form->escape($form->{$key}, 1);
2028 $callback .= qq|&$key=$value|;
2030 $form->{callback} = $callback;
2036 $lxdebug->leave_sub();
2040 $lxdebug->enter_sub();
2042 $auth->assert('part_service_assembly_edit');
2044 # saving the history
2045 if(!exists $form->{addition}) {
2046 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
2047 $form->{addition} = "SAVED AS NEW";
2048 $form->{what_done} = "part";
2049 $form->save_history;
2051 # /saving the history
2053 if ($form->{"original_partnumber"} &&
2054 ($form->{"partnumber"} eq $form->{"original_partnumber"})) {
2055 $form->{partnumber} = "";
2058 $lxdebug->leave_sub();
2062 $lxdebug->enter_sub();
2064 $auth->assert('part_service_assembly_edit');
2066 # saving the history
2067 if(!exists $form->{addition}) {
2068 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
2069 $form->{addition} = "DELETED";
2070 $form->{what_done} = "part";
2071 $form->save_history;
2073 # /saving the history
2074 my $rc = IC->delete(\%myconfig, \%$form);
2077 $form->redirect($locale->text('Item deleted!')) if ($rc > 0);
2078 $form->error($locale->text('Cannot delete item!'));
2080 $lxdebug->leave_sub();
2084 $lxdebug->enter_sub();
2086 $auth->assert('part_service_assembly_details');
2091 pricegroup => $form->{"pricegroup_$_"},
2092 pricegroup_id => $form->{"pricegroup_id_$_"},
2093 price => $form->{"price_$_"},
2096 print $form->parse_html_template('ic/price_row', { PRICES => \@PRICES });
2098 $lxdebug->leave_sub();
2101 sub ajax_autocomplete {
2102 $main::lxdebug->enter_sub();
2104 my $form = $main::form;
2105 my %myconfig = %main::myconfig;
2107 $form->{column} = 'description' unless $form->{column} =~ /^partnumber|description$/;
2108 $form->{$form->{column}} = $form->{q} || '';
2109 $form->{limit} = ($form->{limit} * 1) || 10;
2110 $form->{searchitems} ||= '';
2112 my @results = IC->all_parts(\%myconfig, $form);
2114 print $form->ajax_response_header(),
2115 $form->parse_html_template('ic/ajax_autocomplete');
2117 $main::lxdebug->leave_sub();
2121 $::lxdebug->enter_sub;
2123 $auth->assert('part_service_assembly_edit');
2127 $::form->language_payment(\%::myconfig);
2129 Common::webdav_folder($::form);
2132 price_row($::form->{price_rows});
2133 makemodel_row(++$::form->{makemodel_rows}) if $::form->{item} =~ /^(part|service)$/;
2134 assembly_row(++$::form->{assembly_rows}) if $::form->{item} eq 'assembly';
2138 $::lxdebug->leave_sub;
2141 sub back_to_record {
2145 delete @{$::form}{qw(action action_add action_back_to_record back_sub description item notes partnumber sellprice taxaccount2 unit vc)};
2147 $::auth->restore_form_from_session($::form->{previousform}, clobber => 1);
2148 $::form->{rowcount}--;
2149 $::form->{action} = 'display_form';
2150 $::form->{callback} = $::form->{script} . '?' . join('&', map { $::form->escape($_) . '=' . $::form->escape($::form->{$_}) } sort keys %{ $::form });
2154 sub continue { call_sub($form->{"nextsub"}); }
2157 my $action = first { $::form->{"action_${_}"} } qw(add back_to_record);
2158 $::form->error($::locale->text('No action defined.')) unless $action;
2160 $::form->{dispatched_action} = $action;