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(max);
 
  36 use List::MoreUtils qw(any);
 
  41 use SL::ReportGenerator;
 
  49 our ($form, $locale, %myconfig, $lxdebug, $auth);
 
  51 require "bin/mozilla/io.pl";
 
  52 require "bin/mozilla/invoice_io.pl";
 
  53 require "bin/mozilla/common.pl";
 
  54 require "bin/mozilla/reportgenerator.pl";
 
  59 # type=submit $locale->text('Add Part')
 
  60 # type=submit $locale->text('Add Service')
 
  61 # type=submit $locale->text('Add Assembly')
 
  62 # type=submit $locale->text('Edit Part')
 
  63 # type=submit $locale->text('Edit Service')
 
  64 # type=submit $locale->text('Edit Assembly')
 
  65 # $locale->text('Parts')
 
  66 # $locale->text('Services')
 
  67 # $locale->text('Inventory quantity must be zero before you can set this part obsolete!')
 
  68 # $locale->text('Inventory quantity must be zero before you can set this assembly obsolete!')
 
  69 # $locale->text('Part Number missing!')
 
  70 # $locale->text('Service Number missing!')
 
  71 # $locale->text('Assembly Number missing!')
 
  72 # $locale->text('ea');
 
  77   $lxdebug->enter_sub();
 
  79   $auth->assert('part_service_assembly_edit');
 
  81   $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;
 
  85   IC->get_pricegroups(\%myconfig, \%$form);
 
  89   $lxdebug->leave_sub();
 
  93   $lxdebug->enter_sub();
 
  95   $auth->assert('part_service_assembly_edit');
 
  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
 
 101   my %is_xyz     = map { +"is_$_" => ($form->{searchitems} eq $_) } qw(part service assembly);
 
 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});
 
 107   $form->{jsscript} = 1;
 
 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   print $form->parse_html_template('ic/search', { %is_xyz,
 
 118                                                   dateformat => $myconfig{dateformat}, });
 
 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);
 
 132   print $form->parse_html_template('ic/search_update_prices', { PRICE_ROWS => $pricegroups });
 
 134   $lxdebug->leave_sub();
 
 137 sub confirm_price_update {
 
 138   $lxdebug->enter_sub();
 
 140   $auth->assert('part_service_assembly_edit');
 
 143   my $value_found = undef;
 
 145   foreach my $idx (qw(sellprice listprice), (1..$form->{price_rows})) {
 
 146     my $name      = $idx =~ m/\d/ ? $form->{"pricegroup_${idx}"}      : $idx eq 'sellprice' ? $locale->text('Sell Price') : $locale->text('List Price');
 
 147     my $type      = $idx =~ m/\d/ ? $form->{"pricegroup_type_${idx}"} : $form->{"${idx}_type"};
 
 148     my $value_idx = $idx =~ m/\d/ ? "price_${idx}" : $idx;
 
 149     my $value     = $form->parse_amount(\%myconfig, $form->{$value_idx});
 
 151     if ((0 > $value) && ($type eq 'percent')) {
 
 152       push @errors, $locale->text('You cannot adjust the price for pricegroup "#1" by a negative percentage.', $name);
 
 154     } elsif (!$value && ($form->{$value_idx} ne '')) {
 
 155       push @errors, $locale->text('No valid number entered for pricegroup "#1".', $name);
 
 157     } elsif (0 < $value) {
 
 162   push @errors, $locale->text('No prices will be updated because no prices have been entered.') if (!$value_found);
 
 164   my $num_matches = IC->get_num_matches_for_priceupdate();
 
 169     $form->show_generic_error(join('<br>', @errors), 'back_button' => 1);
 
 172   $form->{nextsub} = "update_prices";
 
 174   map { delete $form->{$_} } qw(action header);
 
 176   print $form->parse_html_template('ic/confirm_price_update', { HIDDENS     => [ map { name => $_, value => $form->{$_} }, keys %$form ],
 
 177                                                                 num_matches => $num_matches });
 
 179   $lxdebug->leave_sub();
 
 183   $lxdebug->enter_sub();
 
 185   $auth->assert('part_service_assembly_edit');
 
 187   my $num_updated = IC->update_prices(\%myconfig, \%$form);
 
 189   if (-1 != $num_updated) {
 
 190     $form->redirect($locale->text('#1 prices were updated.', $num_updated));
 
 192     $form->error($locale->text('Could not update prices!'));
 
 195   $lxdebug->leave_sub();
 
 199 #  $lxdebug->enter_sub();
 
 201 #  $auth->assert('part_service_assembly_edit');
 
 203 #  our ($j, $lastndx);
 
 206 #  $form->{title} = $locale->text('Top 100 hinzufuegen');
 
 210 #  push @custom_hiddens, qw(searchitems title bom titel revers lastsort sort ndxs_counter extras);
 
 211 #  push @custom_hiddens, qw(itemstatus l_linetotal l_partnumber l_description l_onhand l_unit l_sellprice l_linetotalsellprice);
 
 213 #        +{ name => 'row',     value => $j              },
 
 214 #        +{ name => 'nextsub', value => 'item_selected' },
 
 215 #        +{ name => 'test',    value => 'item_selected' },
 
 216 #        +{ name => 'lastndx', value => $lastndx        },
 
 217 #    map(+{ name => $_,        value => $form->{$_}     }, @custom_hiddens),
 
 220 #  my ($partnumber, $description, $unit, $sellprice, $soldtotal);
 
 221 #  # if choice set data
 
 222 ##  if ($form->{ndx}) {
 
 223 ##    for my $i (0 .. $form->{ndxs_counter}) {
 
 225 ##      # insert data into top100
 
 226 ##      push @{ $form->{parts} },
 
 228 ##          partnumber  => $form->{"totop100_partnumber_$j"},
 
 229 ##          description => $form->{"totop100_description_$j"},
 
 230 ##          unit        => $form->{"totop100_unit_$j"},
 
 231 ##          sellprice   => $form->{"totop100_sellprice_$j"},
 
 232 ##          soldtotal   => $form->{"totop100_soldtotal_$j"},
 
 239 #  # set data for next page
 
 240 #  for my $i (1 .. $form->{ndxs_counter}) {
 
 241 #    $partnumber  = $form->{"totop100_partnumber_$i"};
 
 242 #    $description = $form->{"totop100_description_$i"};
 
 243 #    $unit        = $form->{"totop100_unit_$i"};
 
 244 #    $sellprice   = $form->{"totop100_sellprice_$i"};
 
 245 #    $soldtotal   = $form->{"totop100_soldtotal_$i"};
 
 248 #    totop100_partnumber  => $form->{"totop100_partnumber_$i"},
 
 249 #    totop100_description => $form->{"totop100_description_$i"},
 
 250 #    totop100_unit        => $form->{"totop100_unit_$i"},
 
 251 #    totop100_sellprice   => $form->{"totop100_sellprice_$i"},
 
 252 #    totop100_soldtotal   => $form->{"totop100_soldtotal_$i"},
 
 256 ##<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
 
 257 ##<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
 
 258 ##<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
 
 259 ##<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
 
 260 ##<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
 
 264 #  print $form->parse_html_template('ic/choice', +{ HIDDENS => \@HIDDENS, PARTS => \@PARTS });
 
 266 #  $lxdebug->leave_sub();
 
 270 #  $lxdebug->enter_sub();
 
 272 #  $auth->assert('part_service_assembly_edit');
 
 275 #  our ($partnumber, $description, $unit, $sellprice, $soldtotal);
 
 277 #  my @sortorders = ("", "partnumber", "description", "all");
 
 278 #  my $sortorder = $sortorders[($form->{description} ? 2 : 0) + ($form->{partnumber} ? 1 : 0)];
 
 279 #  IC->get_parts(\%myconfig, \%$form, $sortorder);
 
 281 #  $form->{title} = $locale->text('Top 100 hinzufuegen');
 
 287 #  <form method=post action=ic.pl>
 
 290 #      <th class=listtop colspan=6>| . $locale->text('choice part') . qq|</th>
 
 292 #        <tr height="5"></tr>
 
 293 #        <tr class=listheading>
 
 295 #          <th class=listheading>| . $locale->text('Part Number') . qq|</th>
 
 296 #          <th class=listheading>| . $locale->text('Part Description') . qq|</th>
 
 297 #          <th class=listheading>| . $locale->text('Unit of measure') . qq|</th>
 
 298 #          <th class=listheading>| . $locale->text('Sell Price') . qq|</th>
 
 299 #          <th class=listheading>| . $locale->text('soldtotal') . qq|</th>
 
 303 #  my $i = $form->{rows};
 
 305 #  for ($j = 1; $j <= $i; $j++) {
 
 308 #        <tr class=listrow| . ($j % 2) . qq|>|;
 
 311 #            <td><input name=ndx class=radio type=radio value=$j checked></td>|;
 
 314 #          <td><input name=ndx class=radio type=radio value=$j></td>|;
 
 317 #          <td><input name="new_partnumber_$j" type=hidden value="$form->{"partnumber_$j"}">$form->{"partnumber_$j"}</td>
 
 318 #          <td><input name="new_description_$j" type=hidden value="$form->{"description_$j"}">$form->{"description_$j"}</td>
 
 319 #          <td><input name="new_unit_$j" type=hidden value="$form->{"unit_$j"}">$form->{"unit_$j"}</td>
 
 320 #          <td><input name="new_sellprice_$j" type=hidden value="$form->{"sellprice_$j"}">$form->{"sellprice_$j"}</td>
 
 321 #          <td><input name="new_soldtotal_$j" type=hidden value="$form->{"soldtotal_$j"}">$form->{"soldtotal_$j"}</td>
 
 324 #        <input name="new_id_$j" type=hidden value="$form->{"id_$j"}">|;
 
 334 #<input type=hidden name=itemstatus value="$form->{itemstatus}">
 
 335 #<input type=hidden name=l_linetotal value="$form->{l_linetotal}">
 
 336 #<input type=hidden name=l_partnumber value="$form->{l_partnumber}">
 
 337 #<input type=hidden name=l_description value="$form->{l_description}">
 
 338 #<input type=hidden name=l_onhand value="$form->{l_onhand}">
 
 339 #<input type=hidden name=l_unit value="$form->{l_unit}">
 
 340 #<input type=hidden name=l_sellprice value="$form->{l_sellprice}">
 
 341 #<input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
 
 342 #<input type=hidden name=sort value="$form->{sort}">
 
 343 #<input type=hidden name=revers value="$form->{revers}">
 
 344 #<input type=hidden name=lastsort value="$form->{lastsort}">
 
 346 #<input type=hidden name=bom value="$form->{bom}">
 
 347 #<input type=hidden name=titel value="$form->{titel}">
 
 348 #<input type=hidden name=searchitems value="$form->{searchitems}">
 
 350 #<input type=hidden name=row value=$j>
 
 352 #<input type=hidden name=nextsub value=item_selected>
 
 354 #<input name=lastndx type=hidden value=$lastndx>
 
 356 #<input name=ndxs_counter type=hidden value=$form->{ndxs_counter}>|;
 
 360 #  if (($form->{ndxs_counter}) > 0) {
 
 361 #    for ($i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
 
 363 #      $partnumber  = $form->{"totop100_partnumber_$i"};
 
 364 #      $description = $form->{"totop100_description_$i"};
 
 365 #      $unit        = $form->{"totop100_unit_$i"};
 
 366 #      $sellprice   = $form->{"totop100_sellprice_$i"};
 
 367 #      $soldtotal   = $form->{"totop100_soldtotal_$i"};
 
 370 #<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
 
 371 #<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
 
 372 #<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
 
 373 #<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
 
 374 #<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
 
 382 #<input class=submit type=submit name=action value="|
 
 383 #    . $locale->text('TOP100') . qq|">
 
 389 #  $lxdebug->leave_sub();
 
 393   $lxdebug->enter_sub();
 
 395   $auth->assert('part_service_assembly_edit');
 
 398     $form->{ndxs_counter}++;
 
 400     if ($form->{ndxs_counter} > 0) {
 
 402       my $index = $form->{ndx};
 
 404       $form->{"totop100_partnumber_$form->{ndxs_counter}"} = $form->{"new_partnumber_$index"};
 
 405       $form->{"totop100_description_$form->{ndxs_counter}"} = $form->{"new_description_$index"};
 
 406       $form->{"totop100_unit_$form->{ndxs_counter}"} = $form->{"new_unit_$index"};
 
 407       $form->{"totop100_sellprice_$form->{ndxs_counter}"} = $form->{"new_sellprice_$index"};
 
 408       $form->{"totop100_soldtotal_$form->{ndxs_counter}"} = $form->{"new_soldtotal_$index"};
 
 412   $lxdebug->leave_sub();
 
 416   $lxdebug->enter_sub();
 
 418   $auth->assert('part_service_assembly_edit');
 
 420   my ($revers, $lastsort, $callback, $option, $description, $sameitem,
 
 421       $partnumber, $unit, $sellprice, $soldtotal, $totop100, $onhand, $align);
 
 422   my (@column_index, %column_header, %column_data);
 
 423   my ($totalsellprice, $totallastcost, $totallistprice, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice);
 
 425   $form->{top100}      = "top100";
 
 426   $form->{l_soldtotal} = "Y";
 
 427   $form->{soldtotal}   = "soldtotal";
 
 428   $form->{sort}        = "soldtotal";
 
 429   $form->{l_qty}       = "N";
 
 430   $form->{l_linetotal} = "";
 
 432   $form->{number}      = "position";
 
 433   $form->{l_number}    = "Y";
 
 437   $form->{title} = $locale->text('Top 100');
 
 439   $revers   = $form->{revers};
 
 440   $lastsort = $form->{lastsort};
 
 442   if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
 
 444     $form->{lastsort} = "partnumber";
 
 445     $form->{sort}     = "partnumber";
 
 449     "$form->{script}?action=top100&searchitems=$form->{searchitems}&itemstatus=$form->{itemstatus}&bom=$form->{bom}&l_linetotal=$form->{l_linetotal}&title="
 
 450     . $form->escape($form->{title}, 1);
 
 452   # if we have a serialnumber limit search
 
 453   if ($form->{serialnumber} || $form->{l_serialnumber}) {
 
 454     $form->{l_serialnumber} = "Y";
 
 455     unless (   $form->{bought}
 
 458             || $form->{quoted}) {
 
 459       $form->{bought} = $form->{sold} = 1;
 
 462   IC->all_parts(\%myconfig, \%$form);
 
 464   if ($form->{itemstatus} eq 'active') {
 
 465     $option .= $locale->text('Active') . " : ";
 
 467   if ($form->{itemstatus} eq 'obsolete') {
 
 468     $option .= $locale->text('Obsolete') . " : ";
 
 470   if ($form->{itemstatus} eq 'orphaned') {
 
 471     $option .= $locale->text('Orphaned') . " : ";
 
 473   if ($form->{itemstatus} eq 'onhand') {
 
 474     $option .= $locale->text('On Hand') . " : ";
 
 475     $form->{l_onhand} = "Y";
 
 477   if ($form->{itemstatus} eq 'short') {
 
 478     $option .= $locale->text('Short') . " : ";
 
 479     $form->{l_onhand} = "Y";
 
 481   if ($form->{onorder}) {
 
 482     $form->{l_ordnumber} = "Y";
 
 483     $callback .= "&onorder=$form->{onorder}";
 
 484     $option   .= $locale->text('On Order') . " : ";
 
 486   if ($form->{ordered}) {
 
 487     $form->{l_ordnumber} = "Y";
 
 488     $callback .= "&ordered=$form->{ordered}";
 
 489     $option   .= $locale->text('Ordered') . " : ";
 
 492     $form->{l_quonumber} = "Y";
 
 493     $callback .= "&rfq=$form->{rfq}";
 
 494     $option   .= $locale->text('RFQ') . " : ";
 
 496   if ($form->{quoted}) {
 
 497     $form->{l_quonumber} = "Y";
 
 498     $callback .= ""ed=$form->{quoted}";
 
 499     $option   .= $locale->text('Quoted') . " : ";
 
 501   if ($form->{bought}) {
 
 502     $form->{l_invnumber} = "Y";
 
 503     $callback .= "&bought=$form->{bought}";
 
 504     $option   .= $locale->text('Bought') . " : ";
 
 507     $form->{l_invnumber} = "Y";
 
 508     $callback .= "&sold=$form->{sold}";
 
 509     $option   .= $locale->text('Sold') . " : ";
 
 516       || $form->{quoted}) {
 
 518     $form->{l_lastcost} = "";
 
 519     $form->{l_name}     = "Y";
 
 520     if ($form->{transdatefrom}) {
 
 521       $callback .= "&transdatefrom=$form->{transdatefrom}";
 
 523         . $locale->text('From')
 
 525         . $locale->date(\%myconfig, $form->{transdatefrom}, 1);
 
 527     if ($form->{transdateto}) {
 
 528       $callback .= "&transdateto=$form->{transdateto}";
 
 530         . $locale->text('To')
 
 532         . $locale->date(\%myconfig, $form->{transdateto}, 1);
 
 538   if ($form->{partnumber}) {
 
 539     $callback .= "&partnumber=$form->{partnumber}";
 
 540     $option   .= $locale->text('Part Number') . qq| : $form->{partnumber}<br>|;
 
 543     $callback .= "&partnumber=$form->{ean}";
 
 544     $option   .= $locale->text('EAN') . qq| : $form->{ean}<br>|;
 
 546   if ($form->{partsgroup}) {
 
 547     $callback .= "&partsgroup=$form->{partsgroup}";
 
 548     $option   .= $locale->text('Group') . qq| : $form->{partsgroup}<br>|;
 
 550   if ($form->{serialnumber}) {
 
 551     $callback .= "&serialnumber=$form->{serialnumber}";
 
 552     $option   .= $locale->text('Serial Number') . qq| : $form->{serialnumber}<br>|;
 
 554   if ($form->{description}) {
 
 555     $callback   .= "&description=$form->{description}";
 
 556     $description = $form->{description};
 
 557     $description =~ s/\n/<br>/g;
 
 558     $option     .= $locale->text('Part Description') . qq| : $form->{description}<br>|;
 
 561     $callback .= "&make=$form->{make}";
 
 562     $option   .= $locale->text('Make') . qq| : $form->{make}<br>|;
 
 564   if ($form->{model}) {
 
 565     $callback .= "&model=$form->{model}";
 
 566     $option   .= $locale->text('Model') . qq| : $form->{model}<br>|;
 
 568   if ($form->{drawing}) {
 
 569     $callback .= "&drawing=$form->{drawing}";
 
 570     $option   .= $locale->text('Drawing') . qq| : $form->{drawing}<br>|;
 
 572   if ($form->{microfiche}) {
 
 573     $callback .= "µfiche=$form->{microfiche}";
 
 574     $option   .= $locale->text('Microfiche') . qq| : $form->{microfiche}<br>|;
 
 576   if ($form->{l_soldtotal}) {
 
 577     $callback .= "&soldtotal=$form->{soldtotal}";
 
 578     $option   .= $locale->text('soldtotal') . qq| : $form->{soldtotal}<br>|;
 
 581   my @columns = $form->sort_columns(
 
 582     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   if ($form->{l_linetotal}) {
 
 586     $form->{l_onhand} = "Y";
 
 587     $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
 
 588     if ($form->{l_lastcost}) {
 
 589       $form->{l_linetotallastcost} = "Y";
 
 590       if (($form->{searchitems} eq 'assembly') && !$form->{bom}) {
 
 591         $form->{l_linetotallastcost} = "";
 
 594     $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
 
 597   if ($form->{searchitems} eq 'service') {
 
 599     # remove bin, weight and rop from list
 
 600     map { $form->{"l_$_"} = "" } qw(bin weight rop);
 
 602     $form->{l_onhand} = "";
 
 604     # qty is irrelevant unless bought or sold
 
 610         || $form->{quoted}) {
 
 611       $form->{l_onhand} = "Y";
 
 613       $form->{l_linetotalsellprice} = "";
 
 614       $form->{l_linetotallastcost}  = "";
 
 618   foreach my $item (@columns) {
 
 619     if ($form->{"l_$item"} eq "Y") {
 
 620       push @column_index, $item;
 
 622       # add column to callback
 
 623       $callback .= "&l_$item=Y";
 
 627   if ($form->{l_subtotal} eq 'Y') {
 
 628     $callback .= "&l_subtotal=Y";
 
 631   $column_header{number} =
 
 632     qq|<th class=listheading nowrap>| . $locale->text('number') . qq|</th>|;
 
 633   $column_header{partnumber} =
 
 634     qq|<th nowrap><a class=listheading href=$callback&sort=partnumber&revers=$form->{revers}&lastsort=$form->{lastsort}>|
 
 635     . $locale->text('Part Number')
 
 637   $column_header{description} =
 
 638     qq|<th nowrap><a class=listheading href=$callback&sort=description&revers=$form->{revers}&lastsort=$form->{lastsort}>|
 
 639     . $locale->text('Part Description')
 
 641   $column_header{partsgroup} =
 
 642       qq|<th nowrap><a class=listheading href=$callback&sort=partsgroup>|
 
 643     . $locale->text('Group')
 
 645   $column_header{bin} =
 
 646       qq|<th><a class=listheading href=$callback&sort=bin>|
 
 647     . $locale->text('Bin')
 
 649   $column_header{priceupdate} =
 
 650       qq|<th nowrap><a class=listheading href=$callback&sort=priceupdate>|
 
 651     . $locale->text('Updated')
 
 653   $column_header{onhand} =
 
 654     qq|<th nowrap><a  class=listheading href=$callback&sort=onhand&revers=$form->{revers}&lastsort=$form->{lastsort}>|
 
 655     . $locale->text('Qty')
 
 657   $column_header{unit} =
 
 658     qq|<th class=listheading nowrap>| . $locale->text('Unit') . qq|</th>|;
 
 659   $column_header{listprice} =
 
 660       qq|<th class=listheading nowrap>|
 
 661     . $locale->text('List Price')
 
 663   $column_header{lastcost} =
 
 664     qq|<th class=listheading nowrap>| . $locale->text('Last Cost') . qq|</th>|;
 
 665   $column_header{rop} =
 
 666     qq|<th class=listheading nowrap>| . $locale->text('ROP') . qq|</th>|;
 
 667   $column_header{weight} =
 
 668     qq|<th class=listheading nowrap>| . $locale->text('Weight') . qq|</th>|;
 
 670   $column_header{invnumber} =
 
 671       qq|<th nowrap><a class=listheading href=$callback&sort=invnumber>|
 
 672     . $locale->text('Invoice Number')
 
 674   $column_header{ordnumber} =
 
 675       qq|<th nowrap><a class=listheading href=$callback&sort=ordnumber>|
 
 676     . $locale->text('Order Number')
 
 678   $column_header{quonumber} =
 
 679       qq|<th nowrap><a class=listheading href=$callback&sort=quonumber>|
 
 680     . $locale->text('Quotation')
 
 683   $column_header{name} =
 
 684       qq|<th nowrap><a class=listheading href=$callback&sort=name>|
 
 685     . $locale->text('Name')
 
 688   $column_header{sellprice} =
 
 689       qq|<th class=listheading nowrap>|
 
 690     . $locale->text('Sell Price')
 
 692   $column_header{linetotalsellprice} =
 
 693     qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
 
 694   $column_header{linetotallastcost} =
 
 695     qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
 
 696   $column_header{linetotallistprice} =
 
 697     qq|<th class=listheading nowrap>| . $locale->text('Extended') . qq|</th>|;
 
 699   $column_header{image} =
 
 700     qq|<th class=listheading nowrap>| . $locale->text('Image') . qq|</a></th>|;
 
 701   $column_header{drawing} =
 
 702       qq|<th nowrap><a class=listheading href=$callback&sort=drawing>|
 
 703     . $locale->text('Drawing')
 
 705   $column_header{microfiche} =
 
 706       qq|<th nowrap><a class=listheading href=$callback&sort=microfiche>|
 
 707     . $locale->text('Microfiche')
 
 710   $column_header{serialnumber} =
 
 711       qq|<th nowrap><a class=listheading href=$callback&sort=serialnumber>|
 
 712     . $locale->text('Serial Number')
 
 714   $column_header{soldtotal} =
 
 715     qq|<th nowrap><a class=listheading href=$callback&sort=soldtotal&revers=$form->{revers}&lastsort=$form->{lastsort}>|
 
 716     . $locale->text('soldtotal')
 
 720   my $colspan = $#column_index + 1;
 
 727     <th class=listtop colspan=$colspan>$form->{title}</th>
 
 731   <tr><td colspan=$colspan>$option</td></tr>
 
 733   <tr class=listheading>
 
 736   map { print "\n$column_header{$_}" } @column_index;
 
 742   # add order to callback
 
 743   $form->{callback} = $callback .= "&sort=$form->{sort}";
 
 745   # escape callback for href
 
 746   $callback = $form->escape($callback);
 
 748   if (@{ $form->{parts} }) {
 
 749     $sameitem = $form->{parts}->[0]->{ $form->{sort} };
 
 752   # insert numbers for top100
 
 754   foreach my $ref (@{ $form->{parts} }) {
 
 759   # if avaible -> insert choice here
 
 760   if (($form->{ndxs_counter}) > 0) {
 
 761     for (my $i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
 
 762       $partnumber  = $form->{"totop100_partnumber_$i"};
 
 763       $description = $form->{"totop100_description_$i"};
 
 764       $unit        = $form->{"totop100_unit_$i"};
 
 765       $sellprice   = $form->{"totop100_sellprice_$i"};
 
 766       $soldtotal   = $form->{"totop100_soldtotal_$i"};
 
 769 <input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
 
 770 <input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
 
 771 <input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
 
 772 <input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
 
 773 <input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
 
 777       push @{ $form->{parts} },
 
 779           partnumber  => "$partnumber",
 
 780           description => "$description",
 
 782           sellprice   => "$sellprice",
 
 783           soldtotal   => "$soldtotal" };
 
 786        # build data for columns
 
 788   foreach my $ref (@{ $form->{parts} }) {
 
 790     if ($form->{l_subtotal} eq 'Y' && !$ref->{assemblyitem}) {
 
 791       if ($sameitem ne $ref->{ $form->{sort} }) {
 
 793         $sameitem = $ref->{ $form->{sort} };
 
 797     $ref->{exchangerate} = 1 unless $ref->{exchangerate};
 
 798     $ref->{sellprice} *= $ref->{exchangerate};
 
 799     $ref->{listprice} *= $ref->{exchangerate};
 
 800     $ref->{lastcost}  *= $ref->{exchangerate};
 
 802     # use this for assemblies
 
 803     $onhand = $ref->{onhand};
 
 806     if ($ref->{assemblyitem}) {
 
 808       $onhand = 0 if ($form->{sold});
 
 811     $ref->{description} =~ s/\n/<br>/g;
 
 813     $column_data{number} =
 
 815       . $form->format_amount(\%myconfig, $ref->{number})
 
 817     $column_data{partnumber} =
 
 818       "<td align=$align>$ref->{partnumber} </a></td>";
 
 819     $column_data{description} = "<td>$ref->{description} </td>";
 
 820     $column_data{partsgroup}  = "<td>$ref->{partsgroup} </td>";
 
 822     $column_data{onhand} =
 
 824       . $form->format_amount(\%myconfig, $ref->{onhand})
 
 826     $column_data{sellprice} =
 
 828       . $form->format_amount(\%myconfig, $ref->{sellprice})
 
 830     $column_data{listprice} =
 
 832       . $form->format_amount(\%myconfig, $ref->{listprice})
 
 834     $column_data{lastcost} =
 
 836       . $form->format_amount(\%myconfig, $ref->{lastcost})
 
 839     $column_data{linetotalsellprice} = "<td align=right>"
 
 840       . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{sellprice}, 2)
 
 842     $column_data{linetotallastcost} = "<td align=right>"
 
 843       . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{lastcost}, 2)
 
 845     $column_data{linetotallistprice} = "<td align=right>"
 
 846       . $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{listprice}, 2)
 
 849     if (!$ref->{assemblyitem}) {
 
 850       $totalsellprice += $onhand * $ref->{sellprice};
 
 851       $totallastcost  += $onhand * $ref->{lastcost};
 
 852       $totallistprice += $onhand * $ref->{listprice};
 
 854       $subtotalonhand    += $onhand;
 
 855       $subtotalsellprice += $onhand * $ref->{sellprice};
 
 856       $subtotallastcost  += $onhand * $ref->{lastcost};
 
 857       $subtotallistprice += $onhand * $ref->{listprice};
 
 862       . $form->format_amount(\%myconfig, $ref->{rop}) . "</td>";
 
 863     $column_data{weight} =
 
 865       . $form->format_amount(\%myconfig, $ref->{weight})
 
 867     $column_data{unit}        = "<td>$ref->{unit} </td>";
 
 868     $column_data{bin}         = "<td>$ref->{bin} </td>";
 
 869     $column_data{priceupdate} = "<td>$ref->{priceupdate} </td>";
 
 871     $column_data{invnumber} =
 
 872       ($ref->{module} ne 'oe')
 
 873       ? "<td><a href=$ref->{module}.pl?action=edit&type=invoice&id=$ref->{trans_id}&callback=$callback>$ref->{invnumber}</a></td>"
 
 874       : "<td>$ref->{invnumber}</td>";
 
 875     $column_data{ordnumber} =
 
 876       ($ref->{module} eq 'oe')
 
 877       ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{ordnumber}</a></td>"
 
 878       : "<td>$ref->{ordnumber}</td>";
 
 879     $column_data{quonumber} =
 
 880       ($ref->{module} eq 'oe' && !$ref->{ordnumber})
 
 881       ? "<td><a href=$ref->{module}.pl?action=edit&type=$ref->{type}&id=$ref->{trans_id}&callback=$callback>$ref->{quonumber}</a></td>"
 
 882       : "<td>$ref->{quonumber}</td>";
 
 884     $column_data{name} = "<td>$ref->{name}</td>";
 
 886     $column_data{image} =
 
 888       ? "<td><a href=$ref->{image}><img src=$ref->{image} height=32 border=0></a></td>"
 
 890     $column_data{drawing} =
 
 892       ? "<td><a href=$ref->{drawing}>$ref->{drawing}</a></td>"
 
 894     $column_data{microfiche} =
 
 896       ? "<td><a href=$ref->{microfiche}>$ref->{microfiche}</a></td>"
 
 899     $column_data{serialnumber} = "<td>$ref->{serialnumber}</td>";
 
 901     $column_data{soldtotal} = "<td  align=right>$ref->{soldtotal}</td>";
 
 905     print "<tr class=listrow$i>";
 
 907     map { print "\n$column_data{$_}" } @column_index;
 
 914   if ($form->{l_subtotal} eq 'Y') {
 
 918   if ($form->{"l_linetotal"}) {
 
 919     map { $column_data{$_} = "<td> </td>" } @column_index;
 
 920     $column_data{linetotalsellprice} =
 
 921         "<th class=listtotal align=right>"
 
 922       . $form->format_amount(\%myconfig, $totalsellprice, 2)
 
 924     $column_data{linetotallastcost} =
 
 925         "<th class=listtotal align=right>"
 
 926       . $form->format_amount(\%myconfig, $totallastcost, 2)
 
 928     $column_data{linetotallistprice} =
 
 929         "<th class=listtotal align=right>"
 
 930       . $form->format_amount(\%myconfig, $totallistprice, 2)
 
 933     print "<tr class=listtotal>";
 
 935     map { print "\n$column_data{$_}" } @column_index;
 
 942   <tr><td colspan=$colspan><hr size=3 noshade></td></tr>
 
 951 <form method=post action=$form->{script}>
 
 953 <input type=hidden name=itemstatus value="$form->{itemstatus}">
 
 954 <input type=hidden name=l_linetotal value="$form->{l_linetotal}">
 
 955 <input type=hidden name=l_partnumber value="$form->{l_partnumber}">
 
 956 <input type=hidden name=l_description value="$form->{l_description}">
 
 957 <input type=hidden name=l_onhand value="$form->{l_onhand}">
 
 958 <input type=hidden name=l_unit value="$form->{l_unit}">
 
 959 <input type=hidden name=l_sellprice value="$form->{l_sellprice}">
 
 960 <input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
 
 961 <input type=hidden name=sort value="$form->{sort}">
 
 962 <input type=hidden name=revers value="$form->{revers}">
 
 963 <input type=hidden name=lastsort value="$form->{lastsort}">
 
 964 <input type=hidden name=parts value="$form->{parts}">
 
 966 <input type=hidden name=bom value="$form->{bom}">
 
 967 <input type=hidden name=titel value="$form->{titel}">
 
 968 <input type=hidden name=searchitems value="$form->{searchitems}">|;
 
 973 <!--    <input type=hidden name=ndxs_counter value="$form->{ndxs_counter}">-->
 
 975     <input class=submit type=submit name=action value="|
 
 976     . $locale->text('choice') . qq|">
 
 984   $lxdebug->leave_sub();
 
 989 # Warning, deep magic ahead.
 
 990 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
 
 992 # flags coming from the form:
 
 994 #  searchitems=part revers=0 lastsort=''
 
 997 # partnumber ean description partsgroup serialnumber make model drawing microfiche
 
 998 # transdatefrom transdateto
 
1001 #  itemstatus = active | onhand | short | obsolete | orphaned
 
1002 #  action     = continue | top100
 
1005 #  bought sold onorder ordered rfq quoted
 
1006 #  l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
 
1007 #  l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
 
1008 #  l_partsgroup l_subtotal l_soldtotal l_deliverydate
 
1011 #  nextsub revers lastsort sort ndxs_counter
 
1013 sub generate_report {
 
1014   $lxdebug->enter_sub();
 
1016   $auth->assert('part_service_assembly_edit');
 
1018   my ($revers, $lastsort, $description);
 
1020   my $cvar_configs = CVar->get_configs('module' => 'IC');
 
1022   $form->{title} = (ucfirst $form->{searchitems}) . "s";
 
1023   $form->{title} =~ s/ys$/ies/;
 
1024   $form->{title} = $locale->text($form->{title});
 
1027     'bin'                => { 'text' => $locale->text('Bin'), },
 
1028     'deliverydate'       => { 'text' => $locale->text('deliverydate'), },
 
1029     'description'        => { 'text' => $locale->text('Part Description'), },
 
1030     'drawing'            => { 'text' => $locale->text('Drawing'), },
 
1031     'image'              => { 'text' => $locale->text('Image'), },
 
1032     'invnumber'          => { 'text' => $locale->text('Invoice Number'), },
 
1033     'lastcost'           => { 'text' => $locale->text('Last Cost'), },
 
1034     'linetotallastcost'  => { 'text' => $locale->text('Extended'), },
 
1035     'linetotallistprice' => { 'text' => $locale->text('Extended'), },
 
1036     'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
 
1037     'listprice'          => { 'text' => $locale->text('List Price'), },
 
1038     'microfiche'         => { 'text' => $locale->text('Microfiche'), },
 
1039     'name'               => { 'text' => $locale->text('Name'), },
 
1040     'onhand'             => { 'text' => $locale->text('Qty'), },
 
1041     'ordnumber'          => { 'text' => $locale->text('Order Number'), },
 
1042     'partnumber'         => { 'text' => $locale->text('Part Number'), },
 
1043     'partsgroup'         => { 'text' => $locale->text('Group'), },
 
1044     'priceupdate'        => { 'text' => $locale->text('Updated'), },
 
1045     'quonumber'          => { 'text' => $locale->text('Quotation'), },
 
1046     'rop'                => { 'text' => $locale->text('ROP'), },
 
1047     'sellprice'          => { 'text' => $locale->text('Sell Price'), },
 
1048     'serialnumber'       => { 'text' => $locale->text('Serial Number'), },
 
1049     'soldtotal'          => { 'text' => $locale->text('soldtotal'), },
 
1050     'transdate'          => { 'text' => $locale->text('Transdate'), },
 
1051     'unit'               => { 'text' => $locale->text('Unit'), },
 
1052     'weight'             => { 'text' => $locale->text('Weight'), },
 
1055   $revers     = $form->{revers};
 
1056   $lastsort   = $form->{lastsort};
 
1058   # sorting and direction of sorting
 
1059   # ToDO: change this to the simpler field+direction method
 
1060   if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
 
1061     $form->{revers}   = 0;
 
1062     $form->{lastsort} = "partnumber";
 
1063     $form->{sort}     = "partnumber";
 
1065     if ($form->{lastsort} eq $form->{sort}) {
 
1066       $form->{revers} = 1 - $form->{revers};
 
1068       $form->{revers} = 0;
 
1069       $form->{lastsort} = $form->{sort};
 
1073   # special case if we have a serialnumber limit search
 
1074   # serialnumbers are only given in invoices and orders,
 
1075   # so they can only pop up in bought, sold, rfq, and quoted stuff
 
1076   $form->{no_sn_joins} = 'Y' if (   !$form->{bought} && !$form->{sold}
 
1077                                  && !$form->{rfq}    && !$form->{quoted}
 
1078                                  && ($form->{l_serialnumber} || $form->{serialnumber}));
 
1080   # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
 
1081   # if any of these are ticked the behavior changes slightly for lastcost
 
1082   # since all those are aggregation checks for the legder tables this is an internal switch
 
1083   # refered to as ledgerchecks
 
1084   $form->{ledgerchecks} = 'Y' if (   $form->{bought} || $form->{sold} || $form->{onorder}
 
1085                                   || $form->{ordered} || $form->{rfq} || $form->{quoted});
 
1087   # if something should be activated if something else is active, enter it here
 
1088   my %dependencies = (
 
1089     onhand       => [ qw(l_onhand) ],
 
1090     short        => [ qw(l_onhand) ],
 
1091     onorder      => [ qw(l_ordnumber) ],
 
1092     ordered      => [ qw(l_ordnumber) ],
 
1093     rfq          => [ qw(l_quonumber) ],
 
1094     quoted       => [ qw(l_quonumber) ],
 
1095     bought       => [ qw(l_invnumber) ],
 
1096     sold         => [ qw(l_invnumber) ],
 
1097     ledgerchecks => [ qw(l_name) ],
 
1098     serialnumber => [ qw(l_serialnumber) ],
 
1099     no_sn_joins  => [ qw(bought sold) ],
 
1102   # these strings get displayed at the top of the results to indicate the user which switches were used
 
1104     active        => $locale->text('Active'),
 
1105     obsolete      => $locale->text('Obsolete'),
 
1106     orphaned      => $locale->text('Orphaned'),
 
1107     onhand        => $locale->text('On Hand'),
 
1108     short         => $locale->text('Short'),
 
1109     onorder       => $locale->text('On Order'),
 
1110     ordered       => $locale->text('Ordered'),
 
1111     rfq           => $locale->text('RFQ'),
 
1112     quoted        => $locale->text('Quoted'),
 
1113     bought        => $locale->text('Bought'),
 
1114     sold          => $locale->text('Sold'),
 
1115     transdatefrom => $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
 
1116     transdateto   => $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
 
1117     partnumber    => $locale->text('Part Number')      . ": '$form->{partnumber}'",
 
1118     partsgroup    => $locale->text('Group')            . ": '$form->{partsgroup}'",
 
1119     serialnumber  => $locale->text('Serial Number')    . ": '$form->{serialnumber}'",
 
1120     description   => $locale->text('Part Description') . ": '$form->{description}'",
 
1121     make          => $locale->text('Make')             . ": '$form->{make}'",
 
1122     model         => $locale->text('Model')            . ": '$form->{model}'",
 
1123     drawing       => $locale->text('Drawing')          . ": '$form->{drawing}'",
 
1124     microfiche    => $locale->text('Microfiche')       . ": '$form->{microfiche}'",
 
1125     l_soldtotal   => $locale->text('soldtotal'),
 
1128   my @itemstatus_keys = qw(active obsolete orphaned onhand short);
 
1129   my @callback_keys   = qw(onorder ordered rfq quoted bought sold partnumber partsgroup serialnumber description make model
 
1130                            drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto ean);
 
1132   # calculate dependencies
 
1133   for (@itemstatus_keys, @callback_keys) {
 
1134     next if ($form->{itemstatus} ne $_ && !$form->{$_});
 
1135     map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
 
1138   # generate callback and optionstrings
 
1140   for my  $key (@itemstatus_keys, @callback_keys) {
 
1141     next if ($form->{itemstatus} ne $key && !$form->{$key});
 
1142     push @options, $optiontexts{$key};
 
1145   # special case for lastcost
 
1146   if ($form->{ledgerchecks}){
 
1147     # zumindestens für den haken 'gekauft' muss das verhalten
 
1148     # so sein, das der Verkaufspreis nicht angezeigt
 
1149     # wird. In der Backend-Funktion all_parts wird nur mit
 
1151     $column_defs{sellprice}{text} = $locale->text('Price');
 
1152     $form->{l_lastcost} = "" 
 
1155   if ($form->{description}) {
 
1156     $description = $form->{description};
 
1157     $description =~ s/\n/<br>/g;
 
1160   if ($form->{l_linetotal}) {
 
1161     $form->{l_onhand} = "Y";
 
1162     $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
 
1163     $form->{l_linetotallastcost}  = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if  $form->{l_lastcost};
 
1164     $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
 
1167   if ($form->{searchitems} eq 'service') {
 
1169     # remove bin, weight and rop from list
 
1170     map { $form->{"l_$_"} = "" } qw(bin weight rop);
 
1172     $form->{l_onhand} = "";
 
1174     # qty is irrelevant unless bought or sold
 
1175     if (   $form->{bought}
 
1180         || $form->{quoted}) {
 
1181       $form->{l_onhand} = "Y";
 
1183       $form->{l_linetotalsellprice} = "";
 
1184       $form->{l_linetotallastcost}  = "";
 
1188   IC->all_parts(\%myconfig, \%$form);
 
1191     partnumber description partsgroup bin onhand rop unit listprice
 
1192     linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
 
1193     priceupdate weight image drawing microfiche invnumber ordnumber quonumber
 
1194     transdate name serialnumber soldtotal deliverydate
 
1197   my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
 
1198   my @searchable_custom_variables  = grep { $_->{searchable} }  @{ $cvar_configs };
 
1199   my %column_defs_cvars            = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
 
1201   push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
 
1203   %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);
 
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);
 
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);
 
1214   foreach my $col (@sort_full) {
 
1215     $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
 
1217   map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
 
1219   # add order to callback
 
1220   $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
 
1222   my $report = SL::ReportGenerator->new(\%myconfig, $form);
 
1224   my %attachment_basenames = (
 
1225     'part'     => $locale->text('part_list'),
 
1226     'service'  => $locale->text('service_list'),
 
1227     'assembly' => $locale->text('assembly_list'),
 
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),
 
1236   $report->set_options_from_form();
 
1238   $report->set_columns(%column_defs);
 
1239   $report->set_column_order(@columns);
 
1241   $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
 
1243   $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
 
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});
 
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});
 
1258   my @subtotal_columns = qw(sellprice listprice lastcost);
 
1259   my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
 
1260   my %totals    = map { $_ => 0 } @subtotal_columns;
 
1262   my $same_item = $form->{parts}[0]{ $form->{sort} } if (scalar @{ $form->{parts} });
 
1264   my $defaults  = AM->get_defaults();
 
1267   foreach my $ref (@{ $form->{parts} }) {
 
1269     # fresh row, for inserting later
 
1270     my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
 
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};
 
1278     # use this for assemblies
 
1279     my $onhand = $ref->{onhand};
 
1281     if ($ref->{assemblyitem}) {
 
1282       $row->{partnumber}{align}   = 'right';
 
1283       $row->{onhand}{data}        = 0;
 
1284       $onhand                     = 0 if ($form->{sold});
 
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;
 
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);
 
1296     map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
 
1298     $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
 
1300     if (!$ref->{assemblyitem}) {
 
1301       foreach my $col (@subtotal_columns) {
 
1302         $totals{$col}    += $onhand * $ref->{$col};
 
1303         $subtotals{$col} += $onhand * $ref->{$col};
 
1306       $subtotals{onhand} += $onhand;
 
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});
 
1316       $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback');
 
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>';
 
1324     map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
 
1326     $report->add_data($row);
 
1328     my $next_ref = $form->{parts}[$idx + 1];
 
1330     # insert subtotal rows
 
1331     if (($form->{l_subtotal} eq 'Y') &&
 
1333          (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
 
1334       my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
 
1336       if (($form->{searchitems} ne 'assembly') || !$form->{bom}) {
 
1337         $row->{onhand}->{data} = $form->format_amount(\%myconfig, $subtotals{onhand});
 
1340       map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
 
1341       map { $subtotals{$_} = 0 } ('onhand', @subtotal_columns);
 
1343       $report->add_data($row);
 
1345       $same_item = $next_ref->{ $form->{sort} };
 
1351   if ($form->{"l_linetotal"}) {
 
1352     my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
 
1354     map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
 
1356     $report->add_separator();
 
1357     $report->add_data($row);
 
1360   $report->generate_with_headers();
 
1362   $lxdebug->leave_sub();
 
1363 }    #end generate_report
 
1365 sub parts_subtotal {
 
1366   $lxdebug->enter_sub();
 
1368   $auth->assert('part_service_assembly_edit');
 
1371   our (%column_data, @column_index);
 
1372   our ($subtotalonhand, $totalsellprice, $totallastcost, $totallistprice, $subtotalsellprice, $subtotallastcost, $subtotallistprice);
 
1374   map { $column_data{$_} = "<td> </td>" } @column_index;
 
1375   $subtotalonhand = 0 if ($form->{searchitems} eq 'assembly' && $form->{bom});
 
1377   $column_data{onhand} =
 
1378       "<th class=listsubtotal align=right>"
 
1379     . $form->format_amount(\%myconfig, $subtotalonhand)
 
1382   $column_data{linetotalsellprice} =
 
1383       "<th class=listsubtotal align=right>"
 
1384     . $form->format_amount(\%myconfig, $subtotalsellprice, 2)
 
1386   $column_data{linetotallistprice} =
 
1387       "<th class=listsubtotal align=right>"
 
1388     . $form->format_amount(\%myconfig, $subtotallistprice, 2)
 
1390   $column_data{linetotallastcost} =
 
1391       "<th class=listsubtotal align=right>"
 
1392     . $form->format_amount(\%myconfig, $subtotallastcost, 2)
 
1395   $subtotalonhand    = 0;
 
1396   $subtotalsellprice = 0;
 
1397   $subtotallistprice = 0;
 
1398   $subtotallastcost  = 0;
 
1400   print "<tr class=listsubtotal>";
 
1402   map { print "\n$column_data{$_}" } @column_index;
 
1408   $lxdebug->leave_sub();
 
1412   $lxdebug->enter_sub();
 
1414   $auth->assert('part_service_assembly_edit');
 
1416   # show history button
 
1417   $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
 
1418   #/show hhistory button
 
1419   IC->get_part(\%myconfig, \%$form);
 
1421   $form->{"original_partnumber"} = $form->{"partnumber"};
 
1423   $form->{title} = $locale->text('Edit ' . ucfirst $form->{item});
 
1428   $lxdebug->leave_sub();
 
1432   $lxdebug->enter_sub();
 
1434   $auth->assert('part_service_assembly_edit');
 
1436   IC->create_links("IC", \%myconfig, \%$form);
 
1439   map({ $form->{selectcurrency} .= "<option>$_\n" }
 
1440       split(/:/, $form->{currencies}));
 
1442   # parts and assemblies have the same links
 
1443   my $item = $form->{item};
 
1444   if ($form->{item} eq 'assembly') {
 
1448   # build the popup menus
 
1449   $form->{taxaccounts} = "";
 
1450   foreach my $key (keys %{ $form->{IC_links} }) {
 
1451     foreach my $ref (@{ $form->{IC_links}{$key} }) {
 
1453       # if this is a tax field
 
1454       if ($key =~ /IC_tax/) {
 
1455         if ($key =~ /\Q$item\E/) {
 
1456           $form->{taxaccounts} .= "$ref->{accno} ";
 
1457           $form->{"IC_tax_$ref->{accno}_description"} =
 
1458             "$ref->{accno}--$ref->{description}";
 
1461             if ($form->{amount}{ $ref->{accno} }) {
 
1462               $form->{"IC_tax_$ref->{accno}"} = "checked";
 
1465             $form->{"IC_tax_$ref->{accno}"} = "checked";
 
1470         $form->{"select$key"} .=
 
1471           "<option $ref->{selected}>$ref->{accno}--$ref->{description}\n";
 
1472         if ($form->{amount}{$key} eq $ref->{accno}) {
 
1473           $form->{$key} = "$ref->{accno}--$ref->{description}";
 
1479   chop $form->{taxaccounts};
 
1481   if (($form->{item} eq "part") || ($form->{item} eq "assembly")) {
 
1482     $form->{selectIC_income}  = $form->{selectIC_sale};
 
1483     $form->{selectIC_expense} = $form->{selectIC_cogs};
 
1484     $form->{IC_income}        = $form->{IC_sale};
 
1485     $form->{IC_expense}       = $form->{IC_cogs};
 
1488   delete $form->{IC_links};
 
1489   delete $form->{amount};
 
1491   $form->get_partsgroup(\%myconfig, { all => 1 });
 
1493   $form->{partsgroup} = "$form->{partsgroup}--$form->{partsgroup_id}";
 
1495   if (@{ $form->{all_partsgroup} }) {
 
1496     $form->{selectpartsgroup} = qq|<option>\n|;
 
1497     map { $form->{selectpartsgroup} .= qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n| } @{ $form->{all_partsgroup} };
 
1500   if ($form->{item} eq 'assembly') {
 
1502     foreach my $i (1 .. $form->{assembly_rows}) {
 
1503       if ($form->{"partsgroup_id_$i"}) {
 
1504         $form->{"partsgroup_$i"} =
 
1505           qq|$form->{"partsgroup_$i"}--$form->{"partsgroup_id_$i"}|;
 
1508     $form->get_partsgroup(\%myconfig);
 
1510     if (@{ $form->{all_partsgroup} }) {
 
1511       $form->{selectassemblypartsgroup} = qq|<option>\n|;
 
1514         $form->{selectassemblypartsgroup} .=
 
1515           qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n|
 
1516       } @{ $form->{all_partsgroup} };
 
1519   $lxdebug->leave_sub();
 
1523   $lxdebug->enter_sub();
 
1525   $auth->assert('part_service_assembly_edit');
 
1527   $form->{eur}              = $main::eur; # config dumps into namespace - yuck
 
1528   $form->{pg_keys}          = sub { "$_[0]->{partsgroup}--$_[0]->{id}" };
 
1529   $form->{description_area} = ($form->{rows} = $form->numtextrows($form->{description}, 40)) > 1;
 
1530   $form->{notes_rows}       =  max 4, $form->numtextrows($form->{notes}, 40), $form->numtextrows($form->{formel}, 40);
 
1532   map { $form->{"is_$_"}  = ($form->{item} eq $_) } qw(part service assembly);
 
1533   map { $form->{$_}       =~ s/"/"/g;        } qw(unit);
 
1535   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
 
1536                    'partsgroup'    => 'all_partsgroup',
 
1537                    'vendors'       => 'ALL_VENDORS',);
 
1540   IC->retrieve_buchungsgruppen(\%myconfig, $form);
 
1541   @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
 
1543   # use JavaScript Calendar or not (yes!)
 
1544   $form->{jsscript} = 1;
 
1546   my $units = AM->retrieve_units(\%myconfig, $form);
 
1547   $form->{ALL_UNITS} = [ map +{ name => $_ }, sort { $units->{$a}{sortkey} <=> $units->{$b}{sortkey} } keys %$units ];
 
1549   $form->{defaults} = AM->get_defaults();
 
1551   $form->{fokus} = "ic.partnumber";
 
1553   $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'IC', 'trans_id' => $form->{id});
 
1555   CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}, show_disabled_message => 1)
 
1556     if (scalar @{ $form->{CUSTOM_VARIABLES} });
 
1559   #print $form->parse_html_template('ic/form_header', { ALL_PRICE_FACTORS => $form->{ALL_PRICE_FACTORS},
 
1560   #                                                     ALL_UNITS         => $form->{ALL_UNITS},
 
1561   #                                                     BUCHUNGSGRUPPEN   => $form->{BUCHUNGSGRUPPEN},
 
1562   #                                                     payment_terms     => $form->{payment_terms},
 
1563   #                                                     all_partsgroup    => $form->{all_partsgroup}});
 
1564   print $form->parse_html_template('ic/form_header');
 
1565   $lxdebug->leave_sub();
 
1569   $lxdebug->enter_sub();
 
1571   $auth->assert('part_service_assembly_edit');
 
1573   print $form->parse_html_template('ic/form_footer');
 
1575   $lxdebug->leave_sub();
 
1579   $lxdebug->enter_sub();
 
1582   my @mm_data = grep { any { $_ ne '' } @$_{qw(make model)} } map +{ make => $form->{"make_$_"}, model => $form->{"model_$_"} }, 1 .. $numrows;
 
1583   delete @{$form}{grep { m/^make_\d+/ || m/^model_\d+/ } keys %{ $form }};
 
1584   print $form->parse_html_template('ic/makemodel', { MM_DATA => [ @mm_data, {} ], mm_rows => scalar @mm_data + 1 });
 
1586   $lxdebug->leave_sub();
 
1590   $lxdebug->enter_sub();
 
1593   my ($nochange, $callback, $previousform, $linetotal, $line_purchase_price, $href);
 
1595   our ($deliverydate); # ToDO: check if this indeed comes from global context
 
1597   @column_index = qw(runningnumber qty unit bom partnumber description partsgroup lastcost total);
 
1599   if ($form->{previousform}) {
 
1601     @column_index = qw(qty unit bom partnumber description partsgroup total);
 
1605     $form->{old_callback} = $form->{callback};
 
1606     $callback             = $form->{callback};
 
1607     $form->{callback}     = "$form->{script}?action=display_form";
 
1610     map { delete $form->{$_} } qw(action header);
 
1612     # save form variables in a previousform variable
 
1613     $previousform = $form->escape($form->escape(join '&', map {
 
1614       sprintf "%s=%s", Q($_), /^listprice|lastcost|sellprice$/ ? $form->format_amount(\%myconfig, $form->{$_}) : $form->{$_}
 
1615     } grep { ref $form->{$_} eq '' && $form->{$_} } grep { !/^select/ } sort keys %$form ));
 
1617     $form->{callback} = $callback;
 
1618     $form->{assemblytotal} = 0;
 
1619     $form->{assembly_purchase_price_total} = 0;
 
1620     $form->{weight}        = 0;
 
1624    runningnumber => { text =>  $locale->text('No.'),              nowrap => 1, width => '5%'  },
 
1625    qty           => { text =>  $locale->text('Qty'),              nowrap => 1, width => '10%' },
 
1626    unit          => { text =>  $locale->text('Unit'),             nowrap => 1, width => '5%'  },
 
1627    partnumber    => { text =>  $locale->text('Part Number'),      nowrap => 1, width => '20%' },
 
1628    description   => { text =>  $locale->text('Part Description'), nowrap => 1, width => '50%' },
 
1629    lastcost      => { text =>  $locale->text('Purchase Prices'),  nowrap => 1, width => '50%' },
 
1630    total         => { text =>  $locale->text('Sale Prices'),      nowrap => 1,                },
 
1631    bom           => { text =>  $locale->text('BOM'),                                          },
 
1632    partsgroup    => { text =>  $locale->text('Group'),                                        },
 
1637   for my $i (1 .. $numrows) {
 
1638     my (%row, @row_hiddens);
 
1640     $form->{"partnumber_$i"} =~ s/\"/"/g;
 
1642     $linetotal           = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
 
1643     $line_purchase_price = $form->round_amount($form->{"lastcost_$i"} *  $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
 
1644     $form->{assemblytotal}                  += $linetotal;
 
1645     $form->{assembly_purchase_price_total}  += $line_purchase_price;
 
1646     $form->{"qty_$i"}    = $form->format_amount(\%myconfig, $form->{"qty_$i"});
 
1647     $linetotal           = $form->format_amount(\%myconfig, $linetotal, 2);
 
1648     $line_purchase_price = $form->format_amount(\%myconfig, $line_purchase_price, 2);
 
1649     $href                = qq|$form->{script}?action=edit&id=$form->{"id_$i"}&rowcount=$i&previousform=$previousform|;
 
1650     map { $row{$_}{data} = "" } qw(qty unit partnumber description bom partsgroup runningnumber);
 
1653     if (($i >= 1) && ($i == $numrows)) {
 
1654       if (!$form->{previousform}) {
 
1655         $row{partnumber}{data}  = qq|<input name="partnumber_$i" size=15 value="$form->{"partnumber_$i"}">|;
 
1656         $row{qty}{data}         = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
 
1657         $row{description}{data} = qq|<input name="description_$i" size=40 value="$form->{"description_$i"}">|;
 
1658         $row{partsgroup}{data}  = qq|<input name="partsgroup_$i" size=10 value="$form->{"partsgroup_$i"}">|;
 
1662       if ($form->{previousform}) {
 
1663         push @row_hiddens,          qw(qty bom);
 
1664         $row{partnumber}{data}    = $form->{"partnumber_$i"};
 
1665         $row{qty}{data}           = $form->{"qty_$i"};
 
1666         $row{bom}{data}           = $form->{"bom_$i"} ? "x" : " ";
 
1667         $row{qty}{align}          = 'right';
 
1669         $row{partnumber}{data}    = qq|<a href=$href>$form->{"partnumber_$i"}</a>|;
 
1670         $row{qty}{data}           = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
 
1671         $row{runningnumber}{data} = qq|<input name="runningnumber_$i" size=3 value="$i">|;
 
1672         $row{bom}{data}   = sprintf qq|<input name="bom_$i" type=checkbox class=checkbox value=1 %s>|,
 
1673                                        $form->{"bom_$i"} ? 'checked' : '';
 
1675       push @row_hiddens,        qw(unit description partnumber partsgroup);
 
1676       $row{unit}{data}        = $form->{"unit_$i"};
 
1677       $row{description}{data} = $form->{"description_$i"};
 
1678       $row{partsgroup}{data}  = $form->{"partsgroup_$i"};
 
1679       $row{bom}{align}        = 'center';
 
1682     $row{lastcost}{data}      = $line_purchase_price;
 
1683     $row{total}{data}         = $linetotal;
 
1684     $row{deliverydate}{data}  = $deliverydate;
 
1685     $row{lastcost}{align}     = 'right';
 
1686     $row{total}{align}        = 'right';
 
1687     $row{deliverydate}{align} = 'right';
 
1689     push @row_hiddens, qw(id sellprice lastcost weight price_factor_id price_factor);
 
1690     $row{hiddens} = [ map +{ name => "${_}_$i", value => $form->{"${_}_$i"} }, @row_hiddens ];
 
1695   print $form->parse_html_template('ic/assembly_row', { COLUMNS => \@column_index, ROWS => \@ROWS, HEADER => \%header });
 
1697   $lxdebug->leave_sub();
 
1701   $lxdebug->enter_sub();
 
1703   # parse pricegroups. and no, don't rely on check_form for this...
 
1704   map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
 
1706   if ($form->{item} eq "assembly") {
 
1707     my $i = $form->{assembly_rows};
 
1709     # if last row is empty check the form otherwise retrieve item
 
1710     if (   ($form->{"partnumber_$i"} eq "")
 
1711         && ($form->{"description_$i"} eq "")
 
1712         && ($form->{"partsgroup_$i"}  eq "")) {
 
1718       IC->assembly_item(\%myconfig, \%$form);
 
1720       my $rows = scalar @{ $form->{item_list} };
 
1723         $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
 
1726           $form->{makemodel_rows}--;
 
1730           map { $form->{item_list}[$i]{$_} =~ s/\"/"/g }
 
1731             qw(partnumber description unit partsgroup);
 
1732           map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} }
 
1733             keys %{ $form->{item_list}[0] };
 
1734           $form->{"runningnumber_$i"} = $form->{assembly_rows};
 
1735           $form->{assembly_rows}++;
 
1743         $form->{rowcount} = $i;
 
1744         $form->{assembly_rows}++;
 
1751   } elsif (($form->{item} eq 'part') || ($form->{item} eq 'service')) {
 
1755   $lxdebug->leave_sub();
 
1759   $lxdebug->enter_sub();
 
1761   $auth->assert('part_service_assembly_edit');
 
1763   my ($parts_id, %newform, $previousform, $amount, $callback);
 
1765   # check if there is a part number - commented out, cause there is an automatic allocation of numbers
 
1766   # $form->isblank("partnumber", $locale->text(ucfirst $form->{item}." Part Number missing!"));
 
1768   # check if there is a description
 
1769   $form->isblank("description", $locale->text("Part Description missing!"));
 
1771   $form->error($locale->text("Inventory quantity must be zero before you can set this $form->{item} obsolete!"))
 
1772     if $form->{obsolete} && $form->{onhand} * 1 && $form->{item} ne 'service';
 
1774   if (!$form->{buchungsgruppen_id}) {
 
1775     $form->error($locale->text("Parts must have an entry type.") . " " .
 
1776      $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.")
 
1780   $form->error($locale->text('Description must not be empty!')) unless $form->{description};
 
1781   $form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
 
1784   $lxdebug->message($LXDebug::DEBUG1, "ic.pl: sellprice in save = $form->{sellprice}\n");
 
1785   if (IC->save(\%myconfig, \%$form) == 3) {
 
1786     $form->error($locale->text('Partnumber not unique!'));
 
1788   # saving the history
 
1789   if(!exists $form->{addition}) {
 
1790     $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
 
1791     $form->{addition} = "SAVED";
 
1792     $form->save_history($form->dbconnect(\%myconfig));
 
1794   # /saving the history
 
1795   $parts_id = $form->{id};
 
1798   # load previous variables
 
1799   if ($form->{previousform}) {
 
1801     # save the new form variables before splitting previousform
 
1802     map { $newform{$_} = $form->{$_} } keys %$form;
 
1804     $previousform = $form->unescape($form->{previousform});
 
1806     # don't trample on previous variables
 
1807     map { delete $form->{$_} } keys %newform;
 
1809     my $ic_cvar_configs = CVar->get_configs(module => 'IC');
 
1810     my @ic_cvar_fields  = map { "cvar_$_->{name}" } @{ $ic_cvar_configs };
 
1812     # now take it apart and restore original values
 
1813     foreach my $item (split /&/, $previousform) {
 
1814       my ($key, $value) = split m/=/, $item, 2;
 
1815       $value =~ s/%26/&/g;
 
1816       $form->{$key} = $value;
 
1818     $form->{taxaccounts} = $newform{taxaccount2};
 
1820     if ($form->{item} eq 'assembly') {
 
1822       # undo number formatting
 
1823       map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
 
1824         qw(weight listprice sellprice rop);
 
1826       $form->{assembly_rows}--;
 
1827       $i = $newform{rowcount};
 
1828       $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
 
1830       $form->{sellprice} -= $form->{"sellprice_$i"} * $form->{"qty_$i"};
 
1831       $form->{weight}    -= $form->{"weight_$i"} * $form->{"qty_$i"};
 
1833       # change/add values for assembly item
 
1834       map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit weight listprice sellprice inventory_accno income_accno expense_accno price_factor_id);
 
1835       map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
 
1837       # das ist __voll__ bekloppt, dass so auszurechnen jb 22.5.09
 
1838       #$form->{sellprice} += $form->{"sellprice_$i"} * $form->{"qty_$i"};
 
1839       $form->{weight}    += $form->{"weight_$i"} * $form->{"qty_$i"};
 
1843       # set values for last invoice/order item
 
1844       $i = $form->{rowcount};
 
1845       $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
 
1847       map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit listprice inventory_accno income_accno expense_accno sellprice lastcost price_factor_id);
 
1848       map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
 
1850       $form->{"longdescription_$i"} = $newform{notes};
 
1852       $form->{"sellprice_$i"} = $newform{lastcost} if ($form->{vendor_id});
 
1854       if ($form->{exchangerate} != 0) {
 
1855         $form->{"sellprice_$i"} /= $form->{exchangerate};
 
1858       $lxdebug->message($LXDebug::DEBUG1, qq|sellprice_$i in previousform 2 = | . $form->{"sellprice_$i"} . qq|\n|);
 
1860       map { $form->{"taxaccounts_$i"} .= "$_ " } split / /, $newform{taxaccount};
 
1861       chop $form->{"taxaccounts_$i"};
 
1862       foreach my $item (qw(description rate taxnumber)) {
 
1863         my $index = $form->{"taxaccounts_$i"} . "_$item";
 
1864         $form->{$index} = $newform{$index};
 
1867       # credit remaining calculation
 
1868       $amount = $form->{"sellprice_$i"} * (1 - $form->{"discount_$i"} / 100) * $form->{"qty_$i"};
 
1870       map { $form->{"${_}_base"} += $amount } (split / /, $form->{"taxaccounts_$i"});
 
1871       map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{"taxaccounts_$i"} if !$form->{taxincluded};
 
1873       $form->{creditremaining} -= $amount;
 
1875       # redo number formatting, because invoice parse them!
 
1876       map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}) } qw(weight listprice sellprice rop);
 
1879     $form->{"id_$i"} = $parts_id;
 
1881     # Get the actual price factor (not just the ID) for the marge calculation.
 
1882     $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
 
1883     foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
 
1884       next if ($pfac->{id} != $newform{price_factor_id});
 
1885       $form->{"marge_price_factor_$i"} = $pfac->{factor};
 
1888     delete $form->{ALL_PRICE_FACTORS};
 
1890     delete $form->{action};
 
1892     # restore original callback
 
1893     $callback = $form->unescape($form->{callback});
 
1894     $form->{callback} = $form->unescape($form->{old_callback});
 
1895     delete $form->{old_callback};
 
1897     $form->{makemodel_rows}--;
 
1899     # put callback together
 
1900     foreach my $key (keys %$form) {
 
1902       # do single escape for Apache 2.0
 
1903       my $value = $form->escape($form->{$key}, 1);
 
1904       $callback .= qq|&$key=$value|;
 
1906     $form->{callback} = $callback;
 
1908   $lxdebug->message($LXDebug::DEBUG1, qq|ic.pl: sellprice_$i nach sub save = | . $form->{"sellprice_$i"} . qq|\n|);
 
1913   $lxdebug->leave_sub();
 
1917   $lxdebug->enter_sub();
 
1919   $auth->assert('part_service_assembly_edit');
 
1921   # saving the history
 
1922   if(!exists $form->{addition}) {
 
1923     $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
 
1924     $form->{addition} = "SAVED AS NEW";
 
1925     $form->save_history($form->dbconnect(\%myconfig));
 
1927   # /saving the history
 
1929   if ($form->{"original_partnumber"} &&
 
1930       ($form->{"partnumber"} eq $form->{"original_partnumber"})) {
 
1931     $form->{partnumber} = "";
 
1934   $lxdebug->leave_sub();
 
1938   $lxdebug->enter_sub();
 
1940   $auth->assert('part_service_assembly_edit');
 
1942   # saving the history
 
1943   if(!exists $form->{addition}) {
 
1944     $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
 
1945     $form->{addition} = "DELETED";
 
1946     $form->save_history($form->dbconnect(\%myconfig));
 
1948   # /saving the history
 
1949   my $rc = IC->delete(\%myconfig, \%$form);
 
1952   $form->redirect($locale->text('Item deleted!')) if ($rc > 0);
 
1953   $form->error($locale->text('Cannot delete item!'));
 
1955   $lxdebug->leave_sub();
 
1959   $lxdebug->enter_sub();
 
1961   $auth->assert('part_service_assembly_edit');
 
1966     pricegroup    => $form->{"pricegroup_$_"},
 
1967     pricegroup_id => $form->{"pricegroup_id_$_"},
 
1968     price         => $form->{"price_$_"},
 
1971   print $form->parse_html_template('ic/price_row', { PRICES => \@PRICES });
 
1973   $lxdebug->leave_sub();
 
1976 sub parts_language_selection {
 
1977   $lxdebug->enter_sub();
 
1979   $auth->assert('part_service_assembly_edit');
 
1981   our ($onload, $callback);
 
1983   my $languages = IC->retrieve_languages(\%myconfig, $form);
 
1985   if ($form->{language_values} ne "") {
 
1986     foreach my $item (split(/---\+\+\+---/, $form->{language_values})) {
 
1987       my ($language_id, $translation, $longdescription) = split(/--\+\+--/, $item);
 
1989       foreach my $language (@{ $languages }) {
 
1990         next unless ($language->{id} == $language_id);
 
1992         $language->{translation}     = $translation;
 
1993         $language->{longdescription} = $longdescription;
 
1999   my @header_sort = qw(name longdescription);
 
2000   my %header_title = ( "name" => $locale->text("Name"),
 
2001                        "longdescription" => $locale->text("Long Description"),
 
2005     map(+{ "column_title" => $header_title{$_},
 
2007            "callback" => $callback,
 
2011   $form->{"title"} = $locale->text("Language Values");
 
2013   print $form->parse_html_template("ic/parts_language_selection", { "HEADER"    => \@header,
 
2014                                                                     "LANGUAGES" => $languages,
 
2015                                                                     "onload"    => $onload });
 
2017   $lxdebug->leave_sub();
 
2020 sub ajax_autocomplete {
 
2021   $main::lxdebug->enter_sub();
 
2023   my $form     = $main::form;
 
2024   my %myconfig = %main::myconfig;
 
2026   $form->{column}          = 'description'     unless $form->{column} =~ /^partnumber|description$/;
 
2027   $form->{$form->{column}} = $form->{q}           || '';
 
2028   $form->{limit}           = ($form->{limit} * 1) || 10;
 
2029   $form->{searchitems}   ||= '';
 
2031   my @results = IC->all_parts(\%myconfig, $form);
 
2033   print $form->ajax_response_header(),
 
2034         $form->parse_html_template('ic/ajax_autocomplete');
 
2036   $main::lxdebug->leave_sub();
 
2039 sub continue { call_sub($form->{"nextsub"}); }