1 #=====================================================================
 
   4 # Based on SQL-Ledger Version 2.1.9
 
   5 # Web http://www.lx-office.org
 
   7 #=====================================================================
 
   8 # SQL-Ledger, Accounting
 
  11 #  Author: Dieter Simader
 
  12 #   Email: dsimader@sql-ledger.org
 
  13 #     Web: http://www.sql-ledger.org
 
  16 # This program is free software; you can redistribute it and/or modify
 
  17 # it under the terms of the GNU General Public License as published by
 
  18 # the Free Software Foundation; either version 2 of the License, or
 
  19 # (at your option) any later version.
 
  21 # This program is distributed in the hope that it will be useful,
 
  22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  24 # GNU General Public License for more details.
 
  25 # You should have received a copy of the GNU General Public License
 
  26 # along with this program; if not, write to the Free Software
 
  27 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
  28 #======================================================================
 
  30 # Inventory Control module
 
  32 #======================================================================
 
  34 use POSIX qw(strftime);
 
  35 use List::Util qw(first max);
 
  36 use List::MoreUtils qw(any);
 
  41 use SL::Helper::Flash;
 
  43 use SL::ReportGenerator;
 
  51 our ($form, $locale, %myconfig, $lxdebug, $auth);
 
  53 require "bin/mozilla/io.pl";
 
  54 require "bin/mozilla/invoice_io.pl";
 
  55 require "bin/mozilla/common.pl";
 
  56 require "bin/mozilla/reportgenerator.pl";
 
  61 # type=submit $locale->text('Add Part')
 
  62 # type=submit $locale->text('Add Service')
 
  63 # type=submit $locale->text('Add Assembly')
 
  64 # type=submit $locale->text('Edit Part')
 
  65 # type=submit $locale->text('Edit Service')
 
  66 # type=submit $locale->text('Edit Assembly')
 
  67 # $locale->text('Parts')
 
  68 # $locale->text('Services')
 
  69 # $locale->text('Inventory quantity must be zero before you can set this part obsolete!')
 
  70 # $locale->text('Inventory quantity must be zero before you can set this assembly obsolete!')
 
  71 # $locale->text('Part Number missing!')
 
  72 # $locale->text('Service Number missing!')
 
  73 # $locale->text('Assembly Number missing!')
 
  74 # $locale->text('ea');
 
  79   $lxdebug->enter_sub();
 
  81   $auth->assert('part_service_assembly_edit');
 
  83   my $title                = 'Add ' . ucfirst $form->{item};
 
  84   $form->{title}           = $locale->text($title);
 
  85   $form->{callback}        = "$form->{script}?action=add&item=$form->{item}" unless $form->{callback};
 
  86   $form->{unit_changeable} = 1;
 
  88   IC->get_pricegroups(\%myconfig, \%$form);
 
  92   $lxdebug->leave_sub();
 
  96   $lxdebug->enter_sub();
 
  98   $auth->assert('part_service_assembly_details');
 
 100   $form->{revers}       = 0;  # switch for backward sorting
 
 101   $form->{lastsort}     = ""; # memory for which table was sort at last time
 
 102   $form->{ndxs_counter} = 0;  # counter for added entries to top100
 
 104   my %is_xyz     = map { +"is_$_" => ($form->{searchitems} eq $_) } qw(part service assembly);
 
 106   $form->{title} = (ucfirst $form->{searchitems}) . "s";
 
 107   $form->{title} = $locale->text($form->{title});
 
 108   $form->{title} = $locale->text('Assemblies') if ($is_xyz{is_assembly});
 
 110   $form->{CUSTOM_VARIABLES}                  = CVar->get_configs('module' => 'IC');
 
 111   ($form->{CUSTOM_VARIABLES_FILTER_CODE},
 
 112    $form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables'      => $form->{CUSTOM_VARIABLES},
 
 113                                                                            'include_prefix' => 'l_',
 
 114                                                                            'include_value'  => 'Y');
 
 118   $form->get_lists('partsgroup'    => 'ALL_PARTSGROUPS');
 
 119   print $form->parse_html_template('ic/search', { %is_xyz,
 
 120                                                   dateformat => $myconfig{dateformat},
 
 121                                                   limit => $myconfig{vclimit}, });
 
 123   $lxdebug->leave_sub();
 
 126 sub search_update_prices {
 
 127   $lxdebug->enter_sub();
 
 129   $auth->assert('part_service_assembly_edit');
 
 131   my $pricegroups = IC->get_pricegroups(\%myconfig, \%$form);
 
 133   $form->{title} = $locale->text('Update Prices');
 
 137   print $form->parse_html_template('ic/search_update_prices', { PRICE_ROWS => $pricegroups });
 
 139   $lxdebug->leave_sub();
 
 142 sub confirm_price_update {
 
 143   $lxdebug->enter_sub();
 
 145   $auth->assert('part_service_assembly_edit');
 
 148   my $value_found = undef;
 
 150   foreach my $idx (qw(sellprice listprice), (1..$form->{price_rows})) {
 
 151     my $name      = $idx =~ m/\d/ ? $form->{"pricegroup_${idx}"}      : $idx eq 'sellprice' ? $locale->text('Sell Price') : $locale->text('List Price');
 
 152     my $type      = $idx =~ m/\d/ ? $form->{"pricegroup_type_${idx}"} : $form->{"${idx}_type"};
 
 153     my $value_idx = $idx =~ m/\d/ ? "price_${idx}" : $idx;
 
 154     my $value     = $form->parse_amount(\%myconfig, $form->{$value_idx});
 
 156     if ((0 > $value) && ($type eq 'percent')) {
 
 157       push @errors, $locale->text('You cannot adjust the price for pricegroup "#1" by a negative percentage.', $name);
 
 159     } elsif (!$value && ($form->{$value_idx} ne '')) {
 
 160       push @errors, $locale->text('No valid number entered for pricegroup "#1".', $name);
 
 162     } elsif (0 < $value) {
 
 167   push @errors, $locale->text('No prices will be updated because no prices have been entered.') if (!$value_found);
 
 169   my $num_matches = IC->get_num_matches_for_priceupdate();
 
 174     $form->show_generic_error(join('<br>', @errors), 'back_button' => 1);
 
 177   $form->{nextsub} = "update_prices";
 
 179   map { delete $form->{$_} } qw(action header);
 
 181   print $form->parse_html_template('ic/confirm_price_update', { HIDDENS     => [ map { name => $_, value => $form->{$_} }, keys %$form ],
 
 182                                                                 num_matches => $num_matches });
 
 184   $lxdebug->leave_sub();
 
 188   $lxdebug->enter_sub();
 
 190   $auth->assert('part_service_assembly_edit');
 
 192   my $num_updated = IC->update_prices(\%myconfig, \%$form);
 
 194   if (-1 != $num_updated) {
 
 195     $form->redirect($locale->text('#1 prices were updated.', $num_updated));
 
 197     $form->error($locale->text('Could not update prices!'));
 
 200   $lxdebug->leave_sub();
 
 204 #  $lxdebug->enter_sub();
 
 206 #  $auth->assert('part_service_assembly_edit');
 
 208 #  our ($j, $lastndx);
 
 211 #  $form->{title} = $locale->text('Top 100 hinzufuegen');
 
 215 #  push @custom_hiddens, qw(searchitems title bom titel revers lastsort sort ndxs_counter extras);
 
 216 #  push @custom_hiddens, qw(itemstatus l_linetotal l_partnumber l_description l_onhand l_unit l_sellprice l_linetotalsellprice);
 
 218 #        +{ name => 'row',     value => $j              },
 
 219 #        +{ name => 'nextsub', value => 'item_selected' },
 
 220 #        +{ name => 'test',    value => 'item_selected' },
 
 221 #        +{ name => 'lastndx', value => $lastndx        },
 
 222 #    map(+{ name => $_,        value => $form->{$_}     }, @custom_hiddens),
 
 225 #  my ($partnumber, $description, $unit, $sellprice, $soldtotal);
 
 226 #  # if choice set data
 
 227 ##  if ($form->{ndx}) {
 
 228 ##    for my $i (0 .. $form->{ndxs_counter}) {
 
 230 ##      # insert data into top100
 
 231 ##      push @{ $form->{parts} },
 
 233 ##          partnumber  => $form->{"totop100_partnumber_$j"},
 
 234 ##          description => $form->{"totop100_description_$j"},
 
 235 ##          unit        => $form->{"totop100_unit_$j"},
 
 236 ##          sellprice   => $form->{"totop100_sellprice_$j"},
 
 237 ##          soldtotal   => $form->{"totop100_soldtotal_$j"},
 
 244 #  # set data for next page
 
 245 #  for my $i (1 .. $form->{ndxs_counter}) {
 
 246 #    $partnumber  = $form->{"totop100_partnumber_$i"};
 
 247 #    $description = $form->{"totop100_description_$i"};
 
 248 #    $unit        = $form->{"totop100_unit_$i"};
 
 249 #    $sellprice   = $form->{"totop100_sellprice_$i"};
 
 250 #    $soldtotal   = $form->{"totop100_soldtotal_$i"};
 
 253 #    totop100_partnumber  => $form->{"totop100_partnumber_$i"},
 
 254 #    totop100_description => $form->{"totop100_description_$i"},
 
 255 #    totop100_unit        => $form->{"totop100_unit_$i"},
 
 256 #    totop100_sellprice   => $form->{"totop100_sellprice_$i"},
 
 257 #    totop100_soldtotal   => $form->{"totop100_soldtotal_$i"},
 
 261 ##<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
 
 262 ##<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
 
 263 ##<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
 
 264 ##<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
 
 265 ##<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
 
 269 #  print $form->parse_html_template('ic/choice', +{ HIDDENS => \@HIDDENS, PARTS => \@PARTS });
 
 271 #  $lxdebug->leave_sub();
 
 275 #  $lxdebug->enter_sub();
 
 277 #  $auth->assert('part_service_assembly_edit');
 
 280 #  our ($partnumber, $description, $unit, $sellprice, $soldtotal);
 
 282 #  my @sortorders = ("", "partnumber", "description", "all");
 
 283 #  my $sortorder = $sortorders[($form->{description} ? 2 : 0) + ($form->{partnumber} ? 1 : 0)];
 
 284 #  IC->get_parts(\%myconfig, \%$form, $sortorder);
 
 286 #  $form->{title} = $locale->text('Top 100 hinzufuegen');
 
 291 #  <form method=post action=ic.pl>
 
 294 #      <th class=listtop colspan=6>| . $locale->text('choice part') . qq|</th>
 
 296 #        <tr height="5"></tr>
 
 297 #        <tr class=listheading>
 
 299 #          <th class=listheading>| . $locale->text('Part Number') . qq|</th>
 
 300 #          <th class=listheading>| . $locale->text('Part Description') . qq|</th>
 
 301 #          <th class=listheading>| . $locale->text('Unit of measure') . qq|</th>
 
 302 #          <th class=listheading>| . $locale->text('Sell Price') . qq|</th>
 
 303 #          <th class=listheading>| . $locale->text('soldtotal') . qq|</th>
 
 307 #  my $i = $form->{rows};
 
 309 #  for ($j = 1; $j <= $i; $j++) {
 
 312 #        <tr class=listrow| . ($j % 2) . qq|>|;
 
 315 #            <td><input name=ndx class=radio type=radio value=$j checked></td>|;
 
 318 #          <td><input name=ndx class=radio type=radio value=$j></td>|;
 
 321 #          <td><input name="new_partnumber_$j" type=hidden value="$form->{"partnumber_$j"}">$form->{"partnumber_$j"}</td>
 
 322 #          <td><input name="new_description_$j" type=hidden value="$form->{"description_$j"}">$form->{"description_$j"}</td>
 
 323 #          <td><input name="new_unit_$j" type=hidden value="$form->{"unit_$j"}">$form->{"unit_$j"}</td>
 
 324 #          <td><input name="new_sellprice_$j" type=hidden value="$form->{"sellprice_$j"}">$form->{"sellprice_$j"}</td>
 
 325 #          <td><input name="new_soldtotal_$j" type=hidden value="$form->{"soldtotal_$j"}">$form->{"soldtotal_$j"}</td>
 
 328 #        <input name="new_id_$j" type=hidden value="$form->{"id_$j"}">|;
 
 338 #<input type=hidden name=itemstatus value="$form->{itemstatus}">
 
 339 #<input type=hidden name=l_linetotal value="$form->{l_linetotal}">
 
 340 #<input type=hidden name=l_partnumber value="$form->{l_partnumber}">
 
 341 #<input type=hidden name=l_description value="$form->{l_description}">
 
 342 #<input type=hidden name=l_onhand value="$form->{l_onhand}">
 
 343 #<input type=hidden name=l_unit value="$form->{l_unit}">
 
 344 #<input type=hidden name=l_sellprice value="$form->{l_sellprice}">
 
 345 #<input type=hidden name=l_linetotalsellprice value="$form->{l_linetotalsellprice}">
 
 346 #<input type=hidden name=sort value="$form->{sort}">
 
 347 #<input type=hidden name=revers value="$form->{revers}">
 
 348 #<input type=hidden name=lastsort value="$form->{lastsort}">
 
 350 #<input type=hidden name=bom value="$form->{bom}">
 
 351 #<input type=hidden name=titel value="$form->{titel}">
 
 352 #<input type=hidden name=searchitems value="$form->{searchitems}">
 
 354 #<input type=hidden name=row value=$j>
 
 356 #<input type=hidden name=nextsub value=item_selected>
 
 358 #<input name=lastndx type=hidden value=$lastndx>
 
 360 #<input name=ndxs_counter type=hidden value=$form->{ndxs_counter}>|;
 
 364 #  if (($form->{ndxs_counter}) > 0) {
 
 365 #    for ($i = 1; ($i < $form->{ndxs_counter} + 1); $i++) {
 
 367 #      $partnumber  = $form->{"totop100_partnumber_$i"};
 
 368 #      $description = $form->{"totop100_description_$i"};
 
 369 #      $unit        = $form->{"totop100_unit_$i"};
 
 370 #      $sellprice   = $form->{"totop100_sellprice_$i"};
 
 371 #      $soldtotal   = $form->{"totop100_soldtotal_$i"};
 
 374 #<input type=hidden name=totop100_partnumber_$i value=$form->{"totop100_partnumber_$i"}>
 
 375 #<input type=hidden name=totop100_description_$i value=$form->{"totop100_description_$i"}>
 
 376 #<input type=hidden name=totop100_unit_$i value=$form->{"totop100_unit_$i"}>
 
 377 #<input type=hidden name=totop100_sellprice_$i value=$form->{"totop100_sellprice_$i"}>
 
 378 #<input type=hidden name=totop100_soldtotal_$i value=$form->{"totop100_soldtotal_$i"}>
 
 386 #<input class=submit type=submit name=action value="|
 
 387 #    . $locale->text('TOP100') . qq|">
 
 391 #  $lxdebug->leave_sub();
 
 395   $lxdebug->enter_sub();
 
 397   $auth->assert('part_service_assembly_edit');
 
 400     $form->{ndxs_counter}++;
 
 402     if ($form->{ndxs_counter} > 0) {
 
 404       my $index = $form->{ndx};
 
 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"};
 
 414   $lxdebug->leave_sub();
 
 418   $lxdebug->enter_sub();
 
 420   $auth->assert('part_service_assembly_edit');
 
 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);
 
 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} = "";
 
 434   $form->{number}      = "position";
 
 435   $form->{l_number}    = "Y";
 
 439   $form->{title} = $locale->text('Top 100');
 
 441   $revers   = $form->{revers};
 
 442   $lastsort = $form->{lastsort};
 
 444   if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
 
 446     $form->{lastsort} = "partnumber";
 
 447     $form->{sort}     = "partnumber";
 
 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);
 
 454   # if we have a serialnumber limit search
 
 455   if ($form->{serialnumber} || $form->{l_serialnumber}) {
 
 456     $form->{l_serialnumber} = "Y";
 
 457     unless (   $form->{bought}
 
 460             || $form->{quoted}) {
 
 461       $form->{bought} = $form->{sold} = 1;
 
 464   IC->all_parts(\%myconfig, \%$form);
 
 466   if ($form->{itemstatus} eq 'active') {
 
 467     $option .= $locale->text('Active') . " : ";
 
 469   if ($form->{itemstatus} eq 'obsolete') {
 
 470     $option .= $locale->text('Obsolete') . " : ";
 
 472   if ($form->{itemstatus} eq 'orphaned') {
 
 473     $option .= $locale->text('Orphaned') . " : ";
 
 475   if ($form->{itemstatus} eq 'onhand') {
 
 476     $option .= $locale->text('On Hand') . " : ";
 
 477     $form->{l_onhand} = "Y";
 
 479   if ($form->{itemstatus} eq 'short') {
 
 480     $option .= $locale->text('Short') . " : ";
 
 481     $form->{l_onhand} = "Y";
 
 483   if ($form->{onorder}) {
 
 484     $form->{l_ordnumber} = "Y";
 
 485     $callback .= "&onorder=$form->{onorder}";
 
 486     $option   .= $locale->text('On Order') . " : ";
 
 488   if ($form->{ordered}) {
 
 489     $form->{l_ordnumber} = "Y";
 
 490     $callback .= "&ordered=$form->{ordered}";
 
 491     $option   .= $locale->text('Ordered') . " : ";
 
 494     $form->{l_quonumber} = "Y";
 
 495     $callback .= "&rfq=$form->{rfq}";
 
 496     $option   .= $locale->text('RFQ') . " : ";
 
 498   if ($form->{quoted}) {
 
 499     $form->{l_quonumber} = "Y";
 
 500     $callback .= ""ed=$form->{quoted}";
 
 501     $option   .= $locale->text('Quoted') . " : ";
 
 503   if ($form->{bought}) {
 
 504     $form->{l_invnumber} = "Y";
 
 505     $callback .= "&bought=$form->{bought}";
 
 506     $option   .= $locale->text('Bought') . " : ";
 
 509     $form->{l_invnumber} = "Y";
 
 510     $callback .= "&sold=$form->{sold}";
 
 511     $option   .= $locale->text('Sold') . " : ";
 
 518       || $form->{quoted}) {
 
 520     $form->{l_lastcost} = "";
 
 521     $form->{l_name}     = "Y";
 
 522     if ($form->{transdatefrom}) {
 
 523       $callback .= "&transdatefrom=$form->{transdatefrom}";
 
 525         . $locale->text('From')
 
 527         . $locale->date(\%myconfig, $form->{transdatefrom}, 1);
 
 529     if ($form->{transdateto}) {
 
 530       $callback .= "&transdateto=$form->{transdateto}";
 
 532         . $locale->text('To')
 
 534         . $locale->date(\%myconfig, $form->{transdateto}, 1);
 
 540   if ($form->{partnumber}) {
 
 541     $callback .= "&partnumber=$form->{partnumber}";
 
 542     $option   .= $locale->text('Part Number') . qq| : $form->{partnumber}<br>|;
 
 545     $callback .= "&partnumber=$form->{ean}";
 
 546     $option   .= $locale->text('EAN') . qq| : $form->{ean}<br>|;
 
 548   if ($form->{partsgroup}) {
 
 549     $callback .= "&partsgroup=$form->{partsgroup}";
 
 550     $option   .= $locale->text('Group') . qq| : $form->{partsgroup}<br>|;
 
 552   if ($form->{serialnumber}) {
 
 553     $callback .= "&serialnumber=$form->{serialnumber}";
 
 554     $option   .= $locale->text('Serial Number') . qq| : $form->{serialnumber}<br>|;
 
 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>|;
 
 563     $callback .= "&make=$form->{make}";
 
 564     $option   .= $locale->text('Make') . qq| : $form->{make}<br>|;
 
 566   if ($form->{model}) {
 
 567     $callback .= "&model=$form->{model}";
 
 568     $option   .= $locale->text('Model') . qq| : $form->{model}<br>|;
 
 570   if ($form->{drawing}) {
 
 571     $callback .= "&drawing=$form->{drawing}";
 
 572     $option   .= $locale->text('Drawing') . qq| : $form->{drawing}<br>|;
 
 574   if ($form->{microfiche}) {
 
 575     $callback .= "µfiche=$form->{microfiche}";
 
 576     $option   .= $locale->text('Microfiche') . qq| : $form->{microfiche}<br>|;
 
 578   if ($form->{l_soldtotal}) {
 
 579     $callback .= "&soldtotal=$form->{soldtotal}";
 
 580     $option   .= $locale->text('soldtotal') . qq| : $form->{soldtotal}<br>|;
 
 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)
 
 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} = "";
 
 596     $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
 
 599   if ($form->{searchitems} eq 'service') {
 
 601     # remove bin, weight and rop from list
 
 602     map { $form->{"l_$_"} = "" } qw(bin weight rop);
 
 604     $form->{l_onhand} = "";
 
 606     # qty is irrelevant unless bought or sold
 
 612         || $form->{quoted}) {
 
 613       $form->{l_onhand} = "Y";
 
 615       $form->{l_linetotalsellprice} = "";
 
 616       $form->{l_linetotallastcost}  = "";
 
 620   foreach my $item (@columns) {
 
 621     if ($form->{"l_$item"} eq "Y") {
 
 622       push @column_index, $item;
 
 624       # add column to callback
 
 625       $callback .= "&l_$item=Y";
 
 629   if ($form->{l_subtotal} eq 'Y') {
 
 630     $callback .= "&l_subtotal=Y";
 
 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')
 
 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')
 
 643   $column_header{partsgroup} =
 
 644       qq|<th nowrap><a class=listheading href=$callback&sort=partsgroup>|
 
 645     . $locale->text('Group')
 
 647   $column_header{bin} =
 
 648       qq|<th><a class=listheading href=$callback&sort=bin>|
 
 649     . $locale->text('Bin')
 
 651   $column_header{priceupdate} =
 
 652       qq|<th nowrap><a class=listheading href=$callback&sort=priceupdate>|
 
 653     . $locale->text('Updated')
 
 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')
 
 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')
 
 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>|;
 
 672   $column_header{invnumber} =
 
 673       qq|<th nowrap><a class=listheading href=$callback&sort=invnumber>|
 
 674     . $locale->text('Invoice Number')
 
 676   $column_header{ordnumber} =
 
 677       qq|<th nowrap><a class=listheading href=$callback&sort=ordnumber>|
 
 678     . $locale->text('Order Number')
 
 680   $column_header{quonumber} =
 
 681       qq|<th nowrap><a class=listheading href=$callback&sort=quonumber>|
 
 682     . $locale->text('Quotation')
 
 685   $column_header{name} =
 
 686       qq|<th nowrap><a class=listheading href=$callback&sort=name>|
 
 687     . $locale->text('Name')
 
 690   $column_header{sellprice} =
 
 691       qq|<th class=listheading nowrap>|
 
 692     . $locale->text('Sell Price')
 
 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>|;
 
 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')
 
 707   $column_header{microfiche} =
 
 708       qq|<th nowrap><a class=listheading href=$callback&sort=microfiche>|
 
 709     . $locale->text('Microfiche')
 
 712   $column_header{serialnumber} =
 
 713       qq|<th nowrap><a class=listheading href=$callback&sort=serialnumber>|
 
 714     . $locale->text('Serial Number')
 
 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')
 
 722   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} }) {
 
 792         parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
 
 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') {
 
 915     parts_subtotal(\@column_index, \$subtotalonhand, \$subtotalsellprice, \$subtotallastcost, \$subtotallistprice);
 
 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|"> -->
 
 981   $lxdebug->leave_sub();
 
 986 # Warning, deep magic ahead.
 
 987 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
 
 989 # flags coming from the form:
 
 991 #  searchitems=part revers=0 lastsort=''
 
 994 # partnumber ean description partsgroup serialnumber make model drawing microfiche
 
 995 # transdatefrom transdateto
 
 998 #  itemstatus = active | onhand | short | obsolete | orphaned
 
 999 #  action     = continue | top100
 
1002 #  bought sold onorder ordered rfq quoted
 
1003 #  l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
 
1004 #  l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
 
1005 #  l_partsgroup l_subtotal l_soldtotal l_deliverydate l_pricegroups
 
1008 #  nextsub revers lastsort sort ndxs_counter
 
1010 sub generate_report {
 
1011   $lxdebug->enter_sub();
 
1013   $auth->assert('part_service_assembly_details');
 
1015   my ($revers, $lastsort, $description);
 
1017   my $cvar_configs = CVar->get_configs('module' => 'IC');
 
1019   $form->{title} = (ucfirst $form->{searchitems}) . "s";
 
1020   $form->{title} =~ s/ys$/ies/;
 
1021   $form->{title} = $locale->text($form->{title});
 
1024     'bin'                => { 'text' => $locale->text('Bin'), },
 
1025     'deliverydate'       => { 'text' => $locale->text('deliverydate'), },
 
1026     'description'        => { 'text' => $locale->text('Part Description'), },
 
1027     'notes'              => { 'text' => $locale->text('Notes'), },
 
1028     'drawing'            => { 'text' => $locale->text('Drawing'), },
 
1029     'ean'                => { 'text' => $locale->text('EAN'), },
 
1030     'image'              => { 'text' => $locale->text('Image'), },
 
1031     'invnumber'          => { 'text' => $locale->text('Invoice Number'), },
 
1032     'lastcost'           => { 'text' => $locale->text('Last Cost'), },
 
1033     'linetotallastcost'  => { 'text' => $locale->text('Extended'), },
 
1034     'linetotallistprice' => { 'text' => $locale->text('Extended'), },
 
1035     'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
 
1036     'listprice'          => { 'text' => $locale->text('List Price'), },
 
1037     'microfiche'         => { 'text' => $locale->text('Microfiche'), },
 
1038     'name'               => { 'text' => $locale->text('Name'), },
 
1039     'onhand'             => { 'text' => $locale->text('Stocked Qty'), },
 
1040     'ordnumber'          => { 'text' => $locale->text('Order Number'), },
 
1041     'partnumber'         => { 'text' => $locale->text('Part Number'), },
 
1042     'partsgroup'         => { 'text' => $locale->text('Group'), },
 
1043     'priceupdate'        => { 'text' => $locale->text('Updated'), },
 
1044     'quonumber'          => { 'text' => $locale->text('Quotation'), },
 
1045     'rop'                => { 'text' => $locale->text('ROP'), },
 
1046     'sellprice'          => { 'text' => $locale->text('Sell Price'), },
 
1047     'serialnumber'       => { 'text' => $locale->text('Serial Number'), },
 
1048     'soldtotal'          => { 'text' => $locale->text('Qty in Selected Records'), },
 
1049     'name'               => { 'text' => $locale->text('Name in Selected Records'), },
 
1050     'transdate'          => { 'text' => $locale->text('Transdate'), },
 
1051     'unit'               => { 'text' => $locale->text('Unit'), },
 
1052     'weight'             => { 'text' => $locale->text('Weight'), },
 
1053     'projectnumber'      => { 'text' => $locale->text('Project Number'), },
 
1054     'projectdescription' => { 'text' => $locale->text('Project Description'), },
 
1057   $revers     = $form->{revers};
 
1058   $lastsort   = $form->{lastsort};
 
1060   # sorting and direction of sorting
 
1061   # ToDO: change this to the simpler field+direction method
 
1062   if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
 
1063     $form->{revers}   = 0;
 
1064     $form->{lastsort} = "partnumber";
 
1065     $form->{sort}     = "partnumber";
 
1067     if ($form->{lastsort} eq $form->{sort}) {
 
1068       $form->{revers} = 1 - $form->{revers};
 
1070       $form->{revers} = 0;
 
1071       $form->{lastsort} = $form->{sort};
 
1075   # special case if we have a serialnumber limit search
 
1076   # serialnumbers are only given in invoices and orders,
 
1077   # so they can only pop up in bought, sold, rfq, and quoted stuff
 
1078   $form->{no_sn_joins} = 'Y' if (   !$form->{bought} && !$form->{sold}
 
1079                                  && !$form->{rfq}    && !$form->{quoted}
 
1080                                  && ($form->{l_serialnumber} || $form->{serialnumber}));
 
1082   # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
 
1083   # if any of these are ticked the behavior changes slightly for lastcost
 
1084   # since all those are aggregation checks for the legder tables this is an internal switch
 
1085   # refered to as ledgerchecks
 
1086   $form->{ledgerchecks} = 'Y' if (   $form->{bought} || $form->{sold} || $form->{onorder}
 
1087                                   || $form->{ordered} || $form->{rfq} || $form->{quoted});
 
1089   # if something should be activated if something else is active, enter it here
 
1090   my %dependencies = (
 
1091     onhand       => [ qw(l_onhand) ],
 
1092     short        => [ qw(l_onhand) ],
 
1093     onorder      => [ qw(l_ordnumber) ],
 
1094     ordered      => [ qw(l_ordnumber) ],
 
1095     rfq          => [ qw(l_quonumber) ],
 
1096     quoted       => [ qw(l_quonumber) ],
 
1097     bought       => [ qw(l_invnumber) ],
 
1098     sold         => [ qw(l_invnumber) ],
 
1099     ledgerchecks => [ qw(l_name) ],
 
1100     serialnumber => [ qw(l_serialnumber) ],
 
1101     no_sn_joins  => [ qw(bought sold) ],
 
1104   # get name of partsgroup if id is given
 
1106   if ($form->{partsgroup_id}) {
 
1107     my $pg = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load;
 
1108     $pg_name = $pg->{'partsgroup'};
 
1111   # these strings get displayed at the top of the results to indicate the user which switches were used
 
1113     active        => $locale->text('Active'),
 
1114     obsolete      => $locale->text('Obsolete'),
 
1115     orphaned      => $locale->text('Orphaned'),
 
1116     onhand        => $locale->text('On Hand'),
 
1117     short         => $locale->text('Short'),
 
1118     onorder       => $locale->text('On Order'),
 
1119     ordered       => $locale->text('Ordered'),
 
1120     rfq           => $locale->text('RFQ'),
 
1121     quoted        => $locale->text('Quoted'),
 
1122     bought        => $locale->text('Bought'),
 
1123     sold          => $locale->text('Sold'),
 
1124     transdatefrom => $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
 
1125     transdateto   => $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
 
1126     partnumber    => $locale->text('Part Number')      . ": '$form->{partnumber}'",
 
1127     partsgroup    => $locale->text('Group')            . ": '$form->{partsgroup}'",
 
1128     partsgroup_id => $locale->text('Group')            . ": '$pg_name'",
 
1129     serialnumber  => $locale->text('Serial Number')    . ": '$form->{serialnumber}'",
 
1130     description   => $locale->text('Part Description') . ": '$form->{description}'",
 
1131     make          => $locale->text('Make')             . ": '$form->{make}'",
 
1132     model         => $locale->text('Model')            . ": '$form->{model}'",
 
1133     drawing       => $locale->text('Drawing')          . ": '$form->{drawing}'",
 
1134     microfiche    => $locale->text('Microfiche')       . ": '$form->{microfiche}'",
 
1135     l_soldtotal   => $locale->text('Qty in Selected Records'),
 
1136     ean           => $locale->text('EAN')              . ": '$form->{ean}'",
 
1139   my @itemstatus_keys = qw(active obsolete orphaned onhand short);
 
1140   my @callback_keys   = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
 
1141                            drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto ean);
 
1143   # calculate dependencies
 
1144   for (@itemstatus_keys, @callback_keys) {
 
1145     next if ($form->{itemstatus} ne $_ && !$form->{$_});
 
1146     map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
 
1149   # generate callback and optionstrings
 
1151   for my  $key (@itemstatus_keys, @callback_keys) {
 
1152     next if ($form->{itemstatus} ne $key && !$form->{$key});
 
1153     push @options, $optiontexts{$key};
 
1156   # special case for lastcost
 
1157   if ($form->{ledgerchecks}){
 
1158     # ledgerchecks don't know about sellprice or lastcost. they just return a
 
1159     # price. so rename sellprice to price, and drop lastcost.
 
1160     $column_defs{sellprice}{text} = $locale->text('Price');
 
1161     $form->{l_lastcost} = ""
 
1164   if ($form->{description}) {
 
1165     $description = $form->{description};
 
1166     $description =~ s/\n/<br>/g;
 
1169   if ($form->{l_linetotal}) {
 
1170     $form->{l_qty} = "Y";
 
1171     $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
 
1172     $form->{l_linetotallastcost}  = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if  $form->{l_lastcost};
 
1173     $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
 
1176   if ($form->{searchitems} eq 'service') {
 
1178     # remove bin, weight and rop from list
 
1179     map { $form->{"l_$_"} = "" } qw(bin weight rop);
 
1181     $form->{l_onhand} = "";
 
1183     # qty is irrelevant unless bought or sold
 
1184     if (   $form->{bought}
 
1189         || $form->{quoted}) {
 
1190 #      $form->{l_onhand} = "Y";
 
1192       $form->{l_linetotalsellprice} = "";
 
1193       $form->{l_linetotallastcost}  = "";
 
1197   # soldtotal doesn't make sense with more than one bsooqr option.
 
1198   # so reset it to sold (the most common option), and issue a warning
 
1200   # also it doesn't make sense without bsooqr. disable and issue a warning too
 
1201   my @bsooqr = qw(sold bought onorder ordered rfq quoted);
 
1202   my $bsooqr_mode = grep { $form->{$_} } @bsooqr;
 
1203   if ($form->{l_subtotal} && 1 < $bsooqr_mode) {
 
1204     my $enabled       = first { $form->{$_} } @bsooqr;
 
1205     $form->{$_}       = ''   for @bsooqr;
 
1206     $form->{$enabled} = 'Y';
 
1208     push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
 
1210   if ($form->{l_soldtotal} && !$bsooqr_mode) {
 
1211     delete $form->{l_soldtotal};
 
1213     flash('warning', $::locale->text('Soldtotal does not make sense without any bsooqr options'));
 
1215   if ($form->{l_name} && !$bsooqr_mode) {
 
1216     delete $form->{l_name};
 
1218     flash('warning', $::locale->text('Name does not make sense without any bsooqr options'));
 
1220   IC->all_parts(\%myconfig, \%$form);
 
1223     partnumber description notes partsgroup bin onhand rop soldtotal unit listprice
 
1224     linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
 
1225     priceupdate weight image drawing microfiche invnumber ordnumber quonumber
 
1226     transdate name serialnumber deliverydate ean projectnumber projectdescription
 
1229   my $pricegroups = SL::DB::Manager::Pricegroup->get_all(sort => 'id');
 
1230   my @pricegroup_columns;
 
1231   my %column_defs_pricegroups;
 
1232   if ($form->{l_pricegroups}) {
 
1233     @pricegroup_columns      = map { "pricegroup_" . $_->id } @{ $pricegroups };
 
1234     %column_defs_pricegroups = map {
 
1235       "pricegroup_" . $_->id => {
 
1236         text    => $::locale->text('Pricegroup') . ' ' . $_->pricegroup,
 
1239     }  @{ $pricegroups };
 
1241   push @columns, @pricegroup_columns;
 
1243   my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
 
1244   my @searchable_custom_variables  = grep { $_->{searchable} }  @{ $cvar_configs };
 
1245   my %column_defs_cvars            = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
 
1247   push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
 
1249   %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
 
1250   map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
 
1251   map { $column_defs{$_}->{align}   = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal), @pricegroup_columns;
 
1253   my @hidden_variables = (
 
1254     qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups),
 
1257     map({ "cvar_$_->{name}" } @searchable_custom_variables),
 
1258     map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
 
1259     map({ "l_$_" } @columns),
 
1262   my $callback         = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
 
1264   my @sort_full        = qw(partnumber description onhand soldtotal deliverydate);
 
1265   my @sort_no_revers   = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
 
1267   foreach my $col (@sort_full) {
 
1268     $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
 
1270   map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
 
1272   # add order to callback
 
1273   $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
 
1275   my $report = SL::ReportGenerator->new(\%myconfig, $form);
 
1277   my %attachment_basenames = (
 
1278     'part'     => $locale->text('part_list'),
 
1279     'service'  => $locale->text('service_list'),
 
1280     'assembly' => $locale->text('assembly_list'),
 
1283   $report->set_options('raw_top_info_text'     => $form->parse_html_template('ic/generate_report_top', { options => \@options }),
 
1284                        'raw_bottom_info_text'  => $form->parse_html_template('ic/generate_report_bottom'),
 
1285                        'output_format'         => 'HTML',
 
1286                        'title'                 => $form->{title},
 
1287                        'attachment_basename'   => $attachment_basenames{$form->{searchitems}} . strftime('_%Y%m%d', localtime time),
 
1289   $report->set_options_from_form();
 
1290   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
 
1292   $report->set_columns(%column_defs);
 
1293   $report->set_column_order(@columns);
 
1295   $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
 
1297   $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
 
1299   CVar->add_custom_variables_to_report('module'         => 'IC',
 
1300                                        'trans_id_field' => 'id',
 
1301                                        'configs'        => $cvar_configs,
 
1302                                        'column_defs'    => \%column_defs,
 
1303                                        'data'           => $form->{parts});
 
1305   CVar->add_custom_variables_to_report('module'         => 'IC',
 
1306                                        'sub_module'     => sub { $_[0]->{ioi} },
 
1307                                        'trans_id_field' => 'ioi_id',
 
1308                                        'configs'        => $cvar_configs,
 
1309                                        'column_defs'    => \%column_defs,
 
1310                                        'data'           => $form->{parts});
 
1312   my @subtotal_columns = qw(sellprice listprice lastcost);
 
1313   my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
 
1314   my %totals    = map { $_ => 0 } @subtotal_columns;
 
1316   my $same_item = @{ $form->{parts} } ? $form->{parts}[0]{ $form->{sort} } : undef;
 
1318   my $defaults  = AM->get_defaults();
 
1321   foreach my $ref (@{ $form->{parts} }) {
 
1323     # fresh row, for inserting later
 
1324     my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
 
1326     $ref->{exchangerate} ||= 1;
 
1327     $ref->{price_factor} ||= 1;
 
1328     $ref->{sellprice}     *= $ref->{exchangerate} / $ref->{price_factor};
 
1329     $ref->{listprice}     *= $ref->{exchangerate} / $ref->{price_factor};
 
1330     $ref->{lastcost}      *= $ref->{exchangerate} / $ref->{price_factor};
 
1332     # use this for assemblies
 
1333     my $soldtotal = $bsooqr_mode ? $ref->{soldtotal} : $ref->{onhand};
 
1335     if ($ref->{assemblyitem}) {
 
1336       $row->{partnumber}{align}   = 'right';
 
1337       $row->{soldtotal}{data}     = 0;
 
1338       $soldtotal                  = 0 if ($form->{sold});
 
1341     my $edit_link               = build_std_url('action=edit', 'id=' . E($ref->{id}), 'callback');
 
1342     $row->{partnumber}->{link}  = $edit_link;
 
1343     $row->{description}->{link} = $edit_link;
 
1345     foreach (qw(sellprice listprice lastcost)) {
 
1346       $row->{$_}{data}            = $form->format_amount(\%myconfig, $ref->{$_}, 2);
 
1347       $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
 
1349     foreach ( @pricegroup_columns ) {
 
1350       $row->{$_}{data}            = $form->format_amount(\%myconfig, $ref->{"$_"}, 2);
 
1354     map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
 
1356     $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
 
1358     if (!$ref->{assemblyitem}) {
 
1359       foreach my $col (@subtotal_columns) {
 
1360         $totals{$col}    += $soldtotal * $ref->{$col};
 
1361         $subtotals{$col} += $soldtotal * $ref->{$col};
 
1364       $subtotals{soldtotal} += $soldtotal;
 
1368     if ($ref->{module} eq 'oe') {
 
1369       # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
 
1371       # | ist bestellt  | Von Kunden bestellt |  -> edit_oe_ord_link
 
1372       # | Anfrage       | Angebot             |  -> edit_oe_quo_link
 
1374       my $edit_oe_ord_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'purchase_order' : 'sales_order'), 'id=' . E($ref->{trans_id}), 'callback');
 
1375       my $edit_oe_quo_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'request_quotation' : 'sales_quotation'), 'id=' . E($ref->{trans_id}), 'callback');
 
1377       $row->{ordnumber}{link} = $edit_oe_ord_link;
 
1378       $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
 
1381       $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback');
 
1384     # set properties of images
 
1385     if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
 
1386       $row->{image}{data}     = '';
 
1387       $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
 
1389     map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
 
1391     $row->{notes}{data} = SL::HTML::Util->strip($ref->{notes});
 
1393     $report->add_data($row);
 
1395     my $next_ref = $form->{parts}[$idx + 1];
 
1397     # insert subtotal rows
 
1398     if (($form->{l_subtotal} eq 'Y') &&
 
1400          (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
 
1401       my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
 
1403       if (($form->{searchitems} ne 'assembly') || !$form->{bom}) {
 
1404         $row->{soldtotal}->{data} = $form->format_amount(\%myconfig, $subtotals{soldtotal});
 
1407       map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
 
1408       map { $subtotals{$_} = 0 } ('soldtotal', @subtotal_columns);
 
1410       $report->add_data($row);
 
1412       $same_item = $next_ref->{ $form->{sort} };
 
1418   if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
 
1419     my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
 
1421     map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
 
1423     $report->add_separator();
 
1424     $report->add_data($row);
 
1427   $report->generate_with_headers();
 
1429   $lxdebug->leave_sub();
 
1430 }    #end generate_report
 
1432 sub parts_subtotal {
 
1433   $lxdebug->enter_sub();
 
1435   $auth->assert('part_service_assembly_edit');
 
1438   my ($column_index, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice) = @_;
 
1440   map { $column_data{$_} = "<td> </td>" } @{ $column_index };
 
1441   $$subtotalonhand = 0 if ($form->{searchitems} eq 'assembly' && $form->{bom});
 
1443   $column_data{onhand} =
 
1444       "<th class=listsubtotal align=right>"
 
1445     . $form->format_amount(\%myconfig, $$subtotalonhand)
 
1448   $column_data{linetotalsellprice} =
 
1449       "<th class=listsubtotal align=right>"
 
1450     . $form->format_amount(\%myconfig, $$subtotalsellprice, 2)
 
1452   $column_data{linetotallistprice} =
 
1453       "<th class=listsubtotal align=right>"
 
1454     . $form->format_amount(\%myconfig, $$subtotallistprice, 2)
 
1456   $column_data{linetotallastcost} =
 
1457       "<th class=listsubtotal align=right>"
 
1458     . $form->format_amount(\%myconfig, $$subtotallastcost, 2)
 
1461   $$subtotalonhand    = 0;
 
1462   $$subtotalsellprice = 0;
 
1463   $$subtotallistprice = 0;
 
1464   $$subtotallastcost  = 0;
 
1466   print "<tr class=listsubtotal>";
 
1468   map { print "\n$column_data{$_}" } @{ $column_index };
 
1474   $lxdebug->leave_sub();
 
1478   $lxdebug->enter_sub();
 
1480   $auth->assert('part_service_assembly_details');
 
1482   # show history button
 
1483   $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
 
1484   #/show hhistory button
 
1485   IC->get_part(\%myconfig, \%$form);
 
1487   $form->{"original_partnumber"} = $form->{"partnumber"};
 
1489   my $title      = 'Edit ' . ucfirst $form->{item};
 
1490   $form->{title} = $locale->text($title);
 
1495   $lxdebug->leave_sub();
 
1499   $lxdebug->enter_sub();
 
1501   $auth->assert('part_service_assembly_details');
 
1503   IC->create_links("IC", \%myconfig, \%$form);
 
1506   map({ $form->{selectcurrency} .= "<option>$_\n" } $::form->get_all_currencies());
 
1508   # parts and assemblies have the same links
 
1509   my $item = $form->{item};
 
1510   if ($form->{item} eq 'assembly') {
 
1514   # build the popup menus
 
1515   $form->{taxaccounts} = "";
 
1516   foreach my $key (keys %{ $form->{IC_links} }) {
 
1517     foreach my $ref (@{ $form->{IC_links}{$key} }) {
 
1519       # if this is a tax field
 
1520       if ($key =~ /IC_tax/) {
 
1521         if ($key =~ /\Q$item\E/) {
 
1522           $form->{taxaccounts} .= "$ref->{accno} ";
 
1523           $form->{"IC_tax_$ref->{accno}_description"} =
 
1524             "$ref->{accno}--$ref->{description}";
 
1527             if ($form->{amount}{ $ref->{accno} }) {
 
1528               $form->{"IC_tax_$ref->{accno}"} = "checked";
 
1531             $form->{"IC_tax_$ref->{accno}"} = "checked";
 
1536         $form->{"select$key"} .=
 
1537           "<option $ref->{selected}>$ref->{accno}--$ref->{description}\n";
 
1538         if ($form->{amount}{$key} eq $ref->{accno}) {
 
1539           $form->{$key} = "$ref->{accno}--$ref->{description}";
 
1545   chop $form->{taxaccounts};
 
1547   if (($form->{item} eq "part") || ($form->{item} eq "assembly")) {
 
1548     $form->{selectIC_income}  = $form->{selectIC_sale};
 
1549     $form->{selectIC_expense} = $form->{selectIC_cogs};
 
1550     $form->{IC_income}        = $form->{IC_sale};
 
1551     $form->{IC_expense}       = $form->{IC_cogs};
 
1554   delete $form->{IC_links};
 
1555   delete $form->{amount};
 
1557   $form->get_partsgroup(\%myconfig, { all => 1 });
 
1559   $form->{partsgroup} = "$form->{partsgroup}--$form->{partsgroup_id}";
 
1561   if (@{ $form->{all_partsgroup} }) {
 
1562     $form->{selectpartsgroup} = qq|<option>\n|;
 
1563     map { $form->{selectpartsgroup} .= qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n| } @{ $form->{all_partsgroup} };
 
1566   if ($form->{item} eq 'assembly') {
 
1568     foreach my $i (1 .. $form->{assembly_rows}) {
 
1569       if ($form->{"partsgroup_id_$i"}) {
 
1570         $form->{"partsgroup_$i"} =
 
1571           qq|$form->{"partsgroup_$i"}--$form->{"partsgroup_id_$i"}|;
 
1574     $form->get_partsgroup(\%myconfig);
 
1576     if (@{ $form->{all_partsgroup} }) {
 
1577       $form->{selectassemblypartsgroup} = qq|<option>\n|;
 
1580         $form->{selectassemblypartsgroup} .=
 
1581           qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n|
 
1582       } @{ $form->{all_partsgroup} };
 
1585   $lxdebug->leave_sub();
 
1589   $lxdebug->enter_sub();
 
1591   $auth->assert('part_service_assembly_details');
 
1593   $form->{pg_keys}          = sub { "$_[0]->{partsgroup}--$_[0]->{id}" };
 
1594   $form->{description_area} = ($form->{rows} = $form->numtextrows($form->{description}, 40)) > 1;
 
1595   $form->{notes_rows}       =  max 4, $form->numtextrows($form->{notes}, 40), $form->numtextrows($form->{formel}, 40);
 
1597   map { $form->{"is_$_"}  = ($form->{item} eq $_) } qw(part service assembly);
 
1598   map { $form->{$_}       =~ s/"/"/g;        } qw(unit);
 
1600   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
 
1601                    'partsgroup'    => 'all_partsgroup',
 
1602                    'vendors'       => 'ALL_VENDORS',
 
1603                    'warehouses'    => { 'key'    => 'WAREHOUSES',
 
1604                                         'bins'   => 'BINS', });
 
1605   # leerer wert für Lager und Lagerplatz korrekt einstellt
 
1606   # ID 0 sollte in Ordnung sein, da der Zähler sowieso höher ist
 
1607   my $no_default_bin_entry = { 'id' => '0', description => '--', 'BINS' => [ { id => '0', description => ''} ] };
 
1608   push @ { $form->{WAREHOUSES} }, $no_default_bin_entry;
 
1609   if (my $max = scalar @{ $form->{WAREHOUSES} }) {
 
1610     my ($default_warehouse_id, $default_bin_id);
 
1611     if ($form->{action} eq 'add') { # default only for new entries
 
1612       $default_warehouse_id = $::instance_conf->get_warehouse_id;
 
1613       $default_bin_id       = $::instance_conf->get_bin_id;
 
1615     $form->{warehouse_id} ||= $default_warehouse_id || $form->{WAREHOUSES}->[$max -1]->{id};
 
1616     $form->{bin_id}       ||= $default_bin_id       ||  $form->{WAREHOUSES}->[$max -1]->{BINS}->[0]->{id};
 
1619   $form->{LANGUAGES}        = SL::DB::Manager::Language->get_all_sorted;
 
1620   $form->{translations_map} = { map { ($_->{language_id} => $_) } @{ $form->{translations} || [] } };
 
1622   IC->retrieve_buchungsgruppen(\%myconfig, $form);
 
1623   @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
 
1625   if (($form->{partnumber} ne '') && !SL::TransNumber->new(number => $form->{partnumber}, type => $form->{item}, id => $form->{id})->is_unique) {
 
1626     flash('info', $::locale->text('This partnumber is not unique. You should change it.'));
 
1629   my $units = AM->retrieve_units(\%myconfig, $form);
 
1630   $form->{ALL_UNITS} = [ map +{ name => $_ }, sort { $units->{$a}{sortkey} <=> $units->{$b}{sortkey} } keys %$units ];
 
1632   $form->{defaults} = AM->get_defaults();
 
1634   $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'IC', 'trans_id' => $form->{id});
 
1636   my ($null, $partsgroup_id) = split /--/, $form->{partsgroup};
 
1638   CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}, show_disabled_message => 1, partsgroup_id => $partsgroup_id)
 
1639     if (scalar @{ $form->{CUSTOM_VARIABLES} });
 
1641   $::request->layout->use_javascript("${_}.js") for qw(ckeditor/ckeditor ckeditor/adapters/jquery);
 
1643   #print $form->parse_html_template('ic/form_header', { ALL_PRICE_FACTORS => $form->{ALL_PRICE_FACTORS},
 
1644   #                                                     ALL_UNITS         => $form->{ALL_UNITS},
 
1645   #                                                     BUCHUNGSGRUPPEN   => $form->{BUCHUNGSGRUPPEN},
 
1646   #                                                     payment_terms     => $form->{payment_terms},
 
1647   #                                                     all_partsgroup    => $form->{all_partsgroup}});
 
1649   $form->{show_edit_buttons} = $main::auth->check_right($form->{login}, 'part_service_assembly_edit');
 
1651   print $form->parse_html_template('ic/form_header');
 
1652   $lxdebug->leave_sub();
 
1656   $lxdebug->enter_sub();
 
1658   $auth->assert('part_service_assembly_details');
 
1660   print $form->parse_html_template('ic/form_footer');
 
1662   $lxdebug->leave_sub();
 
1666   $lxdebug->enter_sub();
 
1669   my @mm_data = grep { any { $_ ne '' } @$_{qw(make model)} } map +{ make => $form->{"make_$_"}, model => $form->{"model_$_"}, lastcost => $form->{"lastcost_$_"}, lastupdate => $form->{"lastupdate_$_"}, sortorder => $form->{"sortorder_$_"} }, 1 .. $numrows;
 
1670   delete @{$form}{grep { m/^make_\d+/ || m/^model_\d+/ } keys %{ $form }};
 
1671   print $form->parse_html_template('ic/makemodel', { MM_DATA => [ @mm_data, {} ], mm_rows => scalar @mm_data + 1 });
 
1673   $lxdebug->leave_sub();
 
1677   $lxdebug->enter_sub();
 
1680   my ($nochange, $callback, $previousform, $linetotal, $line_purchase_price, $href);
 
1682   @column_index = qw(runningnumber qty unit bom partnumber description partsgroup lastcost total);
 
1684   if ($form->{previousform}) {
 
1686     @column_index = qw(qty unit bom partnumber description partsgroup total);
 
1690     $form->{old_callback} = $form->{callback};
 
1691     $callback             = $form->{callback};
 
1692     $form->{callback}     = "$form->{script}?action=display_form";
 
1695     map { delete $form->{$_} } qw(action header);
 
1697     # save form variables in a previousform variable
 
1698     my %form_to_save = map   { ($_ => m/^ (?: listprice | sellprice | lastcost ) $/x ? $form->format_amount(\%myconfig, $form->{$_}) : $form->{$_}) }
 
1700     $previousform    = $::auth->save_form_in_session(form => \%form_to_save);
 
1702     $form->{callback} = $callback;
 
1703     $form->{assemblytotal} = 0;
 
1704     $form->{assembly_purchase_price_total} = 0;
 
1705     $form->{weight}        = 0;
 
1709    runningnumber => { text =>  $locale->text('No.'),              nowrap => 1, width => '5%',  align => 'left',},
 
1710    qty           => { text =>  $locale->text('Qty'),              nowrap => 1, width => '10%', align => 'left',},
 
1711    unit          => { text =>  $locale->text('Unit'),             nowrap => 1, width => '5%',  align => 'left',},
 
1712    partnumber    => { text =>  $locale->text('Part Number'),      nowrap => 1, width => '20%', align => 'left',},
 
1713    description   => { text =>  $locale->text('Part Description'), nowrap => 1, width => '50%', align => 'left',},
 
1714    lastcost      => { text =>  $locale->text('Purchase Prices'),  nowrap => 1, width => '50%', align => 'right',},
 
1715    total         => { text =>  $locale->text('Sale Prices'),      nowrap => 1,                 align => 'right',},
 
1716    bom           => { text =>  $locale->text('BOM'),                                           align => 'center',},
 
1717    partsgroup    => { text =>  $locale->text('Group'),                                         align => 'left',},
 
1722   for my $i (1 .. $numrows) {
 
1723     my (%row, @row_hiddens);
 
1725     $form->{"partnumber_$i"} =~ s/\"/"/g;
 
1727     $linetotal           = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
 
1728     $line_purchase_price = $form->round_amount($form->{"lastcost_$i"} *  $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
 
1729     $form->{assemblytotal}                  += $linetotal;
 
1730     $form->{assembly_purchase_price_total}  += $line_purchase_price;
 
1731     $form->{"qty_$i"}    = $form->format_amount(\%myconfig, $form->{"qty_$i"});
 
1732     $linetotal           = $form->format_amount(\%myconfig, $linetotal, 2);
 
1733     $line_purchase_price = $form->format_amount(\%myconfig, $line_purchase_price, 2);
 
1734     $href                = build_std_url("action=edit", qq|id=$form->{"id_$i"}|, "rowcount=$numrows", "currow=$i", "previousform=$previousform");
 
1735     map { $row{$_}{data} = "" } qw(qty unit partnumber description bom partsgroup runningnumber);
 
1738     if (($i >= 1) && ($i == $numrows)) {
 
1739       if (!$form->{previousform}) {
 
1740         $row{partnumber}{data}  = qq|<input name="partnumber_$i" size=15 value="$form->{"partnumber_$i"}">|;
 
1741         $row{qty}{data}         = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
 
1742         $row{description}{data} = qq|<input name="description_$i" size=40 value="$form->{"description_$i"}">|;
 
1743         $row{partsgroup}{data}  = qq|<input name="partsgroup_$i" size=10 value="$form->{"partsgroup_$i"}">|;
 
1747       if ($form->{previousform}) {
 
1748         push @row_hiddens,          qw(qty bom);
 
1749         $row{partnumber}{data}    = $form->{"partnumber_$i"};
 
1750         $row{qty}{data}           = $form->{"qty_$i"};
 
1751         $row{bom}{data}           = $form->{"bom_$i"} ? "x" : " ";
 
1752         $row{qty}{align}          = 'right';
 
1754         $row{partnumber}{data}    = qq|$form->{"partnumber_$i"}|;
 
1755         $row{partnumber}{link}     = $href;
 
1756         $row{qty}{data}           = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
 
1757         $row{runningnumber}{data} = qq|<input name="runningnumber_$i" size=3 value="$i">|;
 
1758         $row{bom}{data}   = sprintf qq|<input name="bom_$i" type=checkbox class=checkbox value=1 %s>|,
 
1759                                        $form->{"bom_$i"} ? 'checked' : '';
 
1761       push @row_hiddens,        qw(unit description partnumber partsgroup);
 
1762       $row{unit}{data}        = $form->{"unit_$i"};
 
1763       #Bei der Artikelbeschreibung und Warengruppe können Sonderzeichen verwendet
 
1764       #werden, die den HTML Code stören. Daher sollen diese im Template escaped werden
 
1765       #dies geschieht, wenn die Variable escape gesetzt ist
 
1766       $row{description}{data}   = $form->{"description_$i"};
 
1767       $row{description}{escape} = 1;
 
1768       $row{partsgroup}{data}    = $form->{"partsgroup_$i"};
 
1769       $row{partsgroup}{escape}  = 1;
 
1770       $row{bom}{align}          = 'center';
 
1773     $row{lastcost}{data}      = $line_purchase_price;
 
1774     $row{total}{data}         = $linetotal;
 
1775     $row{lastcost}{align}     = 'right';
 
1776     $row{total}{align}        = 'right';
 
1777     $row{deliverydate}{align} = 'right';
 
1779     push @row_hiddens, qw(id sellprice lastcost weight price_factor_id price_factor);
 
1780     $row{hiddens} = [ map +{ name => "${_}_$i", value => $form->{"${_}_$i"} }, @row_hiddens ];
 
1785   print $form->parse_html_template('ic/assembly_row', { COLUMNS => \@column_index, ROWS => \@ROWS, HEADER => \%header });
 
1787   $lxdebug->leave_sub();
 
1791   $lxdebug->enter_sub();
 
1793   $auth->assert('part_service_assembly_edit');
 
1795   # parse pricegroups. and no, don't rely on check_form for this...
 
1796   map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
 
1797   $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) for qw(sellprice listprice ve gv);
 
1799   if ($form->{item} eq 'part') {
 
1800     $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) for qw(weight rop);
 
1803   # same for makemodel lastcosts
 
1804   # but parse_amount not necessary for assembly component lastcosts
 
1805   unless ($form->{item} eq "assembly") {
 
1806     map { $form->{"lastcost_$_"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$_"}) } 1 .. $form->{"makemodel_rows"};
 
1807     $form->{lastcost} = $form->parse_amount(\%myconfig, $form->{lastcost});
 
1810   if ($form->{item} eq "assembly") {
 
1811     my $i = $form->{assembly_rows};
 
1813     # if last row is empty check the form otherwise retrieve item
 
1814     if (   ($form->{"partnumber_$i"} eq "")
 
1815         && ($form->{"description_$i"} eq "")
 
1816         && ($form->{"partsgroup_$i"}  eq "")) {
 
1822       IC->assembly_item(\%myconfig, \%$form);
 
1824       my $rows = scalar @{ $form->{item_list} };
 
1827         $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
 
1830           $form->{makemodel_rows}--;
 
1831           select_item(mode => 'IC');
 
1834           map { $form->{item_list}[$i]{$_} =~ s/\"/"/g }
 
1835             qw(partnumber description unit partsgroup);
 
1836           map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} }
 
1837             keys %{ $form->{item_list}[0] };
 
1838           $form->{"runningnumber_$i"} = $form->{assembly_rows};
 
1839           $form->{assembly_rows}++;
 
1847         $form->{rowcount} = $i;
 
1848         $form->{assembly_rows}++;
 
1855   } elsif (($form->{item} eq 'part') || ($form->{item} eq 'service')) {
 
1859   $lxdebug->leave_sub();
 
1863   $lxdebug->enter_sub();
 
1865   $auth->assert('part_service_assembly_edit');
 
1867   my ($parts_id, %newform, $amount, $callback);
 
1869   # check if there is a part number - commented out, cause there is an automatic allocation of numbers
 
1870   # $form->isblank("partnumber", $locale->text(ucfirst $form->{item}." Part Number missing!"));
 
1872   # check if there is a description
 
1873   $form->isblank("description", $locale->text("Part Description missing!"));
 
1875   $form->error($locale->text("Inventory quantity must be zero before you can set this $form->{item} obsolete!"))
 
1876     if $form->{obsolete} && $form->{onhand} * 1 && $form->{item} ne 'service';
 
1878   if (!$form->{buchungsgruppen_id}) {
 
1879     $form->error($locale->text("Parts must have an entry type.") . " " .
 
1880      $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.")
 
1884   $form->error($locale->text('Description must not be empty!')) unless $form->{description};
 
1885   $form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
 
1887   # undef warehouse_id if the empty value is selected
 
1888   if ( ($form->{warehouse_id} == 0) && ($form->{bin_id} == 0) ) {
 
1889     undef $form->{warehouse_id};
 
1890     undef $form->{bin_id};
 
1893   if (IC->save(\%myconfig, \%$form) == 3) {
 
1894     $form->error($locale->text('Partnumber not unique!'));
 
1896   # saving the history
 
1897   if(!exists $form->{addition}) {
 
1898     $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
 
1899     $form->{addition} = "SAVED";
 
1900     $form->save_history;
 
1902   # /saving the history
 
1903   $parts_id = $form->{id};
 
1906   # load previous variables
 
1907   if ($form->{previousform}) {
 
1909     # save the new form variables before splitting previousform
 
1910     map { $newform{$_} = $form->{$_} } keys %$form;
 
1912     # don't trample on previous variables
 
1913     map { delete $form->{$_} } keys %newform;
 
1915     my $ic_cvar_configs = CVar->get_configs(module => 'IC');
 
1916     my @ic_cvar_fields  = map { "cvar_$_->{name}" } @{ $ic_cvar_configs };
 
1918     # restore original values
 
1919     $::auth->restore_form_from_session($newform{previousform}, form => $form);
 
1920     $form->{taxaccounts} = $newform{taxaccount2};
 
1922     if ($form->{item} eq 'assembly') {
 
1924       # undo number formatting
 
1925       map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
 
1926         qw(weight listprice sellprice rop);
 
1928       $form->{assembly_rows}--;
 
1929       if ($newform{currow}) {
 
1930         $i = $newform{currow};
 
1932         $i = $form->{assembly_rows};
 
1934       $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"} > 0);
 
1936       $form->{sellprice} -= $form->{"sellprice_$i"} * $form->{"qty_$i"};
 
1937       $form->{weight}    -= $form->{"weight_$i"} * $form->{"qty_$i"};
 
1939       # change/add values for assembly item
 
1940       map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit weight listprice sellprice inventory_accno income_accno expense_accno price_factor_id);
 
1941       map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
 
1943       # das ist __voll__ bekloppt, dass so auszurechnen jb 22.5.09
 
1944       #$form->{sellprice} += $form->{"sellprice_$i"} * $form->{"qty_$i"};
 
1945       $form->{weight}    += $form->{"weight_$i"} * $form->{"qty_$i"};
 
1949       # set values for last invoice/order item
 
1950       $i = $form->{rowcount};
 
1951       $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"} > 0);
 
1953       map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit listprice inventory_accno income_accno expense_accno sellprice lastcost price_factor_id);
 
1954       map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
 
1956       $form->{"longdescription_$i"} = $newform{notes};
 
1958       $form->{"sellprice_$i"} = $newform{lastcost} if ($form->{vendor_id});
 
1960       if ($form->{exchangerate} != 0) {
 
1961         $form->{"sellprice_$i"} /= $form->{exchangerate};
 
1964       map { $form->{"taxaccounts_$i"} .= "$_ " } split / /, $newform{taxaccount};
 
1965       chop $form->{"taxaccounts_$i"};
 
1966       foreach my $item (qw(description rate taxnumber)) {
 
1967         my $index = $form->{"taxaccounts_$i"} . "_$item";
 
1968         $form->{$index} = $newform{$index};
 
1971       # credit remaining calculation
 
1972       $amount = $form->{"sellprice_$i"} * (1 - $form->{"discount_$i"} / 100) * $form->{"qty_$i"};
 
1974       map { $form->{"${_}_base"} += $amount } (split / /, $form->{"taxaccounts_$i"});
 
1975       map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{"taxaccounts_$i"} if !$form->{taxincluded};
 
1977       $form->{creditremaining} -= $amount;
 
1979       # redo number formatting, because invoice parse them!
 
1980       map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}) } qw(weight listprice sellprice lastcost rop);
 
1983     $form->{"id_$i"} = $parts_id;
 
1985     # Get the actual price factor (not just the ID) for the marge calculation.
 
1986     $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
 
1987     foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
 
1988       next if ($pfac->{id} != $newform{price_factor_id});
 
1989       $form->{"marge_price_factor_$i"} = $pfac->{factor};
 
1992     delete $form->{ALL_PRICE_FACTORS};
 
1994     delete $form->{action};
 
1996     # restore original callback
 
1997     $callback = $form->unescape($form->{callback});
 
1998     $form->{callback} = $form->unescape($form->{old_callback});
 
1999     delete $form->{old_callback};
 
2001     $form->{makemodel_rows}--;
 
2003     # put callback together
 
2004     foreach my $key (keys %$form) {
 
2006       # do single escape for Apache 2.0
 
2007       my $value = $form->escape($form->{$key}, 1);
 
2008       $callback .= qq|&$key=$value|;
 
2010     $form->{callback} = $callback;
 
2016   $lxdebug->leave_sub();
 
2020   $lxdebug->enter_sub();
 
2022   $auth->assert('part_service_assembly_edit');
 
2024   # saving the history
 
2025   if(!exists $form->{addition}) {
 
2026     $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
 
2027     $form->{addition} = "SAVED AS NEW";
 
2028     $form->save_history;
 
2030   # /saving the history
 
2032   if ($form->{"original_partnumber"} &&
 
2033       ($form->{"partnumber"} eq $form->{"original_partnumber"})) {
 
2034     $form->{partnumber} = "";
 
2037   $lxdebug->leave_sub();
 
2041   $lxdebug->enter_sub();
 
2043   $auth->assert('part_service_assembly_edit');
 
2045   # saving the history
 
2046   if(!exists $form->{addition}) {
 
2047     $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
 
2048     $form->{addition} = "DELETED";
 
2049     $form->save_history;
 
2051   # /saving the history
 
2052   my $rc = IC->delete(\%myconfig, \%$form);
 
2055   $form->redirect($locale->text('Item deleted!')) if ($rc > 0);
 
2056   $form->error($locale->text('Cannot delete item!'));
 
2058   $lxdebug->leave_sub();
 
2062   $lxdebug->enter_sub();
 
2064   $auth->assert('part_service_assembly_details');
 
2069     pricegroup    => $form->{"pricegroup_$_"},
 
2070     pricegroup_id => $form->{"pricegroup_id_$_"},
 
2071     price         => $form->{"price_$_"},
 
2074   print $form->parse_html_template('ic/price_row', { PRICES => \@PRICES });
 
2076   $lxdebug->leave_sub();
 
2079 sub ajax_autocomplete {
 
2080   $main::lxdebug->enter_sub();
 
2082   my $form     = $main::form;
 
2083   my %myconfig = %main::myconfig;
 
2085   $form->{column}          = 'description'     unless $form->{column} =~ /^partnumber|description$/;
 
2086   $form->{$form->{column}} = $form->{q}           || '';
 
2087   $form->{limit}           = ($form->{limit} * 1) || 10;
 
2088   $form->{searchitems}   ||= '';
 
2090   my @results = IC->all_parts(\%myconfig, $form);
 
2092   print $form->ajax_response_header(),
 
2093         $form->parse_html_template('ic/ajax_autocomplete');
 
2095   $main::lxdebug->leave_sub();
 
2098 sub back_to_record {
 
2102   delete @{$::form}{qw(action action_add action_back_to_record back_sub description item notes partnumber sellprice taxaccount2 unit vc)};
 
2104   $::auth->restore_form_from_session($::form->{previousform}, clobber => 1);
 
2105   $::form->{rowcount}--;
 
2106   $::form->{action}   = 'display_form';
 
2107   $::form->{callback} = $::form->{script} . '?' . join('&', map { $::form->escape($_) . '=' . $::form->escape($::form->{$_}) } sort keys %{ $::form });
 
2111 sub continue { call_sub($form->{"nextsub"}); }
 
2114   my $action = first { $::form->{"action_${_}"} } qw(add back_to_record);
 
2115   $::form->error($::locale->text('No action defined.')) unless $action;
 
2117   $::form->{dispatched_action} = $action;