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., 51 Franklin Street, Fifth Floor, Boston,
 
  29 #======================================================================
 
  31 # Inventory Control module
 
  33 #======================================================================
 
  35 use POSIX qw(strftime);
 
  36 use List::Util qw(first max);
 
  37 use List::MoreUtils qw(any);
 
  42 use SL::Helper::Flash qw(flash);
 
  44 use SL::ReportGenerator;
 
  52 our ($form, $locale, %myconfig, $lxdebug, $auth);
 
  54 require "bin/mozilla/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_details');
 
  83   $form->{revers}       = 0;  # switch for backward sorting
 
  84   $form->{lastsort}     = ""; # memory for which table was sort at last time
 
  85   $form->{ndxs_counter} = 0;  # counter for added entries to top100
 
  87   $form->{title} = (ucfirst $form->{searchitems}) . "s";
 
  88   $form->{title} =~ s/ys$/ies/;
 
  89   $form->{title} = $locale->text($form->{title});
 
  91   $form->{CUSTOM_VARIABLES}                  = CVar->get_configs('module' => 'IC');
 
  92   ($form->{CUSTOM_VARIABLES_FILTER_CODE},
 
  93    $form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables'      => $form->{CUSTOM_VARIABLES},
 
  94                                                                            'include_prefix' => 'l_',
 
  95                                                                            'include_value'  => 'Y');
 
  97   setup_ic_search_action_bar();
 
 100   $form->get_lists('partsgroup'    => 'ALL_PARTSGROUPS');
 
 101   print $form->parse_html_template('ic/search');
 
 103   $lxdebug->leave_sub();
 
 107   $::lxdebug->enter_sub();
 
 109   $::auth->assert('part_service_assembly_edit');
 
 111   $::form->{l_soldtotal} = "Y";
 
 112   $::form->{sort}        = "soldtotal";
 
 113   $::form->{lastsort}    = "soldtotal";
 
 115   $::form->{l_qty}       = undef;
 
 116   $::form->{l_linetotal} = undef;
 
 117   $::form->{l_number}    = "Y";
 
 118   $::form->{number}      = "position";
 
 120   unless (   $::form->{bought}
 
 123           || $::form->{quoted}) {
 
 124     $::form->{bought} = $::form->{sold} = 1;
 
 129   $lxdebug->leave_sub();
 
 134 # Warning, deep magic ahead.
 
 135 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
 
 137 # flags coming from the form:
 
 139 #  searchitems=part revers=0 lastsort=''
 
 142 # partnumber ean description partsgroup classification serialnumber make model drawing microfiche
 
 143 # transdatefrom transdateto
 
 146 #  itemstatus = active | onhand | short | obsolete | orphaned
 
 147 #  action     = continue | top100
 
 150 #  bought sold onorder ordered rfq quoted
 
 151 #  l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
 
 152 #  l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
 
 153 #  l_partsgroup l_subtotal l_soldtotal l_deliverydate l_pricegroups
 
 156 #  nextsub revers lastsort sort ndxs_counter
 
 158 sub generate_report {
 
 159   $lxdebug->enter_sub();
 
 161   $auth->assert('part_service_assembly_details');
 
 163   my ($revers, $lastsort, $description);
 
 165   my $cvar_configs = CVar->get_configs('module' => 'IC');
 
 167   $form->{title} = $locale->text('Articles');
 
 170     'deliverydate'       => { 'text' => $locale->text('deliverydate'), },
 
 171     'description'        => { 'text' => $locale->text('Part Description'), },
 
 172     'notes'              => { 'text' => $locale->text('Notes'), },
 
 173     'drawing'            => { 'text' => $locale->text('Drawing'), },
 
 174     'ean'                => { 'text' => $locale->text('EAN'), },
 
 175     'image'              => { 'text' => $locale->text('Image'), },
 
 176     'insertdate'         => { 'text' => $locale->text('Insert Date'), },
 
 177     'invnumber'          => { 'text' => $locale->text('Invoice Number'), },
 
 178     'lastcost'           => { 'text' => $locale->text('Last Cost'), },
 
 179     'linetotallastcost'  => { 'text' => $locale->text('Extended'), },
 
 180     'linetotallistprice' => { 'text' => $locale->text('Extended'), },
 
 181     'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
 
 182     'listprice'          => { 'text' => $locale->text('List Price'), },
 
 183     'microfiche'         => { 'text' => $locale->text('Microfiche'), },
 
 184     'name'               => { 'text' => $locale->text('Name'), },
 
 185     'onhand'             => { 'text' => $locale->text('Stocked Qty'), },
 
 186     'ordnumber'          => { 'text' => $locale->text('Order Number'), },
 
 187     'partnumber'         => { 'text' => $locale->text('Part Number'), },
 
 188     'partsgroup'         => { 'text' => $locale->text('Partsgroup'), },
 
 189     'priceupdate'        => { 'text' => $locale->text('Updated'), },
 
 190     'quonumber'          => { 'text' => $locale->text('Quotation'), },
 
 191     'rop'                => { 'text' => $locale->text('ROP'), },
 
 192     'sellprice'          => { 'text' => $locale->text('Sell Price'), },
 
 193     'serialnumber'       => { 'text' => $locale->text('Serial Number'), },
 
 194     'soldtotal'          => { 'text' => $locale->text('Qty in Selected Records'), },
 
 195     'name'               => { 'text' => $locale->text('Name in Selected Records'), },
 
 196     'transdate'          => { 'text' => $locale->text('Transdate Record'), },
 
 197     'unit'               => { 'text' => $locale->text('Unit'), },
 
 198     'weight'             => { 'text' => $locale->text('Weight'), },
 
 199     'shop'               => { 'text' => $locale->text('Shop article'), },
 
 200     'type_and_classific' => { 'text' => $locale->text('Type'), },
 
 201     'projectnumber'      => { 'text' => $locale->text('Project Number'), },
 
 202     'projectdescription' => { 'text' => $locale->text('Project Description'), },
 
 203     'warehouse'          => { 'text' => $locale->text('Default Warehouse'), },
 
 204     'bin'                => { 'text' => $locale->text('Default Bin'), },
 
 207   $revers     = $form->{revers};
 
 208   $lastsort   = $form->{lastsort};
 
 210   # sorting and direction of sorting
 
 211   # ToDO: change this to the simpler field+direction method
 
 212   if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
 
 214     $form->{lastsort} = "partnumber";
 
 215     $form->{sort}     = "partnumber";
 
 217     if ($form->{lastsort} eq $form->{sort}) {
 
 218       $form->{revers} = 1 - $form->{revers};
 
 221       $form->{lastsort} = $form->{sort};
 
 225   # special case if we have a serialnumber limit search
 
 226   # serialnumbers are only given in invoices and orders,
 
 227   # so they can only pop up in bought, sold, rfq, and quoted stuff
 
 228   $form->{no_sn_joins} = 'Y' if (   !$form->{bought} && !$form->{sold}
 
 229                                  && !$form->{rfq}    && !$form->{quoted}
 
 230                                  && ($form->{l_serialnumber} || $form->{serialnumber}));
 
 232   # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
 
 233   # if any of these are ticked the behavior changes slightly for lastcost
 
 234   # since all those are aggregation checks for the legder tables this is an internal switch
 
 235   # refered to as ledgerchecks
 
 236   $form->{ledgerchecks} = 'Y' if (   $form->{bought} || $form->{sold} || $form->{onorder}
 
 237                                   || $form->{ordered} || $form->{rfq} || $form->{quoted});
 
 239   # if something should be activated if something else is active, enter it here
 
 241     onhand       => [ qw(l_onhand) ],
 
 242     short        => [ qw(l_onhand) ],
 
 243     onorder      => [ qw(l_ordnumber) ],
 
 244     ordered      => [ qw(l_ordnumber) ],
 
 245     rfq          => [ qw(l_quonumber) ],
 
 246     quoted       => [ qw(l_quonumber) ],
 
 247     bought       => [ qw(l_invnumber) ],
 
 248     sold         => [ qw(l_invnumber) ],
 
 249     ledgerchecks => [ qw(l_name) ],
 
 250     serialnumber => [ qw(l_serialnumber) ],
 
 251     no_sn_joins  => [ qw(bought sold) ],
 
 254   # get name of partsgroup if id is given
 
 256   if ($form->{partsgroup_id}) {
 
 257     my $pg = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load;
 
 258     $pg_name = $pg->{'partsgroup'};
 
 261   # these strings get displayed at the top of the results to indicate the user which switches were used
 
 263     active        => $locale->text('Active'),
 
 264     obsolete      => $locale->text('Obsolete'),
 
 265     orphaned      => $locale->text('Orphaned'),
 
 266     onhand        => $locale->text('On Hand'),
 
 267     short         => $locale->text('Short'),
 
 268     onorder       => $locale->text('On Order'),
 
 269     ordered       => $locale->text('Ordered'),
 
 270     rfq           => $locale->text('RFQ'),
 
 271     quoted        => $locale->text('Quoted'),
 
 272     bought        => $locale->text('Bought'),
 
 273     sold          => $locale->text('Sold'),
 
 274     transdatefrom => $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
 
 275     transdateto   => $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
 
 276     partnumber    => $locale->text('Part Number')      . ": '$form->{partnumber}'",
 
 277     partsgroup    => $locale->text('Partsgroup')       . ": '$form->{partsgroup}'",
 
 278     partsgroup_id => $locale->text('Partsgroup')       . ": '$pg_name'",
 
 279     serialnumber  => $locale->text('Serial Number')    . ": '$form->{serialnumber}'",
 
 280     description   => $locale->text('Part Description') . ": '$form->{description}'",
 
 281     make          => $locale->text('Make')             . ": '$form->{make}'",
 
 282     model         => $locale->text('Model')            . ": '$form->{model}'",
 
 283     drawing       => $locale->text('Drawing')          . ": '$form->{drawing}'",
 
 284     microfiche    => $locale->text('Microfiche')       . ": '$form->{microfiche}'",
 
 285     l_soldtotal   => $locale->text('Qty in Selected Records'),
 
 286     ean           => $locale->text('EAN')              . ": '$form->{ean}'",
 
 287     insertdatefrom => $locale->text('Insert Date') . ": " . $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1),
 
 288     insertdateto   => $locale->text('Insert Date') . ": " . $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1),
 
 291   my @itemstatus_keys = qw(active obsolete orphaned onhand short);
 
 292   my @callback_keys   = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
 
 293                            drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto insertdatefrom insertdateto ean shop all);
 
 295   # calculate dependencies
 
 296   for (@itemstatus_keys, @callback_keys) {
 
 297     next if ($form->{itemstatus} ne $_ && !$form->{$_});
 
 298     map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
 
 301   # generate callback and optionstrings
 
 303   for my  $key (@itemstatus_keys, @callback_keys) {
 
 304     next if ($form->{itemstatus} ne $key && !$form->{$key});
 
 305     push @options, $optiontexts{$key};
 
 308   # special case for lastcost
 
 309   if ($form->{ledgerchecks}){
 
 310     # ledgerchecks don't know about sellprice or lastcost. they just return a
 
 311     # price. so rename sellprice to price, and drop lastcost.
 
 312     $column_defs{sellprice}{text} = $locale->text('Price');
 
 313     $form->{l_lastcost} = ""
 
 316   if ($form->{description}) {
 
 317     $description = $form->{description};
 
 318     $description =~ s/\n/<br>/g;
 
 321   if ($form->{l_linetotal}) {
 
 322     $form->{l_qty} = "Y";
 
 323     $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
 
 324     $form->{l_linetotallastcost}  = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if  $form->{l_lastcost};
 
 325     $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
 
 327   $form->{"l_type_and_classific"} = "Y";
 
 329   if ($form->{l_service} && !$form->{l_assembly} && !$form->{l_part}) {
 
 331     # remove warehouse, bin, weight and rop from list
 
 332     map { $form->{"l_$_"} = "" } qw(bin weight rop warehouse);
 
 334     $form->{l_onhand} = "";
 
 336     # qty is irrelevant unless bought or sold
 
 342         || $form->{quoted}) {
 
 343 #      $form->{l_onhand} = "Y";
 
 345       $form->{l_linetotalsellprice} = "";
 
 346       $form->{l_linetotallastcost}  = "";
 
 350   # soldtotal doesn't make sense with more than one bsooqr option.
 
 351   # so reset it to sold (the most common option), and issue a warning
 
 353   # also it doesn't make sense without bsooqr. disable and issue a warning too
 
 354   my @bsooqr = qw(sold bought onorder ordered rfq quoted);
 
 355   my $bsooqr_mode = grep { $form->{$_} } @bsooqr;
 
 356   if ($form->{l_subtotal} && 1 < $bsooqr_mode) {
 
 357     my $enabled       = first { $form->{$_} } @bsooqr;
 
 358     $form->{$_}       = ''   for @bsooqr;
 
 359     $form->{$enabled} = 'Y';
 
 361     push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
 
 363   if ($form->{l_soldtotal} && !$bsooqr_mode) {
 
 364     delete $form->{l_soldtotal};
 
 366     flash('warning', $::locale->text('Soldtotal does not make sense without any bsooqr options'));
 
 368   if ($form->{l_soldtotal} && ($form->{l_warehouse} || $form->{l_bin})) {
 
 369     delete $form->{"l_$_"} for  qw(bin warehouse);
 
 370     flash('warning', $::locale->text('Sorry, I am too stupid to figure out the default warehouse/bin and the sold qty. I drop the default warehouse/bin option.'));
 
 372   if ($form->{l_name} && !$bsooqr_mode) {
 
 373     delete $form->{l_name};
 
 375     flash('warning', $::locale->text('Name does not make sense without any bsooqr options'));
 
 377   IC->all_parts(\%myconfig, \%$form);
 
 380     partnumber type_and_classific description notes partsgroup warehouse bin
 
 381     onhand rop soldtotal unit listprice
 
 382     linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
 
 383     priceupdate weight image drawing microfiche invnumber ordnumber quonumber
 
 384     transdate name serialnumber deliverydate ean projectnumber projectdescription
 
 388   my $pricegroups = SL::DB::Manager::Pricegroup->get_all_sorted;
 
 389   my @pricegroup_columns;
 
 390   my %column_defs_pricegroups;
 
 391   if ($form->{l_pricegroups}) {
 
 392     @pricegroup_columns      = map { "pricegroup_" . $_->id } @{ $pricegroups };
 
 393     %column_defs_pricegroups = map {
 
 394       "pricegroup_" . $_->id => {
 
 395         text    => $::locale->text('Pricegroup') . ' ' . $_->pricegroup,
 
 400   push @columns, @pricegroup_columns;
 
 402   my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
 
 403   my @searchable_custom_variables  = grep { $_->{searchable} }  @{ $cvar_configs };
 
 404   my %column_defs_cvars            = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
 
 406   push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
 
 408   %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
 
 409   map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
 
 410   map { $column_defs{$_}->{align}   = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal shop), @pricegroup_columns;
 
 412   my @hidden_variables = (
 
 413     qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups insertdatefrom insertdateto),
 
 414     qw(l_type_and_classific classification_id l_part l_service l_assembly l_assortment),
 
 417     map({ "cvar_$_->{name}" } @searchable_custom_variables),
 
 418     map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
 
 419     map({ "l_$_" } @columns),
 
 422   my $callback         = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
 
 424   my @sort_full        = qw(partnumber description onhand soldtotal deliverydate insertdate shop);
 
 425   my @sort_no_revers   = qw(partsgroup priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
 
 427   foreach my $col (@sort_full) {
 
 428     $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
 
 430   map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
 
 432   # add order to callback
 
 433   $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
 
 435   my $report = SL::ReportGenerator->new(\%myconfig, $form);
 
 437   my %attachment_basenames = (
 
 438     'part'     => $locale->text('part_list'),
 
 439     'service'  => $locale->text('service_list'),
 
 440     'assembly' => $locale->text('assembly_list'),
 
 441     'article'  => $locale->text('article_list'),
 
 444   $report->set_options('raw_top_info_text'     => $form->parse_html_template('ic/generate_report_top', { options => \@options }),
 
 445                        'raw_bottom_info_text'  => $form->parse_html_template('ic/generate_report_bottom' ,
 
 446                                                   { PART_CLASSIFICATIONS => SL::DB::Manager::PartClassification->get_all_sorted }),
 
 447                        'output_format'         => 'HTML',
 
 448                        'title'                 => $form->{title},
 
 449                        'attachment_basename'   => 'article_list' . strftime('_%Y%m%d', localtime time),
 
 451   $report->set_options_from_form();
 
 452   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
 
 454   $report->set_columns(%column_defs);
 
 455   $report->set_column_order(@columns);
 
 457   $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
 
 459   $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
 
 461   CVar->add_custom_variables_to_report('module'         => 'IC',
 
 462                                        'trans_id_field' => 'id',
 
 463                                        'configs'        => $cvar_configs,
 
 464                                        'column_defs'    => \%column_defs,
 
 465                                        'data'           => $form->{parts});
 
 467   CVar->add_custom_variables_to_report('module'         => 'IC',
 
 468                                        'sub_module'     => sub { $_[0]->{ioi} },
 
 469                                        'trans_id_field' => 'ioi_id',
 
 470                                        'configs'        => $cvar_configs,
 
 471                                        'column_defs'    => \%column_defs,
 
 472                                        'data'           => $form->{parts});
 
 474   my @subtotal_columns = qw(sellprice listprice lastcost);
 
 475   my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
 
 476   my %totals    = map { $_ => 0 } @subtotal_columns;
 
 478   my $same_item = @{ $form->{parts} } ? $form->{parts}[0]{ $form->{sort} } : undef;
 
 480   my $defaults  = AM->get_defaults();
 
 483   foreach my $ref (@{ $form->{parts} }) {
 
 485     # fresh row, for inserting later
 
 486     my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
 
 488     $ref->{exchangerate} ||= 1;
 
 489     $ref->{price_factor} ||= 1;
 
 490     $ref->{sellprice}     *= $ref->{exchangerate} / $ref->{price_factor};
 
 491     $ref->{listprice}     *= $ref->{exchangerate} / $ref->{price_factor};
 
 492     $ref->{lastcost}      *= $ref->{exchangerate} / $ref->{price_factor};
 
 494     # use this for assemblies
 
 495     my $soldtotal = $bsooqr_mode ? $ref->{soldtotal} : $ref->{onhand};
 
 497     if ($ref->{assemblyitem}) {
 
 498       $row->{partnumber}{align}   = 'right';
 
 499       $row->{soldtotal}{data}     = 0;
 
 500       $soldtotal                  = 0 if ($form->{sold});
 
 503     my $edit_link               = build_std_url('script=controller.pl', 'action=Part/edit', 'part.id=' . E($ref->{id}));
 
 504     $row->{partnumber}->{link}  = $edit_link;
 
 505     $row->{description}->{link} = $edit_link;
 
 507     foreach (qw(sellprice listprice lastcost)) {
 
 508       $row->{$_}{data}            = $form->format_amount(\%myconfig, $ref->{$_}, 2);
 
 509       $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
 
 511     foreach ( @pricegroup_columns ) {
 
 512       $row->{$_}{data}            = $form->format_amount(\%myconfig, $ref->{"$_"}, 2);
 
 516     map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
 
 518     $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
 
 520     # 'yes' and 'no' for boolean value shop
 
 521     if ($form->{l_shop}) {
 
 522       $row->{shop}{data} = $row->{shop}{data}? $::locale->text('yes') : $::locale->text('no');
 
 525     if (!$ref->{assemblyitem}) {
 
 526       foreach my $col (@subtotal_columns) {
 
 527         $totals{$col}    += $soldtotal * $ref->{$col};
 
 528         $subtotals{$col} += $soldtotal * $ref->{$col};
 
 531       $subtotals{soldtotal} += $soldtotal;
 
 535     if ($ref->{module} eq 'oe') {
 
 536       # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
 
 538       # | ist bestellt  | Von Kunden bestellt |  -> edit_oe_ord_link
 
 539       # | Anfrage       | Angebot             |  -> edit_oe_quo_link
 
 541       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');
 
 542       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');
 
 544       $row->{ordnumber}{link} = $edit_oe_ord_link;
 
 545       $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
 
 548       $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback') if ($ref->{invnumber});
 
 551     # set properties of images
 
 552     if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
 
 553       $row->{image}{data}     = '';
 
 554       $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
 
 556     map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
 
 558     $row->{notes}{data} = SL::HTML::Util->strip($ref->{notes});
 
 559     $row->{type_and_classific}{data} = $::request->presenter->type_abbreviation($ref->{part_type}).
 
 560                                        $::request->presenter->classification_abbreviation($ref->{classification_id});
 
 562     $report->add_data($row);
 
 564     my $next_ref = $form->{parts}[$idx + 1];
 
 566     # insert subtotal rows
 
 567     if (($form->{l_subtotal} eq 'Y') &&
 
 569          (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
 
 570       my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
 
 572       if ( !$form->{l_assembly} || !$form->{bom}) {
 
 573         $row->{soldtotal}->{data} = $form->format_amount(\%myconfig, $subtotals{soldtotal});
 
 576       map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
 
 577       map { $subtotals{$_} = 0 } ('soldtotal', @subtotal_columns);
 
 579       $report->add_data($row);
 
 581       $same_item = $next_ref->{ $form->{sort} };
 
 587   if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
 
 588     my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
 
 590     map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
 
 592     $report->add_separator();
 
 593     $report->add_data($row);
 
 596   setup_ic_generate_report_action_bar();
 
 597   $report->generate_with_headers();
 
 599   $lxdebug->leave_sub();
 
 600 }    #end generate_report
 
 602 sub setup_ic_search_action_bar {
 
 605   for my $bar ($::request->layout->get('actionbar')) {
 
 609         submit    => [ '#form', { action => 'generate_report' } ],
 
 610         accesskey => 'enter',
 
 615         submit => [ '#form', { action => 'top100' } ],
 
 621 sub setup_ic_generate_report_action_bar {
 
 624   for my $bar ($::request->layout->get('actionbar')) {
 
 632           submit    => [ '#new_form', { action => 'Part/add_part' } ],
 
 633           accesskey => 'enter',
 
 637           submit    => [ '#new_form', { action => 'Part/add_service' } ],
 
 641           submit    => [ '#new_form', { action => 'Part/add_assembly' } ],
 
 644           t8('Add Assortment'),
 
 645           submit    => [ '#new_form', { action => 'Part/add_assortment' } ],
 
 647       ], # end of combobox "Add part"