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, });
120 $lxdebug->leave_sub();
123 sub search_update_prices {
124 $lxdebug->enter_sub();
126 $auth->assert('part_service_assembly_edit');
128 my $pricegroups = IC->get_pricegroups(\%myconfig, \%$form);
130 $form->{title} = $locale->text('Update Prices');
134 print $form->parse_html_template('ic/search_update_prices', { PRICE_ROWS => $pricegroups });
136 $lxdebug->leave_sub();
139 sub confirm_price_update {
140 $lxdebug->enter_sub();
142 $auth->assert('part_service_assembly_edit');
145 my $value_found = undef;
147 foreach my $idx (qw(sellprice listprice), (1..$form->{price_rows})) {
148 my $name = $idx =~ m/\d/ ? $form->{"pricegroup_${idx}"} : $idx eq 'sellprice' ? $locale->text('Sell Price') : $locale->text('List Price');
149 my $type = $idx =~ m/\d/ ? $form->{"pricegroup_type_${idx}"} : $form->{"${idx}_type"};
150 my $value_idx = $idx =~ m/\d/ ? "price_${idx}" : $idx;
151 my $value = $form->parse_amount(\%myconfig, $form->{$value_idx});
153 if ((0 > $value) && ($type eq 'percent')) {
154 push @errors, $locale->text('You cannot adjust the price for pricegroup "#1" by a negative percentage.', $name);
156 } elsif (!$value && ($form->{$value_idx} ne '')) {
157 push @errors, $locale->text('No valid number entered for pricegroup "#1".', $name);
159 } elsif (0 < $value) {
164 push @errors, $locale->text('No prices will be updated because no prices have been entered.') if (!$value_found);
166 my $num_matches = IC->get_num_matches_for_priceupdate();
171 $form->show_generic_error(join('<br>', @errors), 'back_button' => 1);
174 $form->{nextsub} = "update_prices";
176 map { delete $form->{$_} } qw(action header);
178 print $form->parse_html_template('ic/confirm_price_update', { HIDDENS => [ map { name => $_, value => $form->{$_} }, keys %$form ],
179 num_matches => $num_matches });
181 $lxdebug->leave_sub();
185 $lxdebug->enter_sub();
187 $auth->assert('part_service_assembly_edit');
189 my $num_updated = IC->update_prices(\%myconfig, \%$form);
191 if (-1 != $num_updated) {
192 $form->redirect($locale->text('#1 prices were updated.', $num_updated));
194 $form->error($locale->text('Could not update prices!'));
197 $lxdebug->leave_sub();
201 # $lxdebug->enter_sub();
203 # $auth->assert('part_service_assembly_edit');
205 # our ($j, $lastndx);
208 # $form->{title} = $locale->text('Top 100 hinzufuegen');
212 # push @custom_hiddens, qw(searchitems title bom titel revers lastsort sort ndxs_counter extras);
213 # push @custom_hiddens, qw(itemstatus l_linetotal l_partnumber l_description l_onhand l_unit l_sellprice l_linetotalsellprice);
215 # +{ name => 'row', value => $j },
216 # +{ name => 'nextsub', value => 'item_selected' },
217 # +{ name => 'test', value => 'item_selected' },
218 # +{ name => 'lastndx', value => $lastndx },
219 # map(+{ name => $_, value => $form->{$_} }, @custom_hiddens),
222 # my ($partnumber, $description, $unit, $sellprice, $soldtotal);
223 # # if choice set data
224 ## if ($form->{ndx}) {
225 ## for my $i (0 .. $form->{ndxs_counter}) {
227 ## # insert data into top100
228 ## push @{ $form->{parts} },
230 ## partnumber => $form->{"totop100_partnumber_$j"},
231 ## description => $form->{"totop100_description_$j"},
232 ## unit => $form->{"totop100_unit_$j"},
233 ## sellprice => $form->{"totop100_sellprice_$j"},
234 ## soldtotal => $form->{"totop100_soldtotal_$j"},
241 # # set data for next page
242 # for my $i (1 .. $form->{ndxs_counter}) {
243 # $partnumber = $form->{"totop100_partnumber_$i"};
244 # $description = $form->{"totop100_description_$i"};
245 # $unit = $form->{"totop100_unit_$i"};
246 # $sellprice = $form->{"totop100_sellprice_$i"};
247 # $soldtotal = $form->{"totop100_soldtotal_$i"};
250 # totop100_partnumber => $form->{"totop100_partnumber_$i"},
251 # totop100_description => $form->{"totop100_description_$i"},
252 # totop100_unit => $form->{"totop100_unit_$i"},
253 # totop100_sellprice => $form->{"totop100_sellprice_$i"},
254 # totop100_soldtotal => $form->{"totop100_soldtotal_$i"},
258 ##<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
259 ##<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
260 ##<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
261 ##<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
262 ##<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
266 # print $form->parse_html_template('ic/choice', +{ HIDDENS => \@HIDDENS, PARTS => \@PARTS });
268 # $lxdebug->leave_sub();
272 # $lxdebug->enter_sub();
274 # $auth->assert('part_service_assembly_edit');
277 # our ($partnumber, $description, $unit, $sellprice, $soldtotal);
279 # my @sortorders = ("", "partnumber", "description", "all");
280 # my $sortorder = $sortorders[($form->{description} ? 2 : 0) + ($form->{partnumber} ? 1 : 0)];
281 # IC->get_parts(\%myconfig, \%$form, $sortorder);
283 # $form->{title} = $locale->text('Top 100 hinzufuegen');
288 # <h1>| . $locale->text('choice part') . qq|</h1>
289 # <form method=post action=ic.pl>
291 # <tr class=listheading>
293 # <th class=listheading>| . $locale->text('Part Number') . qq|</th>
294 # <th class=listheading>| . $locale->text('Part Description') . qq|</th>
295 # <th class=listheading>| . $locale->text('Unit of measure') . qq|</th>
296 # <th class=listheading>| . $locale->text('Sell Price') . qq|</th>
297 # <th class=listheading>| . $locale->text('soldtotal') . qq|</th>
301 # my $i = $form->{rows};
303 # for ($j = 1; $j <= $i; $j++) {
306 # <tr class=listrow| . ($j % 2) . qq|>|;
309 # <td><input name=ndx class=radio type=radio value=$j checked></td>|;
312 # <td><input name=ndx class=radio type=radio value=$j></td>|;
315 # <td><input name="new_partnumber_$j" type=hidden value="$form->{"partnumber_$j"}">$form->{"partnumber_$j"}</td>
316 # <td><input name="new_description_$j" type=hidden value="$form->{"description_$j"}">$form->{"description_$j"}</td>
317 # <td><input name="new_unit_$j" type=hidden value="$form->{"unit_$j"}">$form->{"unit_$j"}</td>
318 # <td><input name="new_sellprice_$j" type=hidden value="$form->{"sellprice_$j"}">$form->{"sellprice_$j"}</td>
319 # <td><input name="new_soldtotal_$j" type=hidden value="$form->{"soldtotal_$j"}">$form->{"soldtotal_$j"}</td>
322 # <input name="new_id_$j" type=hidden value="$form->{"id_$j"}">|;
332 #<input type=hidden name=itemstatus value="$form->{itemstatus}">
333 #<input type=hidden name=l_linetotal value="$form->{l_linetotal}">
334 #<input type=hidden name=l_partnumber value="$form->{l_partnumber}">
335 #<input type=hidden name=l_description value="$form->{l_description}">
336 #<input type=hidden name=l_onhand value="$form->{l_onhand}">
337 #<input type=hidden name=l_unit value="$form->{l_unit}">
338 #<input type=hidden name=l_sellprice value="$form->{l_sellprice}">
339 #<input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
340 #<input type=hidden name=sort value="$form->{sort}">
341 #<input type=hidden name=revers value="$form->{revers}">
342 #<input type=hidden name=lastsort value="$form->{lastsort}">
344 #<input type=hidden name=bom value="$form->{bom}">
345 #<input type=hidden name=titel value="$form->{titel}">
346 #<input type=hidden name=searchitems value="$form->{searchitems}">
348 #<input type=hidden name=row value=$j>
350 #<input type=hidden name=nextsub value=item_selected>
352 #<input name=lastndx type=hidden value=$lastndx>
354 #<input name=ndxs_counter type=hidden value=$form->{ndxs_counter}>|;
358 # if (($form->{ndxs_counter}) > 0) {
359 # for ($i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
361 # $partnumber = $form->{"totop100_partnumber_$i"};
362 # $description = $form->{"totop100_description_$i"};
363 # $unit = $form->{"totop100_unit_$i"};
364 # $sellprice = $form->{"totop100_sellprice_$i"};
365 # $soldtotal = $form->{"totop100_soldtotal_$i"};
368 #<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
369 #<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
370 #<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
371 #<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
372 #<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
380 #<input class=submit type=submit name=action value="|
381 # . $locale->text('TOP100') . qq|">
385 # $lxdebug->leave_sub();
389 $lxdebug->enter_sub();
391 $auth->assert('part_service_assembly_edit');
394 $form->{ndxs_counter}++;
396 if ($form->{ndxs_counter} > 0) {
398 my $index = $form->{ndx};
400 $form->{"totop100_partnumber_$form->{ndxs_counter}"} = $form->{"new_partnumber_$index"};
401 $form->{"totop100_description_$form->{ndxs_counter}"} = $form->{"new_description_$index"};
402 $form->{"totop100_unit_$form->{ndxs_counter}"} = $form->{"new_unit_$index"};
403 $form->{"totop100_sellprice_$form->{ndxs_counter}"} = $form->{"new_sellprice_$index"};
404 $form->{"totop100_soldtotal_$form->{ndxs_counter}"} = $form->{"new_soldtotal_$index"};
408 $lxdebug->leave_sub();
412 $lxdebug->enter_sub();
414 $auth->assert('part_service_assembly_edit');
416 my ($revers, $lastsort, $callback, $option, $description, $sameitem,
417 $partnumber, $unit, $sellprice, $soldtotal, $totop100, $onhand, $align);
418 my (@column_index, %column_header, %column_data);
419 my ($totalsellprice, $totallastcost, $totallistprice, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice);
421 $form->{top100} = "top100";
422 $form->{l_soldtotal} = "Y";
423 $form->{soldtotal} = "soldtotal";
424 $form->{sort} = "soldtotal";
425 $form->{l_qty} = "N";
426 $form->{l_linetotal} = "";
428 $form->{number} = "position";
429 $form->{l_number} = "Y";
433 $form->{title} = $locale->text('Top 100');
435 $revers = $form->{revers};
436 $lastsort = $form->{lastsort};
438 if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
440 $form->{lastsort} = "partnumber";
441 $form->{sort} = "partnumber";
445 "$form->{script}?action=top100&searchitems=$form->{searchitems}&itemstatus=$form->{itemstatus}&bom=$form->{bom}&l_linetotal=$form->{l_linetotal}&title="
446 . $form->escape($form->{title}, 1);
448 # if we have a serialnumber limit search
449 if ($form->{serialnumber} || $form->{l_serialnumber}) {
450 $form->{l_serialnumber} = "Y";
451 unless ( $form->{bought}
454 || $form->{quoted}) {
455 $form->{bought} = $form->{sold} = 1;
458 IC->all_parts(\%myconfig, \%$form);
460 if ($form->{itemstatus} eq 'active') {
461 $option .= $locale->text('Active') . " : ";
463 if ($form->{itemstatus} eq 'obsolete') {
464 $option .= $locale->text('Obsolete') . " : ";
466 if ($form->{itemstatus} eq 'orphaned') {
467 $option .= $locale->text('Orphaned') . " : ";
469 if ($form->{itemstatus} eq 'onhand') {
470 $option .= $locale->text('On Hand') . " : ";
471 $form->{l_onhand} = "Y";
473 if ($form->{itemstatus} eq 'short') {
474 $option .= $locale->text('Short') . " : ";
475 $form->{l_onhand} = "Y";
477 if ($form->{onorder}) {
478 $form->{l_ordnumber} = "Y";
479 $callback .= "&onorder=$form->{onorder}";
480 $option .= $locale->text('On Order') . " : ";
482 if ($form->{ordered}) {
483 $form->{l_ordnumber} = "Y";
484 $callback .= "&ordered=$form->{ordered}";
485 $option .= $locale->text('Ordered') . " : ";
488 $form->{l_quonumber} = "Y";
489 $callback .= "&rfq=$form->{rfq}";
490 $option .= $locale->text('RFQ') . " : ";
492 if ($form->{quoted}) {
493 $form->{l_quonumber} = "Y";
494 $callback .= ""ed=$form->{quoted}";
495 $option .= $locale->text('Quoted') . " : ";
497 if ($form->{bought}) {
498 $form->{l_invnumber} = "Y";
499 $callback .= "&bought=$form->{bought}";
500 $option .= $locale->text('Bought') . " : ";
503 $form->{l_invnumber} = "Y";
504 $callback .= "&sold=$form->{sold}";
505 $option .= $locale->text('Sold') . " : ";
512 || $form->{quoted}) {
514 $form->{l_lastcost} = "";
515 $form->{l_name} = "Y";
516 if ($form->{transdatefrom}) {
517 $callback .= "&transdatefrom=$form->{transdatefrom}";
519 . $locale->text('From')
521 . $locale->date(\%myconfig, $form->{transdatefrom}, 1);
523 if ($form->{transdateto}) {
524 $callback .= "&transdateto=$form->{transdateto}";
526 . $locale->text('To')
528 . $locale->date(\%myconfig, $form->{transdateto}, 1);
534 if ($form->{partnumber}) {
535 $callback .= "&partnumber=$form->{partnumber}";
536 $option .= $locale->text('Part Number') . qq| : $form->{partnumber}<br>|;
539 $callback .= "&partnumber=$form->{ean}";
540 $option .= $locale->text('EAN') . qq| : $form->{ean}<br>|;
542 if ($form->{partsgroup}) {
543 $callback .= "&partsgroup=$form->{partsgroup}";
544 $option .= $locale->text('Group') . qq| : $form->{partsgroup}<br>|;
546 if ($form->{serialnumber}) {
547 $callback .= "&serialnumber=$form->{serialnumber}";
548 $option .= $locale->text('Serial Number') . qq| : $form->{serialnumber}<br>|;
550 if ($form->{description}) {
551 $callback .= "&description=$form->{description}";
552 $description = $form->{description};
553 $description =~ s/\n/<br>/g;
554 $option .= $locale->text('Part Description') . qq| : $form->{description}<br>|;
557 $callback .= "&make=$form->{make}";
558 $option .= $locale->text('Make') . qq| : $form->{make}<br>|;
560 if ($form->{model}) {
561 $callback .= "&model=$form->{model}";
562 $option .= $locale->text('Model') . qq| : $form->{model}<br>|;
564 if ($form->{drawing}) {
565 $callback .= "&drawing=$form->{drawing}";
566 $option .= $locale->text('Drawing') . qq| : $form->{drawing}<br>|;
568 if ($form->{microfiche}) {
569 $callback .= "µfiche=$form->{microfiche}";
570 $option .= $locale->text('Microfiche') . qq| : $form->{microfiche}<br>|;
572 if ($form->{l_soldtotal}) {
573 $callback .= "&soldtotal=$form->{soldtotal}";
574 $option .= $locale->text('soldtotal') . qq| : $form->{soldtotal}<br>|;
577 my @columns = $form->sort_columns(
578 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)
581 if ($form->{l_linetotal}) {
582 $form->{l_onhand} = "Y";
583 $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
584 if ($form->{l_lastcost}) {
585 $form->{l_linetotallastcost} = "Y";
586 if (($form->{searchitems} eq 'assembly') && !$form->{bom}) {
587 $form->{l_linetotallastcost} = "";
590 $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
593 if ($form->{searchitems} eq 'service') {
595 # remove bin, weight and rop from list
596 map { $form->{"l_$_"} = "" } qw(bin weight rop);
598 $form->{l_onhand} = "";
600 # qty is irrelevant unless bought or sold
606 || $form->{quoted}) {
607 $form->{l_onhand} = "Y";
609 $form->{l_linetotalsellprice} = "";
610 $form->{l_linetotallastcost} = "";
614 foreach my $item (@columns) {
615 if ($form->{"l_$item"} eq "Y") {
616 push @column_index, $item;
618 # add column to callback
619 $callback .= "&l_$item=Y";
623 if ($form->{l_subtotal} eq 'Y') {
624 $callback .= "&l_subtotal=Y";
627 $column_header{number} =
628 qq|<th class=listheading nowrap>| . $locale->text('number') . qq|</th>|;
629 $column_header{partnumber} =
630 qq|<th nowrap><a class=listheading href=$callback&sort=partnumber&revers=$form->{revers}&lastsort=$form->{lastsort}>|
631 . $locale->text('Part Number')
633 $column_header{description} =
634 qq|<th nowrap><a class=listheading href=$callback&sort=description&revers=$form->{revers}&lastsort=$form->{lastsort}>|
635 . $locale->text('Part Description')
637 $column_header{partsgroup} =
638 qq|<th nowrap><a class=listheading href=$callback&sort=partsgroup>|
639 . $locale->text('Group')
641 $column_header{bin} =
642 qq|<th><a class=listheading href=$callback&sort=bin>|
643 . $locale->text('Bin')
645 $column_header{priceupdate} =
646 qq|<th nowrap><a class=listheading href=$callback&sort=priceupdate>|
647 . $locale->text('Updated')
649 $column_header{onhand} =
650 qq|<th nowrap><a class=listheading href=$callback&sort=onhand&revers=$form->{revers}&lastsort=$form->{lastsort}>|
651 . $locale->text('Qty')
653 $column_header{unit} =
654 qq|<th class=listheading nowrap>| . $locale->text('Unit') . qq|</th>|;
655 $column_header{listprice} =
656 qq|<th class=listheading nowrap>|
657 . $locale->text('List Price')
659 $column_header{lastcost} =
660 qq|<th class=listheading nowrap>| . $locale->text('Last Cost') . qq|</th>|;
661 $column_header{rop} =
662 qq|<th class=listheading nowrap>| . $locale->text('ROP') . qq|</th>|;
663 $column_header{weight} =
664 qq|<th class=listheading nowrap>| . $locale->text('Weight') . qq|</th>|;
666 $column_header{invnumber} =
667 qq|<th nowrap><a class=listheading href=$callback&sort=invnumber>|
668 . $locale->text('Invoice Number')
670 $column_header{ordnumber} =
671 qq|<th nowrap><a class=listheading href=$callback&sort=ordnumber>|
672 . $locale->text('Order Number')
674 $column_header{quonumber} =
675 qq|<th nowrap><a class=listheading href=$callback&sort=quonumber>|
676 . $locale->text('Quotation')
679 $column_header{name} =
680 qq|<th nowrap><a class=listheading href=$callback&sort=name>|
681 . $locale->text('Name')
684 $column_header{sellprice} =
685 qq|<th class=listheading nowrap>|
686 . $locale->text('Sell Price')
688 $column_header{linetotalsellprice} =
689 qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
690 $column_header{linetotallastcost} =
691 qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
692 $column_header{linetotallistprice} =
693 qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
695 $column_header{image} =
696 qq|<th class=listheading nowrap>| . $locale->text('Image') . qq|</a></th>|;
697 $column_header{drawing} =
698 qq|<th nowrap><a class=listheading href=$callback&sort=drawing>|
699 . $locale->text('Drawing')
701 $column_header{microfiche} =
702 qq|<th nowrap><a class=listheading href=$callback&sort=microfiche>|
703 . $locale->text('Microfiche')
706 $column_header{serialnumber} =
707 qq|<th nowrap><a class=listheading href=$callback&sort=serialnumber>|
708 . $locale->text('Serial Number')
710 $column_header{soldtotal} =
711 qq|<th nowrap><a class=listheading href=$callback&sort=soldtotal&revers=$form->{revers}&lastsort=$form->{lastsort}>|
712 . $locale->text('soldtotal')
716 my $colspan = $#column_index + 1;
719 <h1>$form->{title}</h1>
723 <tr><td colspan=$colspan>$option</td></tr>
725 <tr class=listheading>
728 map { print "\n$column_header{$_}" } @column_index;
734 # add order to callback
735 $form->{callback} = $callback .= "&sort=$form->{sort}";
737 # escape callback for href
738 $callback = $form->escape($callback);
740 if (@{ $form->{parts} }) {
741 $sameitem = $form->{parts}->[0]->{ $form->{sort} };
744 # insert numbers for top100
746 foreach my $ref (@{ $form->{parts} }) {
751 # if avaible -> insert choice here
752 if (($form->{ndxs_counter}) > 0) {
753 for (my $i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
754 $partnumber = $form->{"totop100_partnumber_$i"};
755 $description = $form->{"totop100_description_$i"};
756 $unit = $form->{"totop100_unit_$i"};
757 $sellprice = $form->{"totop100_sellprice_$i"};
758 $soldtotal = $form->{"totop100_soldtotal_$i"};
761 <input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
762 <input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
763 <input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
764 <input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
765 <input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
769 push @{ $form->{parts} },
771 partnumber => "$partnumber",
772 description => "$description",
774 sellprice => "$sellprice",
775 soldtotal => "$soldtotal" };
778 # build data for columns
780 foreach my $ref (@{ $form->{parts} }) {
782 if ($form->{l_subtotal} eq 'Y' && !$ref->{assemblyitem}) {
783 if ($sameitem ne $ref->{ $form->{sort} }) {
784 parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
785 $sameitem = $ref->{ $form->{sort} };
789 $ref->{exchangerate} = 1 unless $ref->{exchangerate};
790 $ref->{sellprice} *= $ref->{exchangerate};
791 $ref->{listprice} *= $ref->{exchangerate};
792 $ref->{lastcost} *= $ref->{exchangerate};
794 # use this for assemblies
795 $onhand = $ref->{onhand};
798 if ($ref->{assemblyitem}) {
800 $onhand = 0 if ($form->{sold});
803 $ref->{description} =~ s/\n/<br>/g;
805 $column_data{number} =
807 . $form->format_amount(\%myconfig, $ref->{number})
809 $column_data{partnumber} =
810 "<td align=$align>$ref->{partnumber} </a></td>";
811 $column_data{description} = "<td>$ref->{description} </td>";
812 $column_data{partsgroup} = "<td>$ref->{partsgroup} </td>";
814 $column_data{onhand} =
816 . $form->format_amount(\%myconfig, $ref->{onhand})
818 $column_data{sellprice} =
820 . $form->format_amount(\%myconfig, $ref->{sellprice})
822 $column_data{listprice} =
824 . $form->format_amount(\%myconfig, $ref->{listprice})
826 $column_data{lastcost} =
828 . $form->format_amount(\%myconfig, $ref->{lastcost})
831 $column_data{linetotalsellprice} = "<td align=right>"
832 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{sellprice}, 2)
834 $column_data{linetotallastcost} = "<td align=right>"
835 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{lastcost}, 2)
837 $column_data{linetotallistprice} = "<td align=right>"
838 . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{listprice}, 2)
841 if (!$ref->{assemblyitem}) {
842 $totalsellprice += $onhand * $ref->{sellprice};
843 $totallastcost += $onhand * $ref->{lastcost};
844 $totallistprice += $onhand * $ref->{listprice};
846 $subtotalonhand += $onhand;
847 $subtotalsellprice += $onhand * $ref->{sellprice};
848 $subtotallastcost += $onhand * $ref->{lastcost};
849 $subtotallistprice += $onhand * $ref->{listprice};
854 . $form->format_amount(\%myconfig, $ref->{rop}) . "</td>";
855 $column_data{weight} =
857 . $form->format_amount(\%myconfig, $ref->{weight})
859 $column_data{unit} = "<td>$ref->{unit} </td>";
860 $column_data{bin} = "<td>$ref->{bin} </td>";
861 $column_data{priceupdate} = "<td>$ref->{priceupdate} </td>";
863 $column_data{invnumber} =
864 ($ref->{module} ne 'oe')
865 ? "<td><a href=$ref->{module}.pl?action=edit&type=invoice&id=$ref->{trans_id}&callback=$callback>$ref->{invnumber}</a></td>"
866 : "<td>$ref->{invnumber}</td>";
867 $column_data{ordnumber} =
868 ($ref->{module} eq 'oe')
869 ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{ordnumber}</a></td>"
870 : "<td>$ref->{ordnumber}</td>";
871 $column_data{quonumber} =
872 ($ref->{module} eq 'oe' && !$ref->{ordnumber})
873 ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{quonumber}</a></td>"
874 : "<td>$ref->{quonumber}</td>";
876 $column_data{name} = "<td>$ref->{name}</td>";
878 $column_data{image} =
880 ? "<td><a href=$ref->{image}><img src=$ref->{image} height=32 border=0></a></td>"
882 $column_data{drawing} =
884 ? "<td><a href=$ref->{drawing}>$ref->{drawing}</a></td>"
886 $column_data{microfiche} =
888 ? "<td><a href=$ref->{microfiche}>$ref->{microfiche}</a></td>"
891 $column_data{serialnumber} = "<td>$ref->{serialnumber}</td>";
893 $column_data{soldtotal} = "<td align=right>$ref->{soldtotal}</td>";
897 print "<tr class=listrow$i>";
899 map { print "\n$column_data{$_}" } @column_index;
906 if ($form->{l_subtotal} eq 'Y') {
907 parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
910 if ($form->{"l_linetotal"}) {
911 map { $column_data{$_} = "<td> </td>" } @column_index;
912 $column_data{linetotalsellprice} =
913 "<th class=listtotal align=right>"
914 . $form->format_amount(\%myconfig, $totalsellprice, 2)
916 $column_data{linetotallastcost} =
917 "<th class=listtotal align=right>"
918 . $form->format_amount(\%myconfig, $totallastcost, 2)
920 $column_data{linetotallistprice} =
921 "<th class=listtotal align=right>"
922 . $form->format_amount(\%myconfig, $totallistprice, 2)
925 print "<tr class=listtotal>";
927 map { print "\n$column_data{$_}" } @column_index;
934 <tr><td colspan=$colspan><hr size=3 noshade></td></tr>
943 <form method=post action=$form->{script}>
945 <input type=hidden name=itemstatus value="$form->{itemstatus}">
946 <input type=hidden name=l_linetotal value="$form->{l_linetotal}">
947 <input type=hidden name=l_partnumber value="$form->{l_partnumber}">
948 <input type=hidden name=l_description value="$form->{l_description}">
949 <input type=hidden name=l_onhand value="$form->{l_onhand}">
950 <input type=hidden name=l_unit value="$form->{l_unit}">
951 <input type=hidden name=l_sellprice value="$form->{l_sellprice}">
952 <input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
953 <input type=hidden name=sort value="$form->{sort}">
954 <input type=hidden name=revers value="$form->{revers}">
955 <input type=hidden name=lastsort value="$form->{lastsort}">
956 <input type=hidden name=parts value="$form->{parts}">
958 <input type=hidden name=bom value="$form->{bom}">
959 <input type=hidden name=titel value="$form->{titel}">
960 <input type=hidden name=searchitems value="$form->{searchitems}">|;
965 <!-- <input type=hidden name=ndxs_counter value="$form->{ndxs_counter}">-->
967 <!-- <input class=submit type=submit name=action value="|
968 . $locale->text('choice') . qq|"> -->
973 $lxdebug->leave_sub();
978 # Warning, deep magic ahead.
979 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
981 # flags coming from the form:
983 # searchitems=part revers=0 lastsort=''
986 # partnumber ean description partsgroup serialnumber make model drawing microfiche
987 # transdatefrom transdateto
990 # itemstatus = active | onhand | short | obsolete | orphaned
991 # action = continue | top100
994 # bought sold onorder ordered rfq quoted
995 # l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
996 # l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
997 # l_partsgroup l_subtotal l_soldtotal l_deliverydate l_pricegroups
1000 # nextsub revers lastsort sort ndxs_counter
1002 sub generate_report {
1003 $lxdebug->enter_sub();
1005 $auth->assert('part_service_assembly_details');
1007 my ($revers, $lastsort, $description);
1009 my $cvar_configs = CVar->get_configs('module' => 'IC');
1011 $form->{title} = (ucfirst $form->{searchitems}) . "s";
1012 $form->{title} =~ s/ys$/ies/;
1013 $form->{title} = $locale->text($form->{title});
1016 'bin' => { 'text' => $locale->text('Bin'), },
1017 'deliverydate' => { 'text' => $locale->text('deliverydate'), },
1018 'description' => { 'text' => $locale->text('Part Description'), },
1019 'notes' => { 'text' => $locale->text('Notes'), },
1020 'drawing' => { 'text' => $locale->text('Drawing'), },
1021 'ean' => { 'text' => $locale->text('EAN'), },
1022 'image' => { 'text' => $locale->text('Image'), },
1023 'insertdate' => { 'text' => $locale->text('Insert Date'), },
1024 'invnumber' => { 'text' => $locale->text('Invoice Number'), },
1025 'lastcost' => { 'text' => $locale->text('Last Cost'), },
1026 'linetotallastcost' => { 'text' => $locale->text('Extended'), },
1027 'linetotallistprice' => { 'text' => $locale->text('Extended'), },
1028 'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
1029 'listprice' => { 'text' => $locale->text('List Price'), },
1030 'microfiche' => { 'text' => $locale->text('Microfiche'), },
1031 'name' => { 'text' => $locale->text('Name'), },
1032 'onhand' => { 'text' => $locale->text('Stocked Qty'), },
1033 'ordnumber' => { 'text' => $locale->text('Order Number'), },
1034 'partnumber' => { 'text' => $locale->text('Part Number'), },
1035 'partsgroup' => { 'text' => $locale->text('Group'), },
1036 'priceupdate' => { 'text' => $locale->text('Updated'), },
1037 'quonumber' => { 'text' => $locale->text('Quotation'), },
1038 'rop' => { 'text' => $locale->text('ROP'), },
1039 'sellprice' => { 'text' => $locale->text('Sell Price'), },
1040 'serialnumber' => { 'text' => $locale->text('Serial Number'), },
1041 'soldtotal' => { 'text' => $locale->text('Qty in Selected Records'), },
1042 'name' => { 'text' => $locale->text('Name in Selected Records'), },
1043 'transdate' => { 'text' => $locale->text('Transdate'), },
1044 'unit' => { 'text' => $locale->text('Unit'), },
1045 'weight' => { 'text' => $locale->text('Weight'), },
1046 'shop' => { 'text' => $locale->text('Shopartikel'), },
1047 'projectnumber' => { 'text' => $locale->text('Project Number'), },
1048 'projectdescription' => { 'text' => $locale->text('Project Description'), },
1051 $revers = $form->{revers};
1052 $lastsort = $form->{lastsort};
1054 # sorting and direction of sorting
1055 # ToDO: change this to the simpler field+direction method
1056 if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
1057 $form->{revers} = 0;
1058 $form->{lastsort} = "partnumber";
1059 $form->{sort} = "partnumber";
1061 if ($form->{lastsort} eq $form->{sort}) {
1062 $form->{revers} = 1 - $form->{revers};
1064 $form->{revers} = 0;
1065 $form->{lastsort} = $form->{sort};
1069 # special case if we have a serialnumber limit search
1070 # serialnumbers are only given in invoices and orders,
1071 # so they can only pop up in bought, sold, rfq, and quoted stuff
1072 $form->{no_sn_joins} = 'Y' if ( !$form->{bought} && !$form->{sold}
1073 && !$form->{rfq} && !$form->{quoted}
1074 && ($form->{l_serialnumber} || $form->{serialnumber}));
1076 # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
1077 # if any of these are ticked the behavior changes slightly for lastcost
1078 # since all those are aggregation checks for the legder tables this is an internal switch
1079 # refered to as ledgerchecks
1080 $form->{ledgerchecks} = 'Y' if ( $form->{bought} || $form->{sold} || $form->{onorder}
1081 || $form->{ordered} || $form->{rfq} || $form->{quoted});
1083 # if something should be activated if something else is active, enter it here
1084 my %dependencies = (
1085 onhand => [ qw(l_onhand) ],
1086 short => [ qw(l_onhand) ],
1087 onorder => [ qw(l_ordnumber) ],
1088 ordered => [ qw(l_ordnumber) ],
1089 rfq => [ qw(l_quonumber) ],
1090 quoted => [ qw(l_quonumber) ],
1091 bought => [ qw(l_invnumber) ],
1092 sold => [ qw(l_invnumber) ],
1093 ledgerchecks => [ qw(l_name) ],
1094 serialnumber => [ qw(l_serialnumber) ],
1095 no_sn_joins => [ qw(bought sold) ],
1098 # get name of partsgroup if id is given
1100 if ($form->{partsgroup_id}) {
1101 my $pg = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load;
1102 $pg_name = $pg->{'partsgroup'};
1105 # these strings get displayed at the top of the results to indicate the user which switches were used
1107 active => $locale->text('Active'),
1108 obsolete => $locale->text('Obsolete'),
1109 orphaned => $locale->text('Orphaned'),
1110 onhand => $locale->text('On Hand'),
1111 short => $locale->text('Short'),
1112 onorder => $locale->text('On Order'),
1113 ordered => $locale->text('Ordered'),
1114 rfq => $locale->text('RFQ'),
1115 quoted => $locale->text('Quoted'),
1116 bought => $locale->text('Bought'),
1117 sold => $locale->text('Sold'),
1118 transdatefrom => $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
1119 transdateto => $locale->text('To (time)') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
1120 partnumber => $locale->text('Part Number') . ": '$form->{partnumber}'",
1121 partsgroup => $locale->text('Group') . ": '$form->{partsgroup}'",
1122 partsgroup_id => $locale->text('Group') . ": '$pg_name'",
1123 serialnumber => $locale->text('Serial Number') . ": '$form->{serialnumber}'",
1124 description => $locale->text('Part Description') . ": '$form->{description}'",
1125 make => $locale->text('Make') . ": '$form->{make}'",
1126 model => $locale->text('Model') . ": '$form->{model}'",
1127 drawing => $locale->text('Drawing') . ": '$form->{drawing}'",
1128 microfiche => $locale->text('Microfiche') . ": '$form->{microfiche}'",
1129 l_soldtotal => $locale->text('Qty in Selected Records'),
1130 ean => $locale->text('EAN') . ": '$form->{ean}'",
1131 insertdatefrom => $locale->text('Insert Date') . ": " . $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1),
1132 insertdateto => $locale->text('Insert Date') . ": " . $locale->text('To (time)') . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1),
1135 my @itemstatus_keys = qw(active obsolete orphaned onhand short);
1136 my @callback_keys = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
1137 drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto insertdatefrom insertdateto ean shop);
1139 # calculate dependencies
1140 for (@itemstatus_keys, @callback_keys) {
1141 next if ($form->{itemstatus} ne $_ && !$form->{$_});
1142 map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
1145 # generate callback and optionstrings
1147 for my $key (@itemstatus_keys, @callback_keys) {
1148 next if ($form->{itemstatus} ne $key && !$form->{$key});
1149 push @options, $optiontexts{$key};
1152 # special case for lastcost
1153 if ($form->{ledgerchecks}){
1154 # ledgerchecks don't know about sellprice or lastcost. they just return a
1155 # price. so rename sellprice to price, and drop lastcost.
1156 $column_defs{sellprice}{text} = $locale->text('Price');
1157 $form->{l_lastcost} = ""
1160 if ($form->{description}) {
1161 $description = $form->{description};
1162 $description =~ s/\n/<br>/g;
1165 if ($form->{l_linetotal}) {
1166 $form->{l_qty} = "Y";
1167 $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
1168 $form->{l_linetotallastcost} = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if $form->{l_lastcost};
1169 $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
1172 if ($form->{searchitems} eq 'service') {
1174 # remove bin, weight and rop from list
1175 map { $form->{"l_$_"} = "" } qw(bin weight rop);
1177 $form->{l_onhand} = "";
1179 # qty is irrelevant unless bought or sold
1180 if ( $form->{bought}
1185 || $form->{quoted}) {
1186 # $form->{l_onhand} = "Y";
1188 $form->{l_linetotalsellprice} = "";
1189 $form->{l_linetotallastcost} = "";
1193 # soldtotal doesn't make sense with more than one bsooqr option.
1194 # so reset it to sold (the most common option), and issue a warning
1196 # also it doesn't make sense without bsooqr. disable and issue a warning too
1197 my @bsooqr = qw(sold bought onorder ordered rfq quoted);
1198 my $bsooqr_mode = grep { $form->{$_} } @bsooqr;
1199 if ($form->{l_subtotal} && 1 < $bsooqr_mode) {
1200 my $enabled = first { $form->{$_} } @bsooqr;
1201 $form->{$_} = '' for @bsooqr;
1202 $form->{$enabled} = 'Y';
1204 push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
1206 if ($form->{l_soldtotal} && !$bsooqr_mode) {
1207 delete $form->{l_soldtotal};
1209 flash('warning', $::locale->text('Soldtotal does not make sense without any bsooqr options'));
1211 if ($form->{l_name} && !$bsooqr_mode) {
1212 delete $form->{l_name};
1214 flash('warning', $::locale->text('Name does not make sense without any bsooqr options'));
1216 IC->all_parts(\%myconfig, \%$form);
1219 partnumber description notes partsgroup bin onhand rop soldtotal unit listprice
1220 linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
1221 priceupdate weight image drawing microfiche invnumber ordnumber quonumber
1222 transdate name serialnumber deliverydate ean projectnumber projectdescription
1226 my $pricegroups = SL::DB::Manager::Pricegroup->get_all(sort => 'id');
1227 my @pricegroup_columns;
1228 my %column_defs_pricegroups;
1229 if ($form->{l_pricegroups}) {
1230 @pricegroup_columns = map { "pricegroup_" . $_->id } @{ $pricegroups };
1231 %column_defs_pricegroups = map {
1232 "pricegroup_" . $_->id => {
1233 text => $::locale->text('Pricegroup') . ' ' . $_->pricegroup,
1236 } @{ $pricegroups };
1238 push @columns, @pricegroup_columns;
1240 my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
1241 my @searchable_custom_variables = grep { $_->{searchable} } @{ $cvar_configs };
1242 my %column_defs_cvars = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
1244 push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
1246 %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
1247 map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
1248 map { $column_defs{$_}->{align} = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal shop), @pricegroup_columns;
1250 my @hidden_variables = (
1251 qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups insertdatefrom insertdateto),
1254 map({ "cvar_$_->{name}" } @searchable_custom_variables),
1255 map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
1256 map({ "l_$_" } @columns),
1259 my $callback = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
1261 my @sort_full = qw(partnumber description onhand soldtotal deliverydate insertdate shop);
1262 my @sort_no_revers = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
1264 foreach my $col (@sort_full) {
1265 $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
1267 map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
1269 # add order to callback
1270 $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
1272 my $report = SL::ReportGenerator->new(\%myconfig, $form);
1274 my %attachment_basenames = (
1275 'part' => $locale->text('part_list'),
1276 'service' => $locale->text('service_list'),
1277 'assembly' => $locale->text('assembly_list'),
1280 $report->set_options('raw_top_info_text' => $form->parse_html_template('ic/generate_report_top', { options => \@options }),
1281 'raw_bottom_info_text' => $form->parse_html_template('ic/generate_report_bottom'),
1282 'output_format' => 'HTML',
1283 'title' => $form->{title},
1284 'attachment_basename' => $attachment_basenames{$form->{searchitems}} . strftime('_%Y%m%d', localtime time),
1286 $report->set_options_from_form();
1287 $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
1289 $report->set_columns(%column_defs);
1290 $report->set_column_order(@columns);
1292 $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
1294 $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
1296 CVar->add_custom_variables_to_report('module' => 'IC',
1297 'trans_id_field' => 'id',
1298 'configs' => $cvar_configs,
1299 'column_defs' => \%column_defs,
1300 'data' => $form->{parts});
1302 CVar->add_custom_variables_to_report('module' => 'IC',
1303 'sub_module' => sub { $_[0]->{ioi} },
1304 'trans_id_field' => 'ioi_id',
1305 'configs' => $cvar_configs,
1306 'column_defs' => \%column_defs,
1307 'data' => $form->{parts});
1309 my @subtotal_columns = qw(sellprice listprice lastcost);
1310 my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
1311 my %totals = map { $_ => 0 } @subtotal_columns;
1313 my $same_item = @{ $form->{parts} } ? $form->{parts}[0]{ $form->{sort} } : undef;
1315 my $defaults = AM->get_defaults();
1318 foreach my $ref (@{ $form->{parts} }) {
1320 # fresh row, for inserting later
1321 my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
1323 $ref->{exchangerate} ||= 1;
1324 $ref->{price_factor} ||= 1;
1325 $ref->{sellprice} *= $ref->{exchangerate} / $ref->{price_factor};
1326 $ref->{listprice} *= $ref->{exchangerate} / $ref->{price_factor};
1327 $ref->{lastcost} *= $ref->{exchangerate} / $ref->{price_factor};
1329 # use this for assemblies
1330 my $soldtotal = $bsooqr_mode ? $ref->{soldtotal} : $ref->{onhand};
1332 if ($ref->{assemblyitem}) {
1333 $row->{partnumber}{align} = 'right';
1334 $row->{soldtotal}{data} = 0;
1335 $soldtotal = 0 if ($form->{sold});
1338 my $edit_link = build_std_url('action=edit', 'id=' . E($ref->{id}), 'callback');
1339 $row->{partnumber}->{link} = $edit_link;
1340 $row->{description}->{link} = $edit_link;
1342 foreach (qw(sellprice listprice lastcost)) {
1343 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}, 2);
1344 $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
1346 foreach ( @pricegroup_columns ) {
1347 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{"$_"}, 2);
1351 map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
1353 $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
1355 # 'yes' and 'no' for boolean value shop
1356 if ($form->{l_shop}) {
1357 $row->{shop}{data} = $row->{shop}{data}? $::locale->text('yes') : $::locale->text('no');
1360 if (!$ref->{assemblyitem}) {
1361 foreach my $col (@subtotal_columns) {
1362 $totals{$col} += $soldtotal * $ref->{$col};
1363 $subtotals{$col} += $soldtotal * $ref->{$col};
1366 $subtotals{soldtotal} += $soldtotal;
1370 if ($ref->{module} eq 'oe') {
1371 # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
1373 # | ist bestellt | Von Kunden bestellt | -> edit_oe_ord_link
1374 # | Anfrage | Angebot | -> edit_oe_quo_link
1376 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');
1377 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');
1379 $row->{ordnumber}{link} = $edit_oe_ord_link;
1380 $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
1383 $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback');
1386 # set properties of images
1387 if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
1388 $row->{image}{data} = '';
1389 $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
1391 map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
1393 $row->{notes}{data} = SL::HTML::Util->strip($ref->{notes});
1395 $report->add_data($row);
1397 my $next_ref = $form->{parts}[$idx + 1];
1399 # insert subtotal rows
1400 if (($form->{l_subtotal} eq 'Y') &&
1402 (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
1403 my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
1405 if (($form->{searchitems} ne 'assembly') || !$form->{bom}) {
1406 $row->{soldtotal}->{data} = $form->format_amount(\%myconfig, $subtotals{soldtotal});
1409 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
1410 map { $subtotals{$_} = 0 } ('soldtotal', @subtotal_columns);
1412 $report->add_data($row);
1414 $same_item = $next_ref->{ $form->{sort} };
1420 if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
1421 my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
1423 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
1425 $report->add_separator();
1426 $report->add_data($row);
1429 $report->generate_with_headers();
1431 $lxdebug->leave_sub();
1432 } #end generate_report
1434 sub parts_subtotal {
1435 $lxdebug->enter_sub();
1437 $auth->assert('part_service_assembly_edit');
1440 my ($column_index, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice) = @_;
1442 map { $column_data{$_} = "<td> </td>" } @{ $column_index };
1443 $$subtotalonhand = 0 if ($form->{searchitems} eq 'assembly' && $form->{bom});
1445 $column_data{onhand} =
1446 "<th class=listsubtotal align=right>"
1447 . $form->format_amount(\%myconfig, $$subtotalonhand)
1450 $column_data{linetotalsellprice} =
1451 "<th class=listsubtotal align=right>"
1452 . $form->format_amount(\%myconfig, $$subtotalsellprice, 2)
1454 $column_data{linetotallistprice} =
1455 "<th class=listsubtotal align=right>"
1456 . $form->format_amount(\%myconfig, $$subtotallistprice, 2)
1458 $column_data{linetotallastcost} =
1459 "<th class=listsubtotal align=right>"
1460 . $form->format_amount(\%myconfig, $$subtotallastcost, 2)
1463 $$subtotalonhand = 0;
1464 $$subtotalsellprice = 0;
1465 $$subtotallistprice = 0;
1466 $$subtotallastcost = 0;
1468 print "<tr class=listsubtotal>";
1470 map { print "\n$column_data{$_}" } @{ $column_index };
1476 $lxdebug->leave_sub();
1480 $lxdebug->enter_sub();
1482 $auth->assert('part_service_assembly_details');
1484 # show history button
1485 $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
1486 #/show hhistory button
1487 IC->get_part(\%myconfig, \%$form);
1489 $form->{"original_partnumber"} = $form->{"partnumber"};
1491 my $title = 'Edit ' . ucfirst $form->{item};
1492 $form->{title} = $locale->text($title);
1497 $lxdebug->leave_sub();
1501 $lxdebug->enter_sub();
1503 $auth->assert('part_service_assembly_details');
1505 IC->create_links("IC", \%myconfig, \%$form);
1508 map({ $form->{selectcurrency} .= "<option>$_\n" } $::form->get_all_currencies());
1510 # parts and assemblies have the same links
1511 my $item = $form->{item};
1512 if ($form->{item} eq 'assembly') {
1516 # build the popup menus
1517 $form->{taxaccounts} = "";
1518 foreach my $key (keys %{ $form->{IC_links} }) {
1519 foreach my $ref (@{ $form->{IC_links}{$key} }) {
1521 # if this is a tax field
1522 if ($key =~ /IC_tax/) {
1523 if ($key =~ /\Q$item\E/) {
1524 $form->{taxaccounts} .= "$ref->{accno} ";
1525 $form->{"IC_tax_$ref->{accno}_description"} =
1526 "$ref->{accno}--$ref->{description}";
1529 if ($form->{amount}{ $ref->{accno} }) {
1530 $form->{"IC_tax_$ref->{accno}"} = "checked";
1533 $form->{"IC_tax_$ref->{accno}"} = "checked";
1538 $form->{"select$key"} .=
1539 "<option $ref->{selected}>$ref->{accno}--$ref->{description}\n";
1540 if ($form->{amount}{$key} eq $ref->{accno}) {
1541 $form->{$key} = "$ref->{accno}--$ref->{description}";
1547 chop $form->{taxaccounts};
1549 if (($form->{item} eq "part") || ($form->{item} eq "assembly")) {
1550 $form->{selectIC_income} = $form->{selectIC_sale};
1551 $form->{selectIC_expense} = $form->{selectIC_cogs};
1552 $form->{IC_income} = $form->{IC_sale};
1553 $form->{IC_expense} = $form->{IC_cogs};
1556 delete $form->{IC_links};
1557 delete $form->{amount};
1559 $form->get_partsgroup(\%myconfig, { all => 1 });
1561 $form->{partsgroup} = "$form->{partsgroup}--$form->{partsgroup_id}";
1563 if (@{ $form->{all_partsgroup} }) {
1564 $form->{selectpartsgroup} = qq|<option>\n|;
1565 map { $form->{selectpartsgroup} .= qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n| } @{ $form->{all_partsgroup} };
1568 if ($form->{item} eq 'assembly') {
1570 foreach my $i (1 .. $form->{assembly_rows}) {
1571 if ($form->{"partsgroup_id_$i"}) {
1572 $form->{"partsgroup_$i"} =
1573 qq|$form->{"partsgroup_$i"}--$form->{"partsgroup_id_$i"}|;
1576 $form->get_partsgroup(\%myconfig);
1578 if (@{ $form->{all_partsgroup} }) {
1579 $form->{selectassemblypartsgroup} = qq|<option>\n|;
1582 $form->{selectassemblypartsgroup} .=
1583 qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n|
1584 } @{ $form->{all_partsgroup} };
1587 $lxdebug->leave_sub();
1591 $lxdebug->enter_sub();
1593 $auth->assert('part_service_assembly_details');
1595 $form->{pg_keys} = sub { "$_[0]->{partsgroup}--$_[0]->{id}" };
1596 $form->{description_area} = ($form->{rows} = $form->numtextrows($form->{description}, 40)) > 1;
1597 $form->{notes_rows} = max 4, $form->numtextrows($form->{notes}, 40), $form->numtextrows($form->{formel}, 40);
1599 map { $form->{"is_$_"} = ($form->{item} eq $_) } qw(part service assembly);
1600 map { $form->{$_} =~ s/"/"/g; } qw(unit);
1602 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
1603 'partsgroup' => 'all_partsgroup',
1604 'vendors' => 'ALL_VENDORS',
1605 'warehouses' => { 'key' => 'WAREHOUSES',
1606 'bins' => 'BINS', });
1607 # leerer wert für Lager und Lagerplatz korrekt einstellt
1608 # ID 0 sollte in Ordnung sein, da der Zähler sowieso höher ist
1609 my $no_default_bin_entry = { 'id' => '0', description => '--', 'BINS' => [ { id => '0', description => ''} ] };
1610 push @ { $form->{WAREHOUSES} }, $no_default_bin_entry;
1611 if (my $max = scalar @{ $form->{WAREHOUSES} }) {
1612 my ($default_warehouse_id, $default_bin_id);
1613 if ($form->{action} eq 'add') { # default only for new entries
1614 $default_warehouse_id = $::instance_conf->get_warehouse_id;
1615 $default_bin_id = $::instance_conf->get_bin_id;
1617 $form->{warehouse_id} ||= $default_warehouse_id || $form->{WAREHOUSES}->[$max -1]->{id};
1618 $form->{bin_id} ||= $default_bin_id || $form->{WAREHOUSES}->[$max -1]->{BINS}->[0]->{id};
1621 $form->{LANGUAGES} = SL::DB::Manager::Language->get_all_sorted;
1622 $form->{translations_map} = { map { ($_->{language_id} => $_) } @{ $form->{translations} || [] } };
1624 IC->retrieve_buchungsgruppen(\%myconfig, $form);
1625 @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
1627 if (($form->{partnumber} ne '') && !SL::TransNumber->new(number => $form->{partnumber}, type => $form->{item}, id => $form->{id})->is_unique) {
1628 flash('info', $::locale->text('This partnumber is not unique. You should change it.'));
1631 my $units = AM->retrieve_units(\%myconfig, $form);
1632 $form->{ALL_UNITS} = [ map +{ name => $_ }, sort { $units->{$a}{sortkey} <=> $units->{$b}{sortkey} } keys %$units ];
1634 $form->{defaults} = AM->get_defaults();
1636 $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'IC', 'trans_id' => $form->{id});
1638 my ($null, $partsgroup_id) = split /--/, $form->{partsgroup};
1640 CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}, show_disabled_message => 1, partsgroup_id => $partsgroup_id)
1641 if (scalar @{ $form->{CUSTOM_VARIABLES} });
1643 $::request->layout->use_javascript("${_}.js") for qw(ckeditor/ckeditor ckeditor/adapters/jquery kivi.PriceRule);
1644 $::request->layout->add_javascripts_inline("\$(function(){kivi.PriceRule.load_price_rules_for_part(@{[ $::form->{id} * 1 ]})});") if $::form->{id};
1646 #print $form->parse_html_template('ic/form_header', { ALL_PRICE_FACTORS => $form->{ALL_PRICE_FACTORS},
1647 # ALL_UNITS => $form->{ALL_UNITS},
1648 # BUCHUNGSGRUPPEN => $form->{BUCHUNGSGRUPPEN},
1649 # payment_terms => $form->{payment_terms},
1650 # all_partsgroup => $form->{all_partsgroup}});
1652 $form->{show_edit_buttons} = $main::auth->check_right($::myconfig{login}, 'part_service_assembly_edit');
1654 print $form->parse_html_template('ic/form_header');
1655 $lxdebug->leave_sub();
1659 $lxdebug->enter_sub();
1661 $auth->assert('part_service_assembly_details');
1663 print $form->parse_html_template('ic/form_footer');
1665 $lxdebug->leave_sub();
1669 $lxdebug->enter_sub();
1672 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;
1673 delete @{$form}{grep { m/^make_\d+/ || m/^model_\d+/ } keys %{ $form }};
1674 print $form->parse_html_template('ic/makemodel', { MM_DATA => [ @mm_data, {} ], mm_rows => scalar @mm_data + 1 });
1676 $lxdebug->leave_sub();
1680 $lxdebug->enter_sub();
1683 my ($nochange, $callback, $previousform, $linetotal, $line_purchase_price, $href);
1685 @column_index = qw(runningnumber qty unit bom partnumber description partsgroup lastcost total);
1687 if ($form->{previousform}) {
1689 @column_index = qw(qty unit bom partnumber description partsgroup total);
1693 $form->{old_callback} = $form->{callback};
1694 $callback = $form->{callback};
1695 $form->{callback} = "$form->{script}?action=display_form";
1698 map { delete $form->{$_} } qw(action header);
1700 # save form variables in a previousform variable
1701 my %form_to_save = map { ($_ => m/^ (?: listprice | sellprice | lastcost ) $/x ? $form->format_amount(\%myconfig, $form->{$_}) : $form->{$_}) }
1703 $previousform = $::auth->save_form_in_session(form => \%form_to_save);
1705 $form->{callback} = $callback;
1706 $form->{assemblytotal} = 0;
1707 $form->{assembly_purchase_price_total} = 0;
1708 $form->{weight} = 0;
1712 runningnumber => { text => $locale->text('No.'), nowrap => 1, width => '5%', align => 'left',},
1713 qty => { text => $locale->text('Qty'), nowrap => 1, width => '10%', align => 'left',},
1714 unit => { text => $locale->text('Unit'), nowrap => 1, width => '5%', align => 'left',},
1715 partnumber => { text => $locale->text('Part Number'), nowrap => 1, width => '20%', align => 'left',},
1716 description => { text => $locale->text('Part Description'), nowrap => 1, width => '50%', align => 'left',},
1717 lastcost => { text => $locale->text('Purchase Prices'), nowrap => 1, width => '50%', align => 'right',},
1718 total => { text => $locale->text('Sale Prices'), nowrap => 1, align => 'right',},
1719 bom => { text => $locale->text('BOM'), align => 'center',},
1720 partsgroup => { text => $locale->text('Group'), align => 'left',},
1725 for my $i (1 .. $numrows) {
1726 my (%row, @row_hiddens);
1728 $form->{"partnumber_$i"} =~ s/\"/"/g;
1730 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
1731 $line_purchase_price = $form->round_amount($form->{"lastcost_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
1732 $form->{assemblytotal} += $linetotal;
1733 $form->{assembly_purchase_price_total} += $line_purchase_price;
1734 $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
1735 $linetotal = $form->format_amount(\%myconfig, $linetotal, 2);
1736 $line_purchase_price = $form->format_amount(\%myconfig, $line_purchase_price, 2);
1737 $href = build_std_url("action=edit", qq|id=$form->{"id_$i"}|, "rowcount=$numrows", "currow=$i", "previousform=$previousform");
1738 map { $row{$_}{data} = "" } qw(qty unit partnumber description bom partsgroup runningnumber);
1741 if (($i >= 1) && ($i == $numrows)) {
1742 if (!$form->{previousform}) {
1743 $row{partnumber}{data} = qq|<input name="partnumber_$i" size=15 value="$form->{"partnumber_$i"}">|;
1744 $row{qty}{data} = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1745 $row{description}{data} = qq|<input name="description_$i" size=40 value="$form->{"description_$i"}">|;
1746 $row{partsgroup}{data} = qq|<input name="partsgroup_$i" size=10 value="$form->{"partsgroup_$i"}">|;
1750 if ($form->{previousform}) {
1751 push @row_hiddens, qw(qty bom);
1752 $row{partnumber}{data} = $form->{"partnumber_$i"};
1753 $row{qty}{data} = $form->{"qty_$i"};
1754 $row{bom}{data} = $form->{"bom_$i"} ? "x" : " ";
1755 $row{qty}{align} = 'right';
1757 $row{partnumber}{data} = qq|$form->{"partnumber_$i"}|;
1758 $row{partnumber}{link} = $href;
1759 $row{qty}{data} = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1760 $row{runningnumber}{data} = qq|<input name="runningnumber_$i" size=3 value="$i">|;
1761 $row{bom}{data} = sprintf qq|<input name="bom_$i" type=checkbox class=checkbox value=1 %s>|,
1762 $form->{"bom_$i"} ? 'checked' : '';
1764 push @row_hiddens, qw(unit description partnumber partsgroup);
1765 $row{unit}{data} = $form->{"unit_$i"};
1766 #Bei der Artikelbeschreibung und Warengruppe können Sonderzeichen verwendet
1767 #werden, die den HTML Code stören. Daher sollen diese im Template escaped werden
1768 #dies geschieht, wenn die Variable escape gesetzt ist
1769 $row{description}{data} = $form->{"description_$i"};
1770 $row{description}{escape} = 1;
1771 $row{partsgroup}{data} = $form->{"partsgroup_$i"};
1772 $row{partsgroup}{escape} = 1;
1773 $row{bom}{align} = 'center';
1776 $row{lastcost}{data} = $line_purchase_price;
1777 $row{total}{data} = $linetotal;
1778 $row{lastcost}{align} = 'right';
1779 $row{total}{align} = 'right';
1780 $row{deliverydate}{align} = 'right';
1782 push @row_hiddens, qw(id sellprice lastcost weight price_factor_id price_factor);
1783 $row{hiddens} = [ map +{ name => "${_}_$i", value => $form->{"${_}_$i"} }, @row_hiddens ];
1788 print $form->parse_html_template('ic/assembly_row', { COLUMNS => \@column_index, ROWS => \@ROWS, HEADER => \%header });
1790 $lxdebug->leave_sub();
1794 $lxdebug->enter_sub();
1796 $auth->assert('part_service_assembly_edit');
1798 # update checks whether pricegroups, makemodels or assembly items have been changed/added
1799 # new items might have been added (and the original form might have been stored and restored)
1800 # so at the end the ic form is run through check_form in io.pl
1801 # The various combination of events can lead to problems with the order of parse_amount and format_amount
1802 # Currently check_form parses some variables in assembly mode, but not in article or service mode
1803 # This will only ever really be sanely resolved with a rewrite...
1805 # parse pricegroups. and no, don't rely on check_form for this...
1806 map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
1808 unless ($form->{item} eq 'assembly') {
1809 # for assemblies check_form will parse sellprice and listprice, but not for parts or services
1810 $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) for qw(sellprice listprice ve gv);
1813 if ($form->{item} eq 'part') {
1814 $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) for qw(weight rop);
1817 # same for makemodel lastcosts
1818 # but parse_amount not necessary for assembly component lastcosts
1819 unless ($form->{item} eq "assembly") {
1820 map { $form->{"lastcost_$_"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$_"}) } 1 .. $form->{"makemodel_rows"};
1821 $form->{lastcost} = $form->parse_amount(\%myconfig, $form->{lastcost});
1824 if ($form->{item} eq "assembly") {
1825 my $i = $form->{assembly_rows};
1827 # if last row is empty check the form otherwise retrieve item
1828 if ( ($form->{"partnumber_$i"} eq "")
1829 && ($form->{"description_$i"} eq "")
1830 && ($form->{"partsgroup_$i"} eq "")) {
1831 # no new assembly item was added
1836 # search db for newly added assemblyitems, via partnumber or description
1837 IC->assembly_item(\%myconfig, \%$form);
1839 # form->{item_list} contains the possible matches, next check whether the
1840 # match is unique or we need to call the page to select the item
1841 my $rows = scalar @{ $form->{item_list} };
1844 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1847 $form->{makemodel_rows}--;
1848 select_item(mode => 'IC', pre_entered_qty => $form->parse_amount(\%myconfig, $form->{"qty_$i"}));
1851 map { $form->{item_list}[$i]{$_} =~ s/\"/"/g }
1852 qw(partnumber description unit partsgroup);
1853 map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} }
1854 keys %{ $form->{item_list}[0] };
1855 $form->{"runningnumber_$i"} = $form->{assembly_rows};
1856 $form->{assembly_rows}++;
1864 $form->{rowcount} = $i;
1865 $form->{assembly_rows}++;
1872 } elsif (($form->{item} eq 'part') || ($form->{item} eq 'service')) {
1876 $lxdebug->leave_sub();
1880 $lxdebug->enter_sub();
1882 $auth->assert('part_service_assembly_edit');
1883 $::form->mtime_ischanged('parts');
1884 my ($parts_id, %newform, $amount, $callback);
1886 # check if there is a part number - commented out, cause there is an automatic allocation of numbers
1887 # $form->isblank("partnumber", $locale->text(ucfirst $form->{item}." Part Number missing!"));
1889 # check if there is a description
1890 $form->isblank("description", $locale->text("Part Description missing!"));
1892 $form->error($locale->text("Inventory quantity must be zero before you can set this $form->{item} obsolete!"))
1893 if $form->{obsolete} && $form->{onhand} * 1 && $form->{item} ne 'service';
1895 if (!$form->{buchungsgruppen_id}) {
1896 $form->error($locale->text("Parts must have an entry type.") . " " .
1897 $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.")
1901 $form->error($locale->text('Description must not be empty!')) unless $form->{description};
1902 $form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
1904 # undef warehouse_id if the empty value is selected
1905 if ( ($form->{warehouse_id} == 0) && ($form->{bin_id} == 0) ) {
1906 undef $form->{warehouse_id};
1907 undef $form->{bin_id};
1910 if (IC->save(\%myconfig, \%$form) == 3) {
1911 $form->error($locale->text('Partnumber not unique!'));
1913 # saving the history
1914 if(!exists $form->{addition}) {
1915 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1916 $form->{what_done} = "part";
1917 $form->{addition} = "SAVED";
1918 $form->save_history;
1920 # /saving the history
1921 $parts_id = $form->{id};
1924 # load previous variables
1925 if ($form->{previousform}) {
1927 # save the new form variables before splitting previousform
1928 map { $newform{$_} = $form->{$_} } keys %$form;
1930 # don't trample on previous variables
1931 map { delete $form->{$_} } keys %newform;
1933 my $ic_cvar_configs = CVar->get_configs(module => 'IC');
1934 my @ic_cvar_fields = map { "cvar_$_->{name}" } @{ $ic_cvar_configs };
1936 # restore original values
1937 $::auth->restore_form_from_session($newform{previousform}, form => $form);
1938 $form->{taxaccounts} = $newform{taxaccount2};
1940 if ($form->{item} eq 'assembly') {
1942 # undo number formatting
1943 map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
1944 qw(weight listprice sellprice rop);
1946 $form->{assembly_rows}--;
1947 if ($newform{currow}) {
1948 $i = $newform{currow};
1950 $i = $form->{assembly_rows};
1952 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"} > 0);
1954 $form->{sellprice} -= $form->{"sellprice_$i"} * $form->{"qty_$i"};
1955 $form->{weight} -= $form->{"weight_$i"} * $form->{"qty_$i"};
1957 # change/add values for assembly item
1958 map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit weight listprice sellprice inventory_accno income_accno expense_accno price_factor_id);
1959 map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1961 # das ist __voll__ bekloppt, dass so auszurechnen jb 22.5.09
1962 #$form->{sellprice} += $form->{"sellprice_$i"} * $form->{"qty_$i"};
1963 $form->{weight} += $form->{"weight_$i"} * $form->{"qty_$i"};
1967 # set values for last invoice/order item
1968 $i = $form->{rowcount};
1969 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"} > 0);
1971 map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit listprice inventory_accno income_accno expense_accno sellprice lastcost price_factor_id);
1972 map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1974 $form->{"longdescription_$i"} = $newform{notes};
1976 $form->{"sellprice_$i"} = $newform{lastcost} if ($form->{vendor_id});
1978 if ($form->{exchangerate} != 0) {
1979 $form->{"sellprice_$i"} /= $form->{exchangerate};
1982 map { $form->{"taxaccounts_$i"} .= "$_ " } split / /, $newform{taxaccount};
1983 chop $form->{"taxaccounts_$i"};
1984 foreach my $item (qw(description rate taxnumber)) {
1985 my $index = $form->{"taxaccounts_$i"} . "_$item";
1986 $form->{$index} = $newform{$index};
1989 # credit remaining calculation
1990 $amount = $form->{"sellprice_$i"} * (1 - $form->{"discount_$i"} / 100) * $form->{"qty_$i"};
1992 map { $form->{"${_}_base"} += $amount } (split / /, $form->{"taxaccounts_$i"});
1993 map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{"taxaccounts_$i"} if !$form->{taxincluded};
1995 $form->{creditremaining} -= $amount;
1997 # redo number formatting, because invoice parse them!
1998 map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}) } qw(weight listprice sellprice lastcost rop);
2001 $form->{"id_$i"} = $parts_id;
2003 # Get the actual price factor (not just the ID) for the marge calculation.
2004 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
2005 foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
2006 next if ($pfac->{id} != $newform{price_factor_id});
2007 $form->{"marge_price_factor_$i"} = $pfac->{factor};
2010 delete $form->{ALL_PRICE_FACTORS};
2012 delete $form->{action};
2014 # restore original callback
2015 $callback = $form->unescape($form->{callback});
2016 $form->{callback} = $form->unescape($form->{old_callback});
2017 delete $form->{old_callback};
2019 $form->{makemodel_rows}--;
2021 # put callback together
2022 foreach my $key (keys %$form) {
2024 # do single escape for Apache 2.0
2025 my $value = $form->escape($form->{$key}, 1);
2026 $callback .= qq|&$key=$value|;
2028 $form->{callback} = $callback;
2034 $lxdebug->leave_sub();
2038 $lxdebug->enter_sub();
2040 $auth->assert('part_service_assembly_edit');
2042 # saving the history
2043 if(!exists $form->{addition}) {
2044 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
2045 $form->{addition} = "SAVED AS NEW";
2046 $form->{what_done} = "part";
2047 $form->save_history;
2049 # /saving the history
2051 if ($form->{"original_partnumber"} &&
2052 ($form->{"partnumber"} eq $form->{"original_partnumber"})) {
2053 $form->{partnumber} = "";
2056 $lxdebug->leave_sub();
2060 $lxdebug->enter_sub();
2062 $auth->assert('part_service_assembly_edit');
2064 # saving the history
2065 if(!exists $form->{addition}) {
2066 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
2067 $form->{addition} = "DELETED";
2068 $form->{what_done} = "part";
2069 $form->save_history;
2071 # /saving the history
2072 my $rc = IC->delete(\%myconfig, \%$form);
2075 $form->redirect($locale->text('Item deleted!')) if ($rc > 0);
2076 $form->error($locale->text('Cannot delete item!'));
2078 $lxdebug->leave_sub();
2082 $lxdebug->enter_sub();
2084 $auth->assert('part_service_assembly_details');
2089 pricegroup => $form->{"pricegroup_$_"},
2090 pricegroup_id => $form->{"pricegroup_id_$_"},
2091 price => $form->{"price_$_"},
2094 print $form->parse_html_template('ic/price_row', { PRICES => \@PRICES });
2096 $lxdebug->leave_sub();
2099 sub ajax_autocomplete {
2100 $main::lxdebug->enter_sub();
2102 my $form = $main::form;
2103 my %myconfig = %main::myconfig;
2105 $form->{column} = 'description' unless $form->{column} =~ /^partnumber|description$/;
2106 $form->{$form->{column}} = $form->{q} || '';
2107 $form->{limit} = ($form->{limit} * 1) || 10;
2108 $form->{searchitems} ||= '';
2110 my @results = IC->all_parts(\%myconfig, $form);
2112 print $form->ajax_response_header(),
2113 $form->parse_html_template('ic/ajax_autocomplete');
2115 $main::lxdebug->leave_sub();
2119 $::lxdebug->enter_sub;
2121 $auth->assert('part_service_assembly_edit');
2125 $::form->language_payment(\%::myconfig);
2127 Common::webdav_folder($::form);
2130 price_row($::form->{price_rows});
2131 makemodel_row(++$::form->{makemodel_rows}) if $::form->{item} =~ /^(part|service)$/;
2132 assembly_row(++$::form->{assembly_rows}) if $::form->{item} eq 'assembly';
2136 $::lxdebug->leave_sub;
2139 sub back_to_record {
2143 delete @{$::form}{qw(action action_add action_back_to_record back_sub description item notes partnumber sellprice taxaccount2 unit vc)};
2145 $::auth->restore_form_from_session($::form->{previousform}, clobber => 1);
2146 $::form->{rowcount}--;
2147 $::form->{action} = 'display_form';
2148 $::form->{callback} = $::form->{script} . '?' . join('&', map { $::form->escape($_) . '=' . $::form->escape($::form->{$_}) } sort keys %{ $::form });
2152 sub continue { call_sub($form->{"nextsub"}); }
2155 my $action = first { $::form->{"action_${_}"} } qw(add back_to_record);
2156 $::form->error($::locale->text('No action defined.')) unless $action;
2158 $::form->{dispatched_action} = $action;