Bugfix for Bug 1468: JavaScript-Title_Hack lieferte falsche oder sinnlose Titel und...
[kivitendo-erp.git] / bin / mozilla / ic.pl
1 #=====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #=====================================================================
8 # SQL-Ledger, Accounting
9 # Copyright (c) 2001
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 #
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.
20 #
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 #======================================================================
29 #
30 # Inventory Control module
31 #
32 #======================================================================
33
34 use POSIX qw(strftime);
35 use List::Util qw(max);
36 use List::MoreUtils qw(any);
37
38 use SL::AM;
39 use SL::CVar;
40 use SL::IC;
41 use SL::ReportGenerator;
42
43 #use SL::PE;
44
45 use strict;
46 #use warnings;
47
48 # global imports
49 our ($form, $locale, %myconfig, $lxdebug, $auth);
50
51 require "bin/mozilla/io.pl";
52 require "bin/mozilla/invoice_io.pl";
53 require "bin/mozilla/common.pl";
54 require "bin/mozilla/reportgenerator.pl";
55
56 1;
57
58 # Parserhappy(R):
59 # type=submit $locale->text('Add Part')
60 # type=submit $locale->text('Add Service')
61 # type=submit $locale->text('Add Assembly')
62 # type=submit $locale->text('Edit Part')
63 # type=submit $locale->text('Edit Service')
64 # type=submit $locale->text('Edit Assembly')
65 # $locale->text('Parts')
66 # $locale->text('Services')
67 # $locale->text('Inventory quantity must be zero before you can set this part obsolete!')
68 # $locale->text('Inventory quantity must be zero before you can set this assembly obsolete!')
69 # $locale->text('Part Number missing!')
70 # $locale->text('Service Number missing!')
71 # $locale->text('Assembly Number missing!')
72 # $locale->text('ea');
73
74 # end of main
75
76 sub add {
77   $lxdebug->enter_sub();
78
79   $auth->assert('part_service_assembly_edit');
80
81   $form->{title}           = $locale->text('Add' . ucfirst $form->{item});
82   $form->{callback}        = "$form->{script}?action=add&item=$form->{item}" unless $form->{callback};
83   $form->{unit_changeable} = 1;
84
85   IC->get_pricegroups(\%myconfig, \%$form);
86   &link_part;
87   &display_form;
88
89   $lxdebug->leave_sub();
90 }
91
92 sub search {
93   $lxdebug->enter_sub();
94
95   $auth->assert('part_service_assembly_edit');
96
97   $form->{revers}       = 0;  # switch for backward sorting
98   $form->{lastsort}     = ""; # memory for which table was sort at last time
99   $form->{ndxs_counter} = 0;  # counter for added entries to top100
100
101   my %is_xyz     = map { +"is_$_" => ($form->{searchitems} eq $_) } qw(part service assembly);
102
103   $form->{title} = (ucfirst $form->{searchitems}) . "s";
104   $form->{title} = $locale->text($form->{title});
105   $form->{title} = $locale->text('Assemblies') if ($is_xyz{is_assembly});
106
107   $form->{jsscript} = 1;
108
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');
114
115   $form->header;
116
117   print $form->parse_html_template('ic/search', { %is_xyz,
118                                                   dateformat => $myconfig{dateformat}, });
119
120   $lxdebug->leave_sub();
121 }    #end search()
122
123 sub search_update_prices {
124   $lxdebug->enter_sub();
125
126   $auth->assert('part_service_assembly_edit');
127
128   my $pricegroups = IC->get_pricegroups(\%myconfig, \%$form);
129
130   $form->{title} = $locale->text('Update Prices');
131
132   $form->header;
133
134   print $form->parse_html_template('ic/search_update_prices', { PRICE_ROWS => $pricegroups });
135
136   $lxdebug->leave_sub();
137 }    #end search()
138
139 sub confirm_price_update {
140   $lxdebug->enter_sub();
141
142   $auth->assert('part_service_assembly_edit');
143
144   my @errors      = ();
145   my $value_found = undef;
146
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});
152
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);
155
156     } elsif (!$value && ($form->{$value_idx} ne '')) {
157       push @errors, $locale->text('No valid number entered for pricegroup "#1".', $name);
158
159     } elsif (0 < $value) {
160       $value_found = 1;
161     }
162   }
163
164   push @errors, $locale->text('No prices will be updated because no prices have been entered.') if (!$value_found);
165
166   my $num_matches = IC->get_num_matches_for_priceupdate();
167
168   $form->header();
169
170   if (@errors) {
171     $form->show_generic_error(join('<br>', @errors), 'back_button' => 1);
172   }
173
174   $form->{nextsub} = "update_prices";
175
176   map { delete $form->{$_} } qw(action header);
177
178   print $form->parse_html_template('ic/confirm_price_update', { HIDDENS     => [ map { name => $_, value => $form->{$_} }, keys %$form ],
179                                                                 num_matches => $num_matches });
180
181   $lxdebug->leave_sub();
182 }
183
184 sub update_prices {
185   $lxdebug->enter_sub();
186
187   $auth->assert('part_service_assembly_edit');
188
189   my $num_updated = IC->update_prices(\%myconfig, \%$form);
190
191   if (-1 != $num_updated) {
192     $form->redirect($locale->text('#1 prices were updated.', $num_updated));
193   } else {
194     $form->error($locale->text('Could not update prices!'));
195   }
196
197   $lxdebug->leave_sub();
198 }
199
200 #sub choice {
201 #  $lxdebug->enter_sub();
202 #
203 #  $auth->assert('part_service_assembly_edit');
204 #
205 #  our ($j, $lastndx);
206 #  my ($totop100);
207 #
208 #  $form->{title} = $locale->text('Top 100 hinzufuegen');
209 #
210 #  $form->header;
211 #
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);
214 #  my @HIDDENS = (
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),
220 #  );
221 #
222 #  my ($partnumber, $description, $unit, $sellprice, $soldtotal);
223 #  # if choice set data
224 ##  if ($form->{ndx}) {
225 ##    for my $i (0 .. $form->{ndxs_counter}) {
226 ##
227 ##      # insert data into top100
228 ##      push @{ $form->{parts} },
229 ##        { number      => "",
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"},
235 ##        };
236 ##    }    #rof
237 ##  }    #fi
238 #
239 #  $totop100 = "";
240 #
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"};
248 #
249 #  push @PARTS, {
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"},
255 #  }
256 #
257 ##    $totop100 .= qq|
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"}>
263 ##    |;
264 #  }    #rof
265 #
266 #  print $form->parse_html_template('ic/choice', +{ HIDDENS => \@HIDDENS, PARTS => \@PARTS });
267 #
268 #  $lxdebug->leave_sub();
269 #}    #end choice
270
271 #sub list {
272 #  $lxdebug->enter_sub();
273 #
274 #  $auth->assert('part_service_assembly_edit');
275 #
276 #  our ($lastndx);
277 #  our ($partnumber, $description, $unit, $sellprice, $soldtotal);
278 #
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);
282 #
283 #  $form->{title} = $locale->text('Top 100 hinzufuegen');
284 #
285 #  $form->header;
286 #
287 #  print qq|
288 #<body>
289 #  <form method=post action=ic.pl>
290 #    <table width=100%>
291 #     <tr>
292 #      <th class=listtop colspan=6>| . $locale->text('choice part') . qq|</th>
293 #     </tr>
294 #        <tr height="5"></tr>
295 #        <tr class=listheading>
296 #          <th>&nbsp;</th>
297 #          <th class=listheading>| . $locale->text('Part Number') . qq|</th>
298 #          <th class=listheading>| . $locale->text('Part Description') . qq|</th>
299 #          <th class=listheading>| . $locale->text('Unit of measure') . qq|</th>
300 #          <th class=listheading>| . $locale->text('Sell Price') . qq|</th>
301 #          <th class=listheading>| . $locale->text('soldtotal') . qq|</th>
302 #        </tr>|;
303 #
304 #  my $j = 0;
305 #  my $i = $form->{rows};
306 #
307 #  for ($j = 1; $j <= $i; $j++) {
308 #
309 #    print qq|
310 #        <tr class=listrow| . ($j % 2) . qq|>|;
311 #    if ($j == 1) {
312 #      print qq|
313 #            <td><input name=ndx class=radio type=radio value=$j checked></td>|;
314 #    } else {
315 #      print qq|
316 #          <td><input name=ndx class=radio type=radio value=$j></td>|;
317 #    }
318 #    print qq|
319 #          <td><input name="new_partnumber_$j" type=hidden value="$form->{"partnumber_$j"}">$form->{"partnumber_$j"}</td>
320 #          <td><input name="new_description_$j" type=hidden value="$form->{"description_$j"}">$form->{"description_$j"}</td>
321 #          <td><input name="new_unit_$j" type=hidden value="$form->{"unit_$j"}">$form->{"unit_$j"}</td>
322 #          <td><input name="new_sellprice_$j" type=hidden value="$form->{"sellprice_$j"}">$form->{"sellprice_$j"}</td>
323 #          <td><input name="new_soldtotal_$j" type=hidden value="$form->{"soldtotal_$j"}">$form->{"soldtotal_$j"}</td>
324 #        </tr>
325 #
326 #        <input name="new_id_$j" type=hidden value="$form->{"id_$j"}">|;
327 #  }
328 #
329 #  print qq|
330 #
331 #</table>
332 #
333 #<br>
334 #
335 #
336 #<input type=hidden name=itemstatus value="$form->{itemstatus}">
337 #<input type=hidden name=l_linetotal value="$form->{l_linetotal}">
338 #<input type=hidden name=l_partnumber value="$form->{l_partnumber}">
339 #<input type=hidden name=l_description value="$form->{l_description}">
340 #<input type=hidden name=l_onhand value="$form->{l_onhand}">
341 #<input type=hidden name=l_unit value="$form->{l_unit}">
342 #<input type=hidden name=l_sellprice value="$form->{l_sellprice}">
343 #<input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
344 #<input type=hidden name=sort value="$form->{sort}">
345 #<input type=hidden name=revers value="$form->{revers}">
346 #<input type=hidden name=lastsort value="$form->{lastsort}">
347 #
348 #<input type=hidden name=bom value="$form->{bom}">
349 #<input type=hidden name=titel value="$form->{titel}">
350 #<input type=hidden name=searchitems value="$form->{searchitems}">
351 #
352 #<input type=hidden name=row value=$j>
353 #
354 #<input type=hidden name=nextsub value=item_selected>
355 #
356 #<input name=lastndx type=hidden value=$lastndx>
357 #
358 #<input name=ndxs_counter type=hidden value=$form->{ndxs_counter}>|;
359 #
360 #  my $totop100 = "";
361 #
362 #  if (($form->{ndxs_counter}) > 0) {
363 #    for ($i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
364 #
365 #      $partnumber  = $form->{"totop100_partnumber_$i"};
366 #      $description = $form->{"totop100_description_$i"};
367 #      $unit        = $form->{"totop100_unit_$i"};
368 #      $sellprice   = $form->{"totop100_sellprice_$i"};
369 #      $soldtotal   = $form->{"totop100_soldtotal_$i"};
370 #
371 #      $totop100 .= qq|
372 #<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
373 #<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
374 #<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
375 #<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
376 #<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
377 #      |;
378 #    }    #rof
379 #  }    #fi
380 #
381 #  print $totop100;
382 #
383 #  print qq|
384 #<input class=submit type=submit name=action value="|
385 #    . $locale->text('TOP100') . qq|">
386 #
387 #</form>
388 #</body>
389 #</html>
390 #|;
391 #  $lxdebug->leave_sub();
392 #}    #end list()
393
394 sub top100 {
395   $lxdebug->enter_sub();
396
397   $auth->assert('part_service_assembly_edit');
398
399   if ($form->{ndx}) {
400     $form->{ndxs_counter}++;
401
402     if ($form->{ndxs_counter} > 0) {
403
404       my $index = $form->{ndx};
405
406       $form->{"totop100_partnumber_$form->{ndxs_counter}"} = $form->{"new_partnumber_$index"};
407       $form->{"totop100_description_$form->{ndxs_counter}"} = $form->{"new_description_$index"};
408       $form->{"totop100_unit_$form->{ndxs_counter}"} = $form->{"new_unit_$index"};
409       $form->{"totop100_sellprice_$form->{ndxs_counter}"} = $form->{"new_sellprice_$index"};
410       $form->{"totop100_soldtotal_$form->{ndxs_counter}"} = $form->{"new_soldtotal_$index"};
411     }    #fi
412   }    #fi
413   &addtop100();
414   $lxdebug->leave_sub();
415 }    #end top100
416
417 sub addtop100 {
418   $lxdebug->enter_sub();
419
420   $auth->assert('part_service_assembly_edit');
421
422   my ($revers, $lastsort, $callback, $option, $description, $sameitem,
423       $partnumber, $unit, $sellprice, $soldtotal, $totop100, $onhand, $align);
424   my (@column_index, %column_header, %column_data);
425   my ($totalsellprice, $totallastcost, $totallistprice, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice);
426
427   $form->{top100}      = "top100";
428   $form->{l_soldtotal} = "Y";
429   $form->{soldtotal}   = "soldtotal";
430   $form->{sort}        = "soldtotal";
431   $form->{l_qty}       = "N";
432   $form->{l_linetotal} = "";
433   $form->{revers}      = 1;
434   $form->{number}      = "position";
435   $form->{l_number}    = "Y";
436
437   $totop100 = "";
438
439   $form->{title} = $locale->text('Top 100');
440
441   $revers   = $form->{revers};
442   $lastsort = $form->{lastsort};
443
444   if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
445     $form->{revers}   = 0;
446     $form->{lastsort} = "partnumber";
447     $form->{sort}     = "partnumber";
448   }    #fi
449
450   $callback =
451     "$form->{script}?action=top100&searchitems=$form->{searchitems}&itemstatus=$form->{itemstatus}&bom=$form->{bom}&l_linetotal=$form->{l_linetotal}&title="
452     . $form->escape($form->{title}, 1);
453
454   # if we have a serialnumber limit search
455   if ($form->{serialnumber} || $form->{l_serialnumber}) {
456     $form->{l_serialnumber} = "Y";
457     unless (   $form->{bought}
458             || $form->{sold}
459             || $form->{rfq}
460             || $form->{quoted}) {
461       $form->{bought} = $form->{sold} = 1;
462     }
463   }
464   IC->all_parts(\%myconfig, \%$form);
465
466   if ($form->{itemstatus} eq 'active') {
467     $option .= $locale->text('Active') . " : ";
468   }
469   if ($form->{itemstatus} eq 'obsolete') {
470     $option .= $locale->text('Obsolete') . " : ";
471   }
472   if ($form->{itemstatus} eq 'orphaned') {
473     $option .= $locale->text('Orphaned') . " : ";
474   }
475   if ($form->{itemstatus} eq 'onhand') {
476     $option .= $locale->text('On Hand') . " : ";
477     $form->{l_onhand} = "Y";
478   }
479   if ($form->{itemstatus} eq 'short') {
480     $option .= $locale->text('Short') . " : ";
481     $form->{l_onhand} = "Y";
482   }
483   if ($form->{onorder}) {
484     $form->{l_ordnumber} = "Y";
485     $callback .= "&onorder=$form->{onorder}";
486     $option   .= $locale->text('On Order') . " : ";
487   }
488   if ($form->{ordered}) {
489     $form->{l_ordnumber} = "Y";
490     $callback .= "&ordered=$form->{ordered}";
491     $option   .= $locale->text('Ordered') . " : ";
492   }
493   if ($form->{rfq}) {
494     $form->{l_quonumber} = "Y";
495     $callback .= "&rfq=$form->{rfq}";
496     $option   .= $locale->text('RFQ') . " : ";
497   }
498   if ($form->{quoted}) {
499     $form->{l_quonumber} = "Y";
500     $callback .= "&quoted=$form->{quoted}";
501     $option   .= $locale->text('Quoted') . " : ";
502   }
503   if ($form->{bought}) {
504     $form->{l_invnumber} = "Y";
505     $callback .= "&bought=$form->{bought}";
506     $option   .= $locale->text('Bought') . " : ";
507   }
508   if ($form->{sold}) {
509     $form->{l_invnumber} = "Y";
510     $callback .= "&sold=$form->{sold}";
511     $option   .= $locale->text('Sold') . " : ";
512   }
513   if (   $form->{bought}
514       || $form->{sold}
515       || $form->{onorder}
516       || $form->{ordered}
517       || $form->{rfq}
518       || $form->{quoted}) {
519
520     $form->{l_lastcost} = "";
521     $form->{l_name}     = "Y";
522     if ($form->{transdatefrom}) {
523       $callback .= "&transdatefrom=$form->{transdatefrom}";
524       $option   .= "\n<br>"
525         . $locale->text('From')
526         . "&nbsp;"
527         . $locale->date(\%myconfig, $form->{transdatefrom}, 1);
528     }
529     if ($form->{transdateto}) {
530       $callback .= "&transdateto=$form->{transdateto}";
531       $option   .= "\n<br>"
532         . $locale->text('To')
533         . "&nbsp;"
534         . $locale->date(\%myconfig, $form->{transdateto}, 1);
535     }
536   }
537
538   $option .= "<br>";
539
540   if ($form->{partnumber}) {
541     $callback .= "&partnumber=$form->{partnumber}";
542     $option   .= $locale->text('Part Number') . qq| : $form->{partnumber}<br>|;
543   }
544   if ($form->{ean}) {
545     $callback .= "&partnumber=$form->{ean}";
546     $option   .= $locale->text('EAN') . qq| : $form->{ean}<br>|;
547   }
548   if ($form->{partsgroup}) {
549     $callback .= "&partsgroup=$form->{partsgroup}";
550     $option   .= $locale->text('Group') . qq| : $form->{partsgroup}<br>|;
551   }
552   if ($form->{serialnumber}) {
553     $callback .= "&serialnumber=$form->{serialnumber}";
554     $option   .= $locale->text('Serial Number') . qq| : $form->{serialnumber}<br>|;
555   }
556   if ($form->{description}) {
557     $callback   .= "&description=$form->{description}";
558     $description = $form->{description};
559     $description =~ s/\n/<br>/g;
560     $option     .= $locale->text('Part Description') . qq| : $form->{description}<br>|;
561   }
562   if ($form->{make}) {
563     $callback .= "&make=$form->{make}";
564     $option   .= $locale->text('Make') . qq| : $form->{make}<br>|;
565   }
566   if ($form->{model}) {
567     $callback .= "&model=$form->{model}";
568     $option   .= $locale->text('Model') . qq| : $form->{model}<br>|;
569   }
570   if ($form->{drawing}) {
571     $callback .= "&drawing=$form->{drawing}";
572     $option   .= $locale->text('Drawing') . qq| : $form->{drawing}<br>|;
573   }
574   if ($form->{microfiche}) {
575     $callback .= "&microfiche=$form->{microfiche}";
576     $option   .= $locale->text('Microfiche') . qq| : $form->{microfiche}<br>|;
577   }
578   if ($form->{l_soldtotal}) {
579     $callback .= "&soldtotal=$form->{soldtotal}";
580     $option   .= $locale->text('soldtotal') . qq| : $form->{soldtotal}<br>|;
581   }
582
583   my @columns = $form->sort_columns(
584     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)
585   );
586
587   if ($form->{l_linetotal}) {
588     $form->{l_onhand} = "Y";
589     $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
590     if ($form->{l_lastcost}) {
591       $form->{l_linetotallastcost} = "Y";
592       if (($form->{searchitems} eq 'assembly') && !$form->{bom}) {
593         $form->{l_linetotallastcost} = "";
594       }
595     }
596     $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
597   }
598
599   if ($form->{searchitems} eq 'service') {
600
601     # remove bin, weight and rop from list
602     map { $form->{"l_$_"} = "" } qw(bin weight rop);
603
604     $form->{l_onhand} = "";
605
606     # qty is irrelevant unless bought or sold
607     if (   $form->{bought}
608         || $form->{sold}
609         || $form->{onorder}
610         || $form->{ordered}
611         || $form->{rfq}
612         || $form->{quoted}) {
613       $form->{l_onhand} = "Y";
614     } else {
615       $form->{l_linetotalsellprice} = "";
616       $form->{l_linetotallastcost}  = "";
617     }
618   }
619
620   foreach my $item (@columns) {
621     if ($form->{"l_$item"} eq "Y") {
622       push @column_index, $item;
623
624       # add column to callback
625       $callback .= "&l_$item=Y";
626     }
627   }
628
629   if ($form->{l_subtotal} eq 'Y') {
630     $callback .= "&l_subtotal=Y";
631   }
632
633   $column_header{number} =
634     qq|<th class=listheading nowrap>| . $locale->text('number') . qq|</th>|;
635   $column_header{partnumber} =
636     qq|<th nowrap><a class=listheading href=$callback&sort=partnumber&revers=$form->{revers}&lastsort=$form->{lastsort}>|
637     . $locale->text('Part Number')
638     . qq|</a></th>|;
639   $column_header{description} =
640     qq|<th nowrap><a class=listheading href=$callback&sort=description&revers=$form->{revers}&lastsort=$form->{lastsort}>|
641     . $locale->text('Part Description')
642     . qq|</a></th>|;
643   $column_header{partsgroup} =
644       qq|<th nowrap><a class=listheading href=$callback&sort=partsgroup>|
645     . $locale->text('Group')
646     . qq|</a></th>|;
647   $column_header{bin} =
648       qq|<th><a class=listheading href=$callback&sort=bin>|
649     . $locale->text('Bin')
650     . qq|</a></th>|;
651   $column_header{priceupdate} =
652       qq|<th nowrap><a class=listheading href=$callback&sort=priceupdate>|
653     . $locale->text('Updated')
654     . qq|</a></th>|;
655   $column_header{onhand} =
656     qq|<th nowrap><a  class=listheading href=$callback&sort=onhand&revers=$form->{revers}&lastsort=$form->{lastsort}>|
657     . $locale->text('Qty')
658     . qq|</th>|;
659   $column_header{unit} =
660     qq|<th class=listheading nowrap>| . $locale->text('Unit') . qq|</th>|;
661   $column_header{listprice} =
662       qq|<th class=listheading nowrap>|
663     . $locale->text('List Price')
664     . qq|</th>|;
665   $column_header{lastcost} =
666     qq|<th class=listheading nowrap>| . $locale->text('Last Cost') . qq|</th>|;
667   $column_header{rop} =
668     qq|<th class=listheading nowrap>| . $locale->text('ROP') . qq|</th>|;
669   $column_header{weight} =
670     qq|<th class=listheading nowrap>| . $locale->text('Weight') . qq|</th>|;
671
672   $column_header{invnumber} =
673       qq|<th nowrap><a class=listheading href=$callback&sort=invnumber>|
674     . $locale->text('Invoice Number')
675     . qq|</a></th>|;
676   $column_header{ordnumber} =
677       qq|<th nowrap><a class=listheading href=$callback&sort=ordnumber>|
678     . $locale->text('Order Number')
679     . qq|</a></th>|;
680   $column_header{quonumber} =
681       qq|<th nowrap><a class=listheading href=$callback&sort=quonumber>|
682     . $locale->text('Quotation')
683     . qq|</a></th>|;
684
685   $column_header{name} =
686       qq|<th nowrap><a class=listheading href=$callback&sort=name>|
687     . $locale->text('Name')
688     . qq|</a></th>|;
689
690   $column_header{sellprice} =
691       qq|<th class=listheading nowrap>|
692     . $locale->text('Sell Price')
693     . qq|</th>|;
694   $column_header{linetotalsellprice} =
695     qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
696   $column_header{linetotallastcost} =
697     qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
698   $column_header{linetotallistprice} =
699     qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
700
701   $column_header{image} =
702     qq|<th class=listheading nowrap>| . $locale->text('Image') . qq|</a></th>|;
703   $column_header{drawing} =
704       qq|<th nowrap><a class=listheading href=$callback&sort=drawing>|
705     . $locale->text('Drawing')
706     . qq|</a></th>|;
707   $column_header{microfiche} =
708       qq|<th nowrap><a class=listheading href=$callback&sort=microfiche>|
709     . $locale->text('Microfiche')
710     . qq|</a></th>|;
711
712   $column_header{serialnumber} =
713       qq|<th nowrap><a class=listheading href=$callback&sort=serialnumber>|
714     . $locale->text('Serial Number')
715     . qq|</a></th>|;
716   $column_header{soldtotal} =
717     qq|<th nowrap><a class=listheading href=$callback&sort=soldtotal&revers=$form->{revers}&lastsort=$form->{lastsort}>|
718     . $locale->text('soldtotal')
719     . qq|</a></th>|;
720
721   $form->header;
722   my $colspan = $#column_index + 1;
723
724   print qq|
725 <body>
726
727 <table width=100%>
728   <tr>
729     <th class=listtop colspan=$colspan>$form->{title}</th>
730   </tr>
731   <tr height="5"></tr>
732
733   <tr><td colspan=$colspan>$option</td></tr>
734
735   <tr class=listheading>
736 |;
737
738   map { print "\n$column_header{$_}" } @column_index;
739
740   print qq|
741   </tr>
742   |;
743
744   # add order to callback
745   $form->{callback} = $callback .= "&sort=$form->{sort}";
746
747   # escape callback for href
748   $callback = $form->escape($callback);
749
750   if (@{ $form->{parts} }) {
751     $sameitem = $form->{parts}->[0]->{ $form->{sort} };
752   }
753
754   # insert numbers for top100
755   my $j = 0;
756   foreach my $ref (@{ $form->{parts} }) {
757     $j++;
758     $ref->{number} = $j;
759   }
760
761   # if avaible -> insert choice here
762   if (($form->{ndxs_counter}) > 0) {
763     for (my $i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
764       $partnumber  = $form->{"totop100_partnumber_$i"};
765       $description = $form->{"totop100_description_$i"};
766       $unit        = $form->{"totop100_unit_$i"};
767       $sellprice   = $form->{"totop100_sellprice_$i"};
768       $soldtotal   = $form->{"totop100_soldtotal_$i"};
769
770       $totop100 .= qq|
771 <input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
772 <input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
773 <input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
774 <input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
775 <input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
776       |;
777
778       # insert into list
779       push @{ $form->{parts} },
780         { number      => "",
781           partnumber  => "$partnumber",
782           description => "$description",
783           unit        => "$unit",
784           sellprice   => "$sellprice",
785           soldtotal   => "$soldtotal" };
786     }    #rof
787   }    #fi
788        # build data for columns
789   my $i = 0;
790   foreach my $ref (@{ $form->{parts} }) {
791
792     if ($form->{l_subtotal} eq 'Y' && !$ref->{assemblyitem}) {
793       if ($sameitem ne $ref->{ $form->{sort} }) {
794         parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
795         $sameitem = $ref->{ $form->{sort} };
796       }
797     }
798
799     $ref->{exchangerate} = 1 unless $ref->{exchangerate};
800     $ref->{sellprice} *= $ref->{exchangerate};
801     $ref->{listprice} *= $ref->{exchangerate};
802     $ref->{lastcost}  *= $ref->{exchangerate};
803
804     # use this for assemblies
805     $onhand = $ref->{onhand};
806
807     $align = "left";
808     if ($ref->{assemblyitem}) {
809       $align = "right";
810       $onhand = 0 if ($form->{sold});
811     }
812
813     $ref->{description} =~ s/\n/<br>/g;
814
815     $column_data{number} =
816         "<td align=right>"
817       . $form->format_amount(\%myconfig, $ref->{number})
818       . "</td>";
819     $column_data{partnumber} =
820       "<td align=$align>$ref->{partnumber}&nbsp;</a></td>";
821     $column_data{description} = "<td>$ref->{description}&nbsp;</td>";
822     $column_data{partsgroup}  = "<td>$ref->{partsgroup}&nbsp;</td>";
823
824     $column_data{onhand} =
825         "<td align=right>"
826       . $form->format_amount(\%myconfig, $ref->{onhand})
827       . "</td>";
828     $column_data{sellprice} =
829         "<td align=right>"
830       . $form->format_amount(\%myconfig, $ref->{sellprice})
831       . "</td>";
832     $column_data{listprice} =
833         "<td align=right>"
834       . $form->format_amount(\%myconfig, $ref->{listprice})
835       . "</td>";
836     $column_data{lastcost} =
837         "<td align=right>"
838       . $form->format_amount(\%myconfig, $ref->{lastcost})
839       . "</td>";
840
841     $column_data{linetotalsellprice} = "<td align=right>"
842       . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{sellprice}, 2)
843       . "</td>";
844     $column_data{linetotallastcost} = "<td align=right>"
845       . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{lastcost}, 2)
846       . "</td>";
847     $column_data{linetotallistprice} = "<td align=right>"
848       . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{listprice}, 2)
849       . "</td>";
850
851     if (!$ref->{assemblyitem}) {
852       $totalsellprice += $onhand * $ref->{sellprice};
853       $totallastcost  += $onhand * $ref->{lastcost};
854       $totallistprice += $onhand * $ref->{listprice};
855
856       $subtotalonhand    += $onhand;
857       $subtotalsellprice += $onhand * $ref->{sellprice};
858       $subtotallastcost  += $onhand * $ref->{lastcost};
859       $subtotallistprice += $onhand * $ref->{listprice};
860     }
861
862     $column_data{rop} =
863       "<td align=right>"
864       . $form->format_amount(\%myconfig, $ref->{rop}) . "</td>";
865     $column_data{weight} =
866         "<td align=right>"
867       . $form->format_amount(\%myconfig, $ref->{weight})
868       . "</td>";
869     $column_data{unit}        = "<td>$ref->{unit}&nbsp;</td>";
870     $column_data{bin}         = "<td>$ref->{bin}&nbsp;</td>";
871     $column_data{priceupdate} = "<td>$ref->{priceupdate}&nbsp;</td>";
872
873     $column_data{invnumber} =
874       ($ref->{module} ne 'oe')
875       ? "<td><a href=$ref->{module}.pl?action=edit&type=invoice&id=$ref->{trans_id}&callback=$callback>$ref->{invnumber}</a></td>"
876       : "<td>$ref->{invnumber}</td>";
877     $column_data{ordnumber} =
878       ($ref->{module} eq 'oe')
879       ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{ordnumber}</a></td>"
880       : "<td>$ref->{ordnumber}</td>";
881     $column_data{quonumber} =
882       ($ref->{module} eq 'oe' && !$ref->{ordnumber})
883       ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{quonumber}</a></td>"
884       : "<td>$ref->{quonumber}</td>";
885
886     $column_data{name} = "<td>$ref->{name}</td>";
887
888     $column_data{image} =
889       ($ref->{image})
890       ? "<td><a href=$ref->{image}><img src=$ref->{image} height=32 border=0></a></td>"
891       : "<td>&nbsp;</td>";
892     $column_data{drawing} =
893       ($ref->{drawing})
894       ? "<td><a href=$ref->{drawing}>$ref->{drawing}</a></td>"
895       : "<td>&nbsp;</td>";
896     $column_data{microfiche} =
897       ($ref->{microfiche})
898       ? "<td><a href=$ref->{microfiche}>$ref->{microfiche}</a></td>"
899       : "<td>&nbsp;</td>";
900
901     $column_data{serialnumber} = "<td>$ref->{serialnumber}</td>";
902
903     $column_data{soldtotal} = "<td  align=right>$ref->{soldtotal}</td>";
904
905     $i++;
906     $i %= 2;
907     print "<tr class=listrow$i>";
908
909     map { print "\n$column_data{$_}" } @column_index;
910
911     print qq|
912     </tr>
913 |;
914   }
915
916   if ($form->{l_subtotal} eq 'Y') {
917     parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
918   }    #fi
919
920   if ($form->{"l_linetotal"}) {
921     map { $column_data{$_} = "<td>&nbsp;</td>" } @column_index;
922     $column_data{linetotalsellprice} =
923         "<th class=listtotal align=right>"
924       . $form->format_amount(\%myconfig, $totalsellprice, 2)
925       . "</th>";
926     $column_data{linetotallastcost} =
927         "<th class=listtotal align=right>"
928       . $form->format_amount(\%myconfig, $totallastcost, 2)
929       . "</th>";
930     $column_data{linetotallistprice} =
931         "<th class=listtotal align=right>"
932       . $form->format_amount(\%myconfig, $totallistprice, 2)
933       . "</th>";
934
935     print "<tr class=listtotal>";
936
937     map { print "\n$column_data{$_}" } @column_index;
938
939     print qq|</tr>
940     |;
941   }
942
943   print qq|
944   <tr><td colspan=$colspan><hr size=3 noshade></td></tr>
945 </table>
946
947 |;
948
949   print qq|
950
951 <br>
952
953 <form method=post action=$form->{script}>
954
955 <input type=hidden name=itemstatus value="$form->{itemstatus}">
956 <input type=hidden name=l_linetotal value="$form->{l_linetotal}">
957 <input type=hidden name=l_partnumber value="$form->{l_partnumber}">
958 <input type=hidden name=l_description value="$form->{l_description}">
959 <input type=hidden name=l_onhand value="$form->{l_onhand}">
960 <input type=hidden name=l_unit value="$form->{l_unit}">
961 <input type=hidden name=l_sellprice value="$form->{l_sellprice}">
962 <input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
963 <input type=hidden name=sort value="$form->{sort}">
964 <input type=hidden name=revers value="$form->{revers}">
965 <input type=hidden name=lastsort value="$form->{lastsort}">
966 <input type=hidden name=parts value="$form->{parts}">
967
968 <input type=hidden name=bom value="$form->{bom}">
969 <input type=hidden name=titel value="$form->{titel}">
970 <input type=hidden name=searchitems value="$form->{searchitems}">|;
971
972   print $totop100;
973
974   print qq|
975 <!--    <input type=hidden name=ndxs_counter value="$form->{ndxs_counter}">-->
976
977     <input class=submit type=submit name=action value="|
978     . $locale->text('choice') . qq|">
979
980   </form>
981
982 </body>
983 </html>
984 |;
985
986   $lxdebug->leave_sub();
987 }    # end addtop100
988
989 #
990 # Report for Wares.
991 # Warning, deep magic ahead.
992 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
993 #
994 # flags coming from the form:
995 # hardcoded:
996 #  searchitems=part revers=0 lastsort=''
997 #
998 # filter:
999 # partnumber ean description partsgroup serialnumber make model drawing microfiche
1000 # transdatefrom transdateto
1001 #
1002 # radio:
1003 #  itemstatus = active | onhand | short | obsolete | orphaned
1004 #  action     = continue | top100
1005 #
1006 # checkboxes:
1007 #  bought sold onorder ordered rfq quoted
1008 #  l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
1009 #  l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
1010 #  l_partsgroup l_subtotal l_soldtotal l_deliverydate
1011 #
1012 # hiddens:
1013 #  nextsub revers lastsort sort ndxs_counter
1014 #
1015 sub generate_report {
1016   $lxdebug->enter_sub();
1017
1018   $auth->assert('part_service_assembly_edit');
1019
1020   my ($revers, $lastsort, $description);
1021
1022   my $cvar_configs = CVar->get_configs('module' => 'IC');
1023
1024   $form->{title} = (ucfirst $form->{searchitems}) . "s";
1025   $form->{title} =~ s/ys$/ies/;
1026   $form->{title} = $locale->text($form->{title});
1027
1028   my %column_defs = (
1029     'bin'                => { 'text' => $locale->text('Bin'), },
1030     'deliverydate'       => { 'text' => $locale->text('deliverydate'), },
1031     'description'        => { 'text' => $locale->text('Part Description'), },
1032     'drawing'            => { 'text' => $locale->text('Drawing'), },
1033     'image'              => { 'text' => $locale->text('Image'), },
1034     'invnumber'          => { 'text' => $locale->text('Invoice Number'), },
1035     'lastcost'           => { 'text' => $locale->text('Last Cost'), },
1036     'linetotallastcost'  => { 'text' => $locale->text('Extended'), },
1037     'linetotallistprice' => { 'text' => $locale->text('Extended'), },
1038     'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
1039     'listprice'          => { 'text' => $locale->text('List Price'), },
1040     'microfiche'         => { 'text' => $locale->text('Microfiche'), },
1041     'name'               => { 'text' => $locale->text('Name'), },
1042     'onhand'             => { 'text' => $locale->text('Qty'), },
1043     'ordnumber'          => { 'text' => $locale->text('Order Number'), },
1044     'partnumber'         => { 'text' => $locale->text('Part Number'), },
1045     'partsgroup'         => { 'text' => $locale->text('Group'), },
1046     'priceupdate'        => { 'text' => $locale->text('Updated'), },
1047     'quonumber'          => { 'text' => $locale->text('Quotation'), },
1048     'rop'                => { 'text' => $locale->text('ROP'), },
1049     'sellprice'          => { 'text' => $locale->text('Sell Price'), },
1050     'serialnumber'       => { 'text' => $locale->text('Serial Number'), },
1051     'soldtotal'          => { 'text' => $locale->text('soldtotal'), },
1052     'transdate'          => { 'text' => $locale->text('Transdate'), },
1053     'unit'               => { 'text' => $locale->text('Unit'), },
1054     'weight'             => { 'text' => $locale->text('Weight'), },
1055   );
1056
1057   $revers     = $form->{revers};
1058   $lastsort   = $form->{lastsort};
1059
1060   # sorting and direction of sorting
1061   # ToDO: change this to the simpler field+direction method
1062   if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
1063     $form->{revers}   = 0;
1064     $form->{lastsort} = "partnumber";
1065     $form->{sort}     = "partnumber";
1066   } else {
1067     if ($form->{lastsort} eq $form->{sort}) {
1068       $form->{revers} = 1 - $form->{revers};
1069     } else {
1070       $form->{revers} = 0;
1071       $form->{lastsort} = $form->{sort};
1072     }    #fi
1073   }    #fi
1074
1075   # special case if we have a serialnumber limit search
1076   # serialnumbers are only given in invoices and orders,
1077   # so they can only pop up in bought, sold, rfq, and quoted stuff
1078   $form->{no_sn_joins} = 'Y' if (   !$form->{bought} && !$form->{sold}
1079                                  && !$form->{rfq}    && !$form->{quoted}
1080                                  && ($form->{l_serialnumber} || $form->{serialnumber}));
1081
1082   # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
1083   # if any of these are ticked the behavior changes slightly for lastcost
1084   # since all those are aggregation checks for the legder tables this is an internal switch
1085   # refered to as ledgerchecks
1086   $form->{ledgerchecks} = 'Y' if (   $form->{bought} || $form->{sold} || $form->{onorder}
1087                                   || $form->{ordered} || $form->{rfq} || $form->{quoted});
1088
1089   # if something should be activated if something else is active, enter it here
1090   my %dependencies = (
1091     onhand       => [ qw(l_onhand) ],
1092     short        => [ qw(l_onhand) ],
1093     onorder      => [ qw(l_ordnumber) ],
1094     ordered      => [ qw(l_ordnumber) ],
1095     rfq          => [ qw(l_quonumber) ],
1096     quoted       => [ qw(l_quonumber) ],
1097     bought       => [ qw(l_invnumber) ],
1098     sold         => [ qw(l_invnumber) ],
1099     ledgerchecks => [ qw(l_name) ],
1100     serialnumber => [ qw(l_serialnumber) ],
1101     no_sn_joins  => [ qw(bought sold) ],
1102   );
1103
1104   # these strings get displayed at the top of the results to indicate the user which switches were used
1105   my %optiontexts = (
1106     active        => $locale->text('Active'),
1107     obsolete      => $locale->text('Obsolete'),
1108     orphaned      => $locale->text('Orphaned'),
1109     onhand        => $locale->text('On Hand'),
1110     short         => $locale->text('Short'),
1111     onorder       => $locale->text('On Order'),
1112     ordered       => $locale->text('Ordered'),
1113     rfq           => $locale->text('RFQ'),
1114     quoted        => $locale->text('Quoted'),
1115     bought        => $locale->text('Bought'),
1116     sold          => $locale->text('Sold'),
1117     transdatefrom => $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
1118     transdateto   => $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
1119     partnumber    => $locale->text('Part Number')      . ": '$form->{partnumber}'",
1120     partsgroup    => $locale->text('Group')            . ": '$form->{partsgroup}'",
1121     serialnumber  => $locale->text('Serial Number')    . ": '$form->{serialnumber}'",
1122     description   => $locale->text('Part Description') . ": '$form->{description}'",
1123     make          => $locale->text('Make')             . ": '$form->{make}'",
1124     model         => $locale->text('Model')            . ": '$form->{model}'",
1125     drawing       => $locale->text('Drawing')          . ": '$form->{drawing}'",
1126     microfiche    => $locale->text('Microfiche')       . ": '$form->{microfiche}'",
1127     l_soldtotal   => $locale->text('soldtotal'),
1128     ean           => $locale->text('EAN')              . ": '$form->{ean}'",
1129   );
1130
1131   my @itemstatus_keys = qw(active obsolete orphaned onhand short);
1132   my @callback_keys   = qw(onorder ordered rfq quoted bought sold partnumber partsgroup serialnumber description make model
1133                            drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto ean);
1134
1135   # calculate dependencies
1136   for (@itemstatus_keys, @callback_keys) {
1137     next if ($form->{itemstatus} ne $_ && !$form->{$_});
1138     map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
1139   }
1140
1141   # generate callback and optionstrings
1142   my @options;
1143   for my  $key (@itemstatus_keys, @callback_keys) {
1144     next if ($form->{itemstatus} ne $key && !$form->{$key});
1145     push @options, $optiontexts{$key};
1146   }
1147
1148   # special case for lastcost
1149   if ($form->{ledgerchecks}){
1150     # ledgerchecks don't know about sellprice or lastcost. they just return a
1151     # price. so rename sellprice to price, and drop lastcost.
1152     $column_defs{sellprice}{text} = $locale->text('Price');
1153     $form->{l_lastcost} = ""
1154   }
1155
1156   if ($form->{description}) {
1157     $description = $form->{description};
1158     $description =~ s/\n/<br>/g;
1159   }
1160
1161   if ($form->{l_linetotal}) {
1162     $form->{l_onhand} = "Y";
1163     $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
1164     $form->{l_linetotallastcost}  = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if  $form->{l_lastcost};
1165     $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
1166   }
1167
1168   if ($form->{searchitems} eq 'service') {
1169
1170     # remove bin, weight and rop from list
1171     map { $form->{"l_$_"} = "" } qw(bin weight rop);
1172
1173     $form->{l_onhand} = "";
1174
1175     # qty is irrelevant unless bought or sold
1176     if (   $form->{bought}
1177         || $form->{sold}
1178         || $form->{onorder}
1179         || $form->{ordered}
1180         || $form->{rfq}
1181         || $form->{quoted}) {
1182       $form->{l_onhand} = "Y";
1183     } else {
1184       $form->{l_linetotalsellprice} = "";
1185       $form->{l_linetotallastcost}  = "";
1186     }
1187   }
1188
1189   IC->all_parts(\%myconfig, \%$form);
1190
1191   my @columns = qw(
1192     partnumber description partsgroup bin onhand rop unit listprice
1193     linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
1194     priceupdate weight image drawing microfiche invnumber ordnumber quonumber
1195     transdate name serialnumber soldtotal deliverydate ean
1196   );
1197
1198   my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
1199   my @searchable_custom_variables  = grep { $_->{searchable} }  @{ $cvar_configs };
1200   my %column_defs_cvars            = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
1201
1202   push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
1203
1204   %column_defs = (%column_defs,%column_defs_cvars); # nochmal die cvars als Ã¼berschrift hinzufügen
1205     map { $column_defs{$_}->{visible} = $form->{"l_$_"} ? 1 : 0 } @columns;
1206   map { $column_defs{$_}->{align}   = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal);
1207
1208   my @hidden_variables = (qw(l_subtotal l_linetotal searchitems itemstatus bom), @itemstatus_keys, @callback_keys, @searchable_custom_variables, map { "l_$_" } @columns);
1209   my $callback         = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
1210
1211   my @sort_full        = qw(partnumber description onhand soldtotal deliverydate);
1212   my @sort_no_revers   = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
1213
1214   foreach my $col (@sort_full) {
1215     $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
1216   }
1217   map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
1218
1219   # add order to callback
1220   $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
1221
1222   my $report = SL::ReportGenerator->new(\%myconfig, $form);
1223
1224   my %attachment_basenames = (
1225     'part'     => $locale->text('part_list'),
1226     'service'  => $locale->text('service_list'),
1227     'assembly' => $locale->text('assembly_list'),
1228   );
1229
1230   $report->set_options('top_info_text'         => $locale->text('Options') . ': ' . join(', ', grep $_, @options),
1231                        'raw_bottom_info_text'  => $form->parse_html_template('ic/generate_report_bottom'),
1232                        'output_format'         => 'HTML',
1233                        'title'                 => $form->{title},
1234                        'attachment_basename'   => $attachment_basenames{$form->{searchitems}} . strftime('_%Y%m%d', localtime time),
1235   );
1236   $report->set_options_from_form();
1237
1238   $report->set_columns(%column_defs);
1239   $report->set_column_order(@columns);
1240
1241   $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
1242
1243   $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
1244
1245   CVar->add_custom_variables_to_report('module'         => 'IC',
1246                                        'trans_id_field' => 'id',
1247                                        'configs'        => $cvar_configs,
1248                                        'column_defs'    => \%column_defs,
1249                                        'data'           => $form->{parts});
1250
1251   CVar->add_custom_variables_to_report('module'         => 'IC',
1252                                        'sub_module'     => sub { $_[0]->{ioi} },
1253                                        'trans_id_field' => 'ioi_id',
1254                                        'configs'        => $cvar_configs,
1255                                        'column_defs'    => \%column_defs,
1256                                        'data'           => $form->{parts});
1257
1258   my @subtotal_columns = qw(sellprice listprice lastcost);
1259   my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
1260   my %totals    = map { $_ => 0 } @subtotal_columns;
1261   my $idx       = 0;
1262   my $same_item = $form->{parts}[0]{ $form->{sort} } if (scalar @{ $form->{parts} });
1263
1264   my $defaults  = AM->get_defaults();
1265
1266   # postprocess parts
1267   foreach my $ref (@{ $form->{parts} }) {
1268
1269     # fresh row, for inserting later
1270     my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
1271
1272     $ref->{exchangerate} ||= 1;
1273     $ref->{price_factor} ||= 1;
1274     $ref->{sellprice}     *= $ref->{exchangerate} / $ref->{price_factor};
1275     $ref->{listprice}     *= $ref->{exchangerate} / $ref->{price_factor};
1276     $ref->{lastcost}      *= $ref->{exchangerate} / $ref->{price_factor};
1277
1278     # use this for assemblies
1279     my $onhand = $ref->{onhand};
1280
1281     if ($ref->{assemblyitem}) {
1282       $row->{partnumber}{align}   = 'right';
1283       $row->{onhand}{data}        = 0;
1284       $onhand                     = 0 if ($form->{sold});
1285     }
1286
1287     my $edit_link               = build_std_url('action=edit', 'id=' . E($ref->{id}), 'callback');
1288     $row->{partnumber}->{link}  = $edit_link;
1289     $row->{description}->{link} = $edit_link;
1290
1291     foreach (qw(sellprice listprice lastcost)) {
1292       $row->{$_}{data}            = $form->format_amount(\%myconfig, $ref->{$_}, -2);
1293       $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
1294     }
1295
1296     map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
1297
1298     $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
1299
1300     if (!$ref->{assemblyitem}) {
1301       foreach my $col (@subtotal_columns) {
1302         $totals{$col}    += $onhand * $ref->{$col};
1303         $subtotals{$col} += $onhand * $ref->{$col};
1304       }
1305
1306       $subtotals{onhand} += $onhand;
1307     }
1308
1309     # set module stuff
1310     if ($ref->{module} eq 'oe') {
1311       my $edit_oe_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');
1312       $row->{ordnumber}{link} = $edit_oe_link;
1313       $row->{quonumber}{link} = $edit_oe_link if (!$ref->{ordnumber});
1314
1315     } else {
1316       $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback');
1317     }
1318
1319     # set properties of images
1320     if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
1321       $row->{image}{data}     = '';
1322       $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
1323     }
1324     map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
1325
1326     $report->add_data($row);
1327
1328     my $next_ref = $form->{parts}[$idx + 1];
1329
1330     # insert subtotal rows
1331     if (($form->{l_subtotal} eq 'Y') &&
1332         (!$next_ref ||
1333          (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
1334       my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
1335
1336       if (($form->{searchitems} ne 'assembly') || !$form->{bom}) {
1337         $row->{onhand}->{data} = $form->format_amount(\%myconfig, $subtotals{onhand});
1338       }
1339
1340       map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
1341       map { $subtotals{$_} = 0 } ('onhand', @subtotal_columns);
1342
1343       $report->add_data($row);
1344
1345       $same_item = $next_ref->{ $form->{sort} };
1346     }
1347
1348     $idx++;
1349   }
1350
1351   if ($form->{"l_linetotal"}) {
1352     my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
1353
1354     map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
1355
1356     $report->add_separator();
1357     $report->add_data($row);
1358   }
1359
1360   $report->generate_with_headers();
1361
1362   $lxdebug->leave_sub();
1363 }    #end generate_report
1364
1365 sub parts_subtotal {
1366   $lxdebug->enter_sub();
1367
1368   $auth->assert('part_service_assembly_edit');
1369
1370   my (%column_data);
1371   my ($column_index, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice) = @_;
1372
1373   map { $column_data{$_} = "<td>&nbsp;</td>" } @{ $column_index };
1374   $$subtotalonhand = 0 if ($form->{searchitems} eq 'assembly' && $form->{bom});
1375
1376   $column_data{onhand} =
1377       "<th class=listsubtotal align=right>"
1378     . $form->format_amount(\%myconfig, $$subtotalonhand)
1379     . "</th>";
1380
1381   $column_data{linetotalsellprice} =
1382       "<th class=listsubtotal align=right>"
1383     . $form->format_amount(\%myconfig, $$subtotalsellprice, 2)
1384     . "</th>";
1385   $column_data{linetotallistprice} =
1386       "<th class=listsubtotal align=right>"
1387     . $form->format_amount(\%myconfig, $$subtotallistprice, 2)
1388     . "</th>";
1389   $column_data{linetotallastcost} =
1390       "<th class=listsubtotal align=right>"
1391     . $form->format_amount(\%myconfig, $$subtotallastcost, 2)
1392     . "</th>";
1393
1394   $$subtotalonhand    = 0;
1395   $$subtotalsellprice = 0;
1396   $$subtotallistprice = 0;
1397   $$subtotallastcost  = 0;
1398
1399   print "<tr class=listsubtotal>";
1400
1401   map { print "\n$column_data{$_}" } @{ $column_index };
1402
1403   print qq|
1404   </tr>
1405 |;
1406
1407   $lxdebug->leave_sub();
1408 }
1409
1410 sub edit {
1411   $lxdebug->enter_sub();
1412
1413   $auth->assert('part_service_assembly_edit');
1414
1415   # show history button
1416   $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
1417   #/show hhistory button
1418   IC->get_part(\%myconfig, \%$form);
1419
1420   $form->{"original_partnumber"} = $form->{"partnumber"};
1421
1422   $form->{title} = $locale->text('Edit' . ucfirst $form->{item});
1423
1424   &link_part;
1425   &display_form;
1426
1427   $lxdebug->leave_sub();
1428 }
1429
1430 sub link_part {
1431   $lxdebug->enter_sub();
1432
1433   $auth->assert('part_service_assembly_edit');
1434
1435   IC->create_links("IC", \%myconfig, \%$form);
1436
1437   # currencies
1438   map({ $form->{selectcurrency} .= "<option>$_\n" }
1439       split(/:/, $form->{currencies}));
1440
1441   # parts and assemblies have the same links
1442   my $item = $form->{item};
1443   if ($form->{item} eq 'assembly') {
1444     $item = 'part';
1445   }
1446
1447   # build the popup menus
1448   $form->{taxaccounts} = "";
1449   foreach my $key (keys %{ $form->{IC_links} }) {
1450     foreach my $ref (@{ $form->{IC_links}{$key} }) {
1451
1452       # if this is a tax field
1453       if ($key =~ /IC_tax/) {
1454         if ($key =~ /\Q$item\E/) {
1455           $form->{taxaccounts} .= "$ref->{accno} ";
1456           $form->{"IC_tax_$ref->{accno}_description"} =
1457             "$ref->{accno}--$ref->{description}";
1458
1459           if ($form->{id}) {
1460             if ($form->{amount}{ $ref->{accno} }) {
1461               $form->{"IC_tax_$ref->{accno}"} = "checked";
1462             }
1463           } else {
1464             $form->{"IC_tax_$ref->{accno}"} = "checked";
1465           }
1466         }
1467       } else {
1468
1469         $form->{"select$key"} .=
1470           "<option $ref->{selected}>$ref->{accno}--$ref->{description}\n";
1471         if ($form->{amount}{$key} eq $ref->{accno}) {
1472           $form->{$key} = "$ref->{accno}--$ref->{description}";
1473         }
1474
1475       }
1476     }
1477   }
1478   chop $form->{taxaccounts};
1479
1480   if (($form->{item} eq "part") || ($form->{item} eq "assembly")) {
1481     $form->{selectIC_income}  = $form->{selectIC_sale};
1482     $form->{selectIC_expense} = $form->{selectIC_cogs};
1483     $form->{IC_income}        = $form->{IC_sale};
1484     $form->{IC_expense}       = $form->{IC_cogs};
1485   }
1486
1487   delete $form->{IC_links};
1488   delete $form->{amount};
1489
1490   $form->get_partsgroup(\%myconfig, { all => 1 });
1491
1492   $form->{partsgroup} = "$form->{partsgroup}--$form->{partsgroup_id}";
1493
1494   if (@{ $form->{all_partsgroup} }) {
1495     $form->{selectpartsgroup} = qq|<option>\n|;
1496     map { $form->{selectpartsgroup} .= qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n| } @{ $form->{all_partsgroup} };
1497   }
1498
1499   if ($form->{item} eq 'assembly') {
1500
1501     foreach my $i (1 .. $form->{assembly_rows}) {
1502       if ($form->{"partsgroup_id_$i"}) {
1503         $form->{"partsgroup_$i"} =
1504           qq|$form->{"partsgroup_$i"}--$form->{"partsgroup_id_$i"}|;
1505       }
1506     }
1507     $form->get_partsgroup(\%myconfig);
1508
1509     if (@{ $form->{all_partsgroup} }) {
1510       $form->{selectassemblypartsgroup} = qq|<option>\n|;
1511
1512       map {
1513         $form->{selectassemblypartsgroup} .=
1514           qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n|
1515       } @{ $form->{all_partsgroup} };
1516     }
1517   }
1518   $lxdebug->leave_sub();
1519 }
1520
1521 sub form_header {
1522   $lxdebug->enter_sub();
1523
1524   $auth->assert('part_service_assembly_edit');
1525
1526   $form->{eur}              = $main::eur; # config dumps into namespace - yuck
1527   $form->{pg_keys}          = sub { "$_[0]->{partsgroup}--$_[0]->{id}" };
1528   $form->{description_area} = ($form->{rows} = $form->numtextrows($form->{description}, 40)) > 1;
1529   $form->{notes_rows}       =  max 4, $form->numtextrows($form->{notes}, 40), $form->numtextrows($form->{formel}, 40);
1530
1531   map { $form->{"is_$_"}  = ($form->{item} eq $_) } qw(part service assembly);
1532   map { $form->{$_}       =~ s/"/&quot;/g;        } qw(unit);
1533
1534   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
1535                    'partsgroup'    => 'all_partsgroup',
1536                    'vendors'       => 'ALL_VENDORS',);
1537
1538
1539   IC->retrieve_buchungsgruppen(\%myconfig, $form);
1540   @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
1541
1542   # use JavaScript Calendar or not (yes!)
1543   $form->{jsscript} = 1;
1544
1545   my $units = AM->retrieve_units(\%myconfig, $form);
1546   $form->{ALL_UNITS} = [ map +{ name => $_ }, sort { $units->{$a}{sortkey} <=> $units->{$b}{sortkey} } keys %$units ];
1547
1548   $form->{defaults} = AM->get_defaults();
1549
1550   $form->{fokus} = "ic.partnumber";
1551
1552   $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'IC', 'trans_id' => $form->{id});
1553
1554   CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}, show_disabled_message => 1)
1555     if (scalar @{ $form->{CUSTOM_VARIABLES} });
1556
1557   $form->header;
1558   #print $form->parse_html_template('ic/form_header', { ALL_PRICE_FACTORS => $form->{ALL_PRICE_FACTORS},
1559   #                                                     ALL_UNITS         => $form->{ALL_UNITS},
1560   #                                                     BUCHUNGSGRUPPEN   => $form->{BUCHUNGSGRUPPEN},
1561   #                                                     payment_terms     => $form->{payment_terms},
1562   #                                                     all_partsgroup    => $form->{all_partsgroup}});
1563   print $form->parse_html_template('ic/form_header');
1564   $lxdebug->leave_sub();
1565 }
1566
1567 sub form_footer {
1568   $lxdebug->enter_sub();
1569
1570   $auth->assert('part_service_assembly_edit');
1571
1572   print $form->parse_html_template('ic/form_footer');
1573
1574   $lxdebug->leave_sub();
1575 }
1576
1577 sub makemodel_row {
1578   $lxdebug->enter_sub();
1579   my ($numrows) = @_;
1580
1581   my @mm_data = grep { any { $_ ne '' } @$_{qw(make model)} } map +{ make => $form->{"make_$_"}, model => $form->{"model_$_"} }, 1 .. $numrows;
1582   delete @{$form}{grep { m/^make_\d+/ || m/^model_\d+/ } keys %{ $form }};
1583   print $form->parse_html_template('ic/makemodel', { MM_DATA => [ @mm_data, {} ], mm_rows => scalar @mm_data + 1 });
1584
1585   $lxdebug->leave_sub();
1586 }
1587
1588 sub assembly_row {
1589   $lxdebug->enter_sub();
1590   my ($numrows) = @_;
1591   my (@column_index);
1592   my ($nochange, $callback, $previousform, $linetotal, $line_purchase_price, $href);
1593
1594   @column_index = qw(runningnumber qty unit bom partnumber description partsgroup lastcost total);
1595
1596   if ($form->{previousform}) {
1597     $nochange     = 1;
1598     @column_index = qw(qty unit bom partnumber description partsgroup total);
1599   } else {
1600
1601     # change callback
1602     $form->{old_callback} = $form->{callback};
1603     $callback             = $form->{callback};
1604     $form->{callback}     = "$form->{script}?action=display_form";
1605
1606     # delete action
1607     map { delete $form->{$_} } qw(action header);
1608
1609     # save form variables in a previousform variable
1610     $previousform = $form->escape($form->escape(join '&', map {
1611       sprintf "%s=%s", Q($_), /^listprice|lastcost|sellprice$/ ? $form->format_amount(\%myconfig, $form->{$_}) : $form->{$_}
1612     } grep { ref $form->{$_} eq '' && $form->{$_} } grep { !/^select/ } sort keys %$form ));
1613
1614     $form->{callback} = $callback;
1615     $form->{assemblytotal} = 0;
1616     $form->{assembly_purchase_price_total} = 0;
1617     $form->{weight}        = 0;
1618   }
1619
1620   my %header = (
1621    runningnumber => { text =>  $locale->text('No.'),              nowrap => 1, width => '5%'  },
1622    qty           => { text =>  $locale->text('Qty'),              nowrap => 1, width => '10%' },
1623    unit          => { text =>  $locale->text('Unit'),             nowrap => 1, width => '5%'  },
1624    partnumber    => { text =>  $locale->text('Part Number'),      nowrap => 1, width => '20%' },
1625    description   => { text =>  $locale->text('Part Description'), nowrap => 1, width => '50%' },
1626    lastcost      => { text =>  $locale->text('Purchase Prices'),  nowrap => 1, width => '50%' },
1627    total         => { text =>  $locale->text('Sale Prices'),      nowrap => 1,                },
1628    bom           => { text =>  $locale->text('BOM'),                                          },
1629    partsgroup    => { text =>  $locale->text('Group'),                                        },
1630   );
1631
1632   my @ROWS;
1633
1634   for my $i (1 .. $numrows) {
1635     my (%row, @row_hiddens);
1636
1637     $form->{"partnumber_$i"} =~ s/\"/&quot;/g;
1638
1639     $linetotal           = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
1640     $line_purchase_price = $form->round_amount($form->{"lastcost_$i"} *  $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
1641     $form->{assemblytotal}                  += $linetotal;
1642     $form->{assembly_purchase_price_total}  += $line_purchase_price;
1643     $form->{"qty_$i"}    = $form->format_amount(\%myconfig, $form->{"qty_$i"});
1644     $linetotal           = $form->format_amount(\%myconfig, $linetotal, 2);
1645     $line_purchase_price = $form->format_amount(\%myconfig, $line_purchase_price, 2);
1646     $href                = qq|$form->{script}?action=edit&id=$form->{"id_$i"}&rowcount=$i&previousform=$previousform|;
1647     map { $row{$_}{data} = "" } qw(qty unit partnumber description bom partsgroup runningnumber);
1648
1649     # last row
1650     if (($i >= 1) && ($i == $numrows)) {
1651       if (!$form->{previousform}) {
1652         $row{partnumber}{data}  = qq|<input name="partnumber_$i" size=15 value="$form->{"partnumber_$i"}">|;
1653         $row{qty}{data}         = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1654         $row{description}{data} = qq|<input name="description_$i" size=40 value="$form->{"description_$i"}">|;
1655         $row{partsgroup}{data}  = qq|<input name="partsgroup_$i" size=10 value="$form->{"partsgroup_$i"}">|;
1656       }
1657     # other rows
1658     } else {
1659       if ($form->{previousform}) {
1660         push @row_hiddens,          qw(qty bom);
1661         $row{partnumber}{data}    = $form->{"partnumber_$i"};
1662         $row{qty}{data}           = $form->{"qty_$i"};
1663         $row{bom}{data}           = $form->{"bom_$i"} ? "x" : "&nbsp;";
1664         $row{qty}{align}          = 'right';
1665       } else {
1666         $row{partnumber}{data}    = qq|<a href=$href>$form->{"partnumber_$i"}</a>|;
1667         $row{qty}{data}           = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1668         $row{runningnumber}{data} = qq|<input name="runningnumber_$i" size=3 value="$i">|;
1669         $row{bom}{data}   = sprintf qq|<input name="bom_$i" type=checkbox class=checkbox value=1 %s>|,
1670                                        $form->{"bom_$i"} ? 'checked' : '';
1671       }
1672       push @row_hiddens,        qw(unit description partnumber partsgroup);
1673       $row{unit}{data}        = $form->{"unit_$i"};
1674       $row{description}{data} = $form->{"description_$i"};
1675       $row{partsgroup}{data}  = $form->{"partsgroup_$i"};
1676       $row{bom}{align}        = 'center';
1677     }
1678
1679     $row{lastcost}{data}      = $line_purchase_price;
1680     $row{total}{data}         = $linetotal;
1681     $row{lastcost}{align}     = 'right';
1682     $row{total}{align}        = 'right';
1683     $row{deliverydate}{align} = 'right';
1684
1685     push @row_hiddens, qw(id sellprice lastcost weight price_factor_id price_factor);
1686     $row{hiddens} = [ map +{ name => "${_}_$i", value => $form->{"${_}_$i"} }, @row_hiddens ];
1687
1688     push @ROWS, \%row;
1689   }
1690
1691   print $form->parse_html_template('ic/assembly_row', { COLUMNS => \@column_index, ROWS => \@ROWS, HEADER => \%header });
1692
1693   $lxdebug->leave_sub();
1694 }
1695
1696 sub update {
1697   $lxdebug->enter_sub();
1698
1699   # parse pricegroups. and no, don't rely on check_form for this...
1700   map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
1701
1702   if ($form->{item} eq "assembly") {
1703     my $i = $form->{assembly_rows};
1704
1705     # if last row is empty check the form otherwise retrieve item
1706     if (   ($form->{"partnumber_$i"} eq "")
1707         && ($form->{"description_$i"} eq "")
1708         && ($form->{"partsgroup_$i"}  eq "")) {
1709
1710       &check_form;
1711
1712     } else {
1713
1714       IC->assembly_item(\%myconfig, \%$form);
1715
1716       my $rows = scalar @{ $form->{item_list} };
1717
1718       if ($rows) {
1719         $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1720
1721         if ($rows > 1) {
1722           $form->{makemodel_rows}--;
1723           &select_item;
1724           ::end_of_request();
1725         } else {
1726           map { $form->{item_list}[$i]{$_} =~ s/\"/&quot;/g }
1727             qw(partnumber description unit partsgroup);
1728           map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} }
1729             keys %{ $form->{item_list}[0] };
1730           $form->{"runningnumber_$i"} = $form->{assembly_rows};
1731           $form->{assembly_rows}++;
1732
1733           &check_form;
1734
1735         }
1736
1737       } else {
1738
1739         $form->{rowcount} = $i;
1740         $form->{assembly_rows}++;
1741
1742         &new_item;
1743
1744       }
1745     }
1746
1747   } elsif (($form->{item} eq 'part') || ($form->{item} eq 'service')) {
1748     &check_form;
1749   }
1750
1751   $lxdebug->leave_sub();
1752 }
1753
1754 sub save {
1755   $lxdebug->enter_sub();
1756
1757   $auth->assert('part_service_assembly_edit');
1758
1759   my ($parts_id, %newform, $previousform, $amount, $callback);
1760
1761   # check if there is a part number - commented out, cause there is an automatic allocation of numbers
1762   # $form->isblank("partnumber", $locale->text(ucfirst $form->{item}." Part Number missing!"));
1763
1764   # check if there is a description
1765   $form->isblank("description", $locale->text("Part Description missing!"));
1766
1767   $form->error($locale->text("Inventory quantity must be zero before you can set this $form->{item} obsolete!"))
1768     if $form->{obsolete} && $form->{onhand} * 1 && $form->{item} ne 'service';
1769
1770   if (!$form->{buchungsgruppen_id}) {
1771     $form->error($locale->text("Parts must have an entry type.") . " " .
1772      $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.")
1773     );
1774   }
1775
1776   $form->error($locale->text('Description must not be empty!')) unless $form->{description};
1777   $form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
1778
1779   # save part
1780   $lxdebug->message($LXDebug::DEBUG1, "ic.pl: sellprice in save = $form->{sellprice}\n");
1781   if (IC->save(\%myconfig, \%$form) == 3) {
1782     $form->error($locale->text('Partnumber not unique!'));
1783   }
1784   # saving the history
1785   if(!exists $form->{addition}) {
1786     $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1787     $form->{addition} = "SAVED";
1788     $form->save_history;
1789   }
1790   # /saving the history
1791   $parts_id = $form->{id};
1792
1793   my $i;
1794   # load previous variables
1795   if ($form->{previousform}) {
1796
1797     # save the new form variables before splitting previousform
1798     map { $newform{$_} = $form->{$_} } keys %$form;
1799
1800     $previousform = $form->unescape($form->{previousform});
1801
1802     # don't trample on previous variables
1803     map { delete $form->{$_} } keys %newform;
1804
1805     my $ic_cvar_configs = CVar->get_configs(module => 'IC');
1806     my @ic_cvar_fields  = map { "cvar_$_->{name}" } @{ $ic_cvar_configs };
1807
1808     # now take it apart and restore original values
1809     foreach my $item (split /&/, $previousform) {
1810       my ($key, $value) = split m/=/, $item, 2;
1811       $value =~ s/%26/&/g;
1812       $form->{$key} = $value;
1813     }
1814     $form->{taxaccounts} = $newform{taxaccount2};
1815
1816     if ($form->{item} eq 'assembly') {
1817
1818       # undo number formatting
1819       map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
1820         qw(weight listprice sellprice rop);
1821
1822       $form->{assembly_rows}--;
1823       $i = $newform{rowcount};
1824       $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1825
1826       $form->{sellprice} -= $form->{"sellprice_$i"} * $form->{"qty_$i"};
1827       $form->{weight}    -= $form->{"weight_$i"} * $form->{"qty_$i"};
1828
1829       # change/add values for assembly item
1830       map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit weight listprice sellprice inventory_accno income_accno expense_accno price_factor_id);
1831       map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1832
1833       # das ist __voll__ bekloppt, dass so auszurechnen jb 22.5.09
1834       #$form->{sellprice} += $form->{"sellprice_$i"} * $form->{"qty_$i"};
1835       $form->{weight}    += $form->{"weight_$i"} * $form->{"qty_$i"};
1836
1837     } else {
1838
1839       # set values for last invoice/order item
1840       $i = $form->{rowcount};
1841       $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1842
1843       map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit listprice inventory_accno income_accno expense_accno sellprice lastcost price_factor_id);
1844       map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1845
1846       $form->{"longdescription_$i"} = $newform{notes};
1847
1848       $form->{"sellprice_$i"} = $newform{lastcost} if ($form->{vendor_id});
1849
1850       if ($form->{exchangerate} != 0) {
1851         $form->{"sellprice_$i"} /= $form->{exchangerate};
1852       }
1853
1854       $lxdebug->message($LXDebug::DEBUG1, qq|sellprice_$i in previousform 2 = | . $form->{"sellprice_$i"} . qq|\n|);
1855
1856       map { $form->{"taxaccounts_$i"} .= "$_ " } split / /, $newform{taxaccount};
1857       chop $form->{"taxaccounts_$i"};
1858       foreach my $item (qw(description rate taxnumber)) {
1859         my $index = $form->{"taxaccounts_$i"} . "_$item";
1860         $form->{$index} = $newform{$index};
1861       }
1862
1863       # credit remaining calculation
1864       $amount = $form->{"sellprice_$i"} * (1 - $form->{"discount_$i"} / 100) * $form->{"qty_$i"};
1865
1866       map { $form->{"${_}_base"} += $amount } (split / /, $form->{"taxaccounts_$i"});
1867       map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{"taxaccounts_$i"} if !$form->{taxincluded};
1868
1869       $form->{creditremaining} -= $amount;
1870
1871       # redo number formatting, because invoice parse them!
1872       map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}) } qw(weight listprice sellprice lastcost rop);
1873     }
1874
1875     $form->{"id_$i"} = $parts_id;
1876
1877     # Get the actual price factor (not just the ID) for the marge calculation.
1878     $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
1879     foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
1880       next if ($pfac->{id} != $newform{price_factor_id});
1881       $form->{"marge_price_factor_$i"} = $pfac->{factor};
1882       last;
1883     }
1884     delete $form->{ALL_PRICE_FACTORS};
1885
1886     delete $form->{action};
1887
1888     # restore original callback
1889     $callback = $form->unescape($form->{callback});
1890     $form->{callback} = $form->unescape($form->{old_callback});
1891     delete $form->{old_callback};
1892
1893     $form->{makemodel_rows}--;
1894
1895     # put callback together
1896     foreach my $key (keys %$form) {
1897
1898       # do single escape for Apache 2.0
1899       my $value = $form->escape($form->{$key}, 1);
1900       $callback .= qq|&$key=$value|;
1901     }
1902     $form->{callback} = $callback;
1903   }
1904   $lxdebug->message($LXDebug::DEBUG1, qq|ic.pl: sellprice_$i nach sub save = | . $form->{"sellprice_$i"} . qq|\n|);
1905
1906   # redirect
1907   $form->redirect;
1908
1909   $lxdebug->leave_sub();
1910 }
1911
1912 sub save_as_new {
1913   $lxdebug->enter_sub();
1914
1915   $auth->assert('part_service_assembly_edit');
1916
1917   # saving the history
1918   if(!exists $form->{addition}) {
1919     $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1920     $form->{addition} = "SAVED AS NEW";
1921     $form->save_history;
1922   }
1923   # /saving the history
1924   $form->{id} = 0;
1925   if ($form->{"original_partnumber"} &&
1926       ($form->{"partnumber"} eq $form->{"original_partnumber"})) {
1927     $form->{partnumber} = "";
1928   }
1929   &save;
1930   $lxdebug->leave_sub();
1931 }
1932
1933 sub delete {
1934   $lxdebug->enter_sub();
1935
1936   $auth->assert('part_service_assembly_edit');
1937
1938   # saving the history
1939   if(!exists $form->{addition}) {
1940     $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1941     $form->{addition} = "DELETED";
1942     $form->save_history;
1943   }
1944   # /saving the history
1945   my $rc = IC->delete(\%myconfig, \%$form);
1946
1947   # redirect
1948   $form->redirect($locale->text('Item deleted!')) if ($rc > 0);
1949   $form->error($locale->text('Cannot delete item!'));
1950
1951   $lxdebug->leave_sub();
1952 }
1953
1954 sub price_row {
1955   $lxdebug->enter_sub();
1956
1957   $auth->assert('part_service_assembly_edit');
1958
1959   my ($numrows) = @_;
1960
1961   my @PRICES = map +{
1962     pricegroup    => $form->{"pricegroup_$_"},
1963     pricegroup_id => $form->{"pricegroup_id_$_"},
1964     price         => $form->{"price_$_"},
1965   }, 1 .. $numrows;
1966
1967   print $form->parse_html_template('ic/price_row', { PRICES => \@PRICES });
1968
1969   $lxdebug->leave_sub();
1970 }
1971
1972 sub parts_language_selection {
1973   $lxdebug->enter_sub();
1974
1975   $auth->assert('part_service_assembly_edit');
1976
1977   my $languages = IC->retrieve_languages(\%myconfig, $form);
1978
1979   if ($form->{language_values} ne "") {
1980     foreach my $item (split(/---\+\+\+---/, $form->{language_values})) {
1981       my ($language_id, $translation, $longdescription) = split(/--\+\+--/, $item);
1982
1983       foreach my $language (@{ $languages }) {
1984         next unless ($language->{id} == $language_id);
1985
1986         $language->{translation}     = $translation;
1987         $language->{longdescription} = $longdescription;
1988         last;
1989       }
1990     }
1991   }
1992
1993   my @header_sort = qw(name longdescription);
1994   my %header_title = ( "name" => $locale->text("Name"),
1995                        "longdescription" => $locale->text("Long Description"),
1996                        );
1997
1998   my @header =
1999     map(+{ "column_title" => $header_title{$_},
2000            "column" => $_,
2001          },
2002         @header_sort);
2003
2004   $form->{"title"} = $locale->text("Language Values");
2005   $form->header();
2006   print $form->parse_html_template("ic/parts_language_selection", { "HEADER"    => \@header,
2007                                                                     "LANGUAGES" => $languages, });
2008
2009   $lxdebug->leave_sub();
2010 }
2011
2012 sub ajax_autocomplete {
2013   $main::lxdebug->enter_sub();
2014
2015   my $form     = $main::form;
2016   my %myconfig = %main::myconfig;
2017
2018   $form->{column}          = 'description'     unless $form->{column} =~ /^partnumber|description$/;
2019   $form->{$form->{column}} = $form->{q}           || '';
2020   $form->{limit}           = ($form->{limit} * 1) || 10;
2021   $form->{searchitems}   ||= '';
2022
2023   my @results = IC->all_parts(\%myconfig, $form);
2024
2025   print $form->ajax_response_header(),
2026         $form->parse_html_template('ic/ajax_autocomplete');
2027
2028   $main::lxdebug->leave_sub();
2029 }
2030
2031 sub continue { call_sub($form->{"nextsub"}); }