Artikelliste: Artikel-Typ-Filter-Checkboxen richtig mitschleifen
[kivitendo-erp.git] / bin / mozilla / ic.pl
1 #=====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #=====================================================================
8 # SQL-Ledger, Accounting
9 # Copyright (c) 2001
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 #
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
20 #
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
28 # MA 02110-1335, USA.
29 #======================================================================
30 #
31 # Inventory Control module
32 #
33 #======================================================================
34
35 use POSIX qw(strftime);
36 use List::Util qw(first max);
37 use List::MoreUtils qw(any);
38
39 use SL::AM;
40 use SL::CVar;
41 use SL::IC;
42 use SL::Helper::Flash qw(flash);
43 use SL::HTML::Util;
44 use SL::ReportGenerator;
45
46 #use SL::PE;
47
48 use strict;
49 #use warnings;
50
51 # global imports
52 our ($form, $locale, %myconfig, $lxdebug, $auth);
53
54 require "bin/mozilla/io.pl";
55 require "bin/mozilla/common.pl";
56 require "bin/mozilla/reportgenerator.pl";
57
58 1;
59
60 # Parserhappy(R):
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');
75
76 # end of main
77
78 sub search {
79   $lxdebug->enter_sub();
80
81   $auth->assert('part_service_assembly_details');
82
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
86
87   $form->{title} = (ucfirst $form->{searchitems}) . "s";
88   $form->{title} =~ s/ys$/ies/;
89   $form->{title} = $locale->text($form->{title});
90
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');
96
97   setup_ic_search_action_bar();
98   $form->header;
99
100   $form->get_lists('partsgroup'    => 'ALL_PARTSGROUPS');
101   print $form->parse_html_template('ic/search');
102
103   $lxdebug->leave_sub();
104 }    #end search()
105
106 sub top100 {
107   $::lxdebug->enter_sub();
108
109   $::auth->assert('part_service_assembly_edit');
110
111   $::form->{l_soldtotal} = "Y";
112   $::form->{sort}        = "soldtotal";
113   $::form->{lastsort}    = "soldtotal";
114
115   $::form->{l_qty}       = undef;
116   $::form->{l_linetotal} = undef;
117   $::form->{l_number}    = "Y";
118   $::form->{number}      = "position";
119
120   unless (   $::form->{bought}
121           || $::form->{sold}
122           || $::form->{rfq}
123           || $::form->{quoted}) {
124     $::form->{bought} = $::form->{sold} = 1;
125   }
126
127   generate_report();
128
129   $lxdebug->leave_sub();
130 }
131
132 #
133 # Report for Wares.
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
136 #
137 # flags coming from the form:
138 # hardcoded:
139 #  searchitems=part revers=0 lastsort=''
140 #
141 # filter:
142 # partnumber ean description partsgroup classification serialnumber make model drawing microfiche
143 # transdatefrom transdateto
144 #
145 # radio:
146 #  itemstatus = active | onhand | short | obsolete | orphaned
147 #  action     = continue | top100
148 #
149 # checkboxes:
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
154 #
155 # hiddens:
156 #  nextsub revers lastsort sort ndxs_counter
157 #
158 sub generate_report {
159   $lxdebug->enter_sub();
160
161   $auth->assert('part_service_assembly_details');
162
163   my ($revers, $lastsort, $description);
164
165   my $cvar_configs = CVar->get_configs('module' => 'IC');
166
167   $form->{title} = $locale->text('Articles');
168
169   my %column_defs = (
170     'bin'                => { 'text' => $locale->text('Bin'), },
171     'deliverydate'       => { 'text' => $locale->text('deliverydate'), },
172     'description'        => { 'text' => $locale->text('Part Description'), },
173     'notes'              => { 'text' => $locale->text('Notes'), },
174     'drawing'            => { 'text' => $locale->text('Drawing'), },
175     'ean'                => { 'text' => $locale->text('EAN'), },
176     'image'              => { 'text' => $locale->text('Image'), },
177     'insertdate'         => { 'text' => $locale->text('Insert Date'), },
178     'invnumber'          => { 'text' => $locale->text('Invoice Number'), },
179     'lastcost'           => { 'text' => $locale->text('Last Cost'), },
180     'linetotallastcost'  => { 'text' => $locale->text('Extended'), },
181     'linetotallistprice' => { 'text' => $locale->text('Extended'), },
182     'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
183     'listprice'          => { 'text' => $locale->text('List Price'), },
184     'microfiche'         => { 'text' => $locale->text('Microfiche'), },
185     'name'               => { 'text' => $locale->text('Name'), },
186     'onhand'             => { 'text' => $locale->text('Stocked Qty'), },
187     'ordnumber'          => { 'text' => $locale->text('Order Number'), },
188     'partnumber'         => { 'text' => $locale->text('Part Number'), },
189     'partsgroup'         => { 'text' => $locale->text('Partsgroup'), },
190     'priceupdate'        => { 'text' => $locale->text('Updated'), },
191     'quonumber'          => { 'text' => $locale->text('Quotation'), },
192     'rop'                => { 'text' => $locale->text('ROP'), },
193     'sellprice'          => { 'text' => $locale->text('Sell Price'), },
194     'serialnumber'       => { 'text' => $locale->text('Serial Number'), },
195     'soldtotal'          => { 'text' => $locale->text('Qty in Selected Records'), },
196     'name'               => { 'text' => $locale->text('Name in Selected Records'), },
197     'transdate'          => { 'text' => $locale->text('Transdate Record'), },
198     'unit'               => { 'text' => $locale->text('Unit'), },
199     'weight'             => { 'text' => $locale->text('Weight'), },
200     'shop'               => { 'text' => $locale->text('Shop article'), },
201     'type_and_classific' => { 'text' => $locale->text('Type'), },
202     'projectnumber'      => { 'text' => $locale->text('Project Number'), },
203     'projectdescription' => { 'text' => $locale->text('Project Description'), },
204   );
205
206   $revers     = $form->{revers};
207   $lastsort   = $form->{lastsort};
208
209   # sorting and direction of sorting
210   # ToDO: change this to the simpler field+direction method
211   if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
212     $form->{revers}   = 0;
213     $form->{lastsort} = "partnumber";
214     $form->{sort}     = "partnumber";
215   } else {
216     if ($form->{lastsort} eq $form->{sort}) {
217       $form->{revers} = 1 - $form->{revers};
218     } else {
219       $form->{revers} = 0;
220       $form->{lastsort} = $form->{sort};
221     }    #fi
222   }    #fi
223
224   # special case if we have a serialnumber limit search
225   # serialnumbers are only given in invoices and orders,
226   # so they can only pop up in bought, sold, rfq, and quoted stuff
227   $form->{no_sn_joins} = 'Y' if (   !$form->{bought} && !$form->{sold}
228                                  && !$form->{rfq}    && !$form->{quoted}
229                                  && ($form->{l_serialnumber} || $form->{serialnumber}));
230
231   # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
232   # if any of these are ticked the behavior changes slightly for lastcost
233   # since all those are aggregation checks for the legder tables this is an internal switch
234   # refered to as ledgerchecks
235   $form->{ledgerchecks} = 'Y' if (   $form->{bought} || $form->{sold} || $form->{onorder}
236                                   || $form->{ordered} || $form->{rfq} || $form->{quoted});
237
238   # if something should be activated if something else is active, enter it here
239   my %dependencies = (
240     onhand       => [ qw(l_onhand) ],
241     short        => [ qw(l_onhand) ],
242     onorder      => [ qw(l_ordnumber) ],
243     ordered      => [ qw(l_ordnumber) ],
244     rfq          => [ qw(l_quonumber) ],
245     quoted       => [ qw(l_quonumber) ],
246     bought       => [ qw(l_invnumber) ],
247     sold         => [ qw(l_invnumber) ],
248     ledgerchecks => [ qw(l_name) ],
249     serialnumber => [ qw(l_serialnumber) ],
250     no_sn_joins  => [ qw(bought sold) ],
251   );
252
253   # get name of partsgroup if id is given
254   my $pg_name;
255   if ($form->{partsgroup_id}) {
256     my $pg = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load;
257     $pg_name = $pg->{'partsgroup'};
258   }
259
260   # these strings get displayed at the top of the results to indicate the user which switches were used
261   my %optiontexts = (
262     active        => $locale->text('Active'),
263     obsolete      => $locale->text('Obsolete'),
264     orphaned      => $locale->text('Orphaned'),
265     onhand        => $locale->text('On Hand'),
266     short         => $locale->text('Short'),
267     onorder       => $locale->text('On Order'),
268     ordered       => $locale->text('Ordered'),
269     rfq           => $locale->text('RFQ'),
270     quoted        => $locale->text('Quoted'),
271     bought        => $locale->text('Bought'),
272     sold          => $locale->text('Sold'),
273     transdatefrom => $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
274     transdateto   => $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
275     partnumber    => $locale->text('Part Number')      . ": '$form->{partnumber}'",
276     partsgroup    => $locale->text('Partsgroup')       . ": '$form->{partsgroup}'",
277     partsgroup_id => $locale->text('Partsgroup')       . ": '$pg_name'",
278     serialnumber  => $locale->text('Serial Number')    . ": '$form->{serialnumber}'",
279     description   => $locale->text('Part Description') . ": '$form->{description}'",
280     make          => $locale->text('Make')             . ": '$form->{make}'",
281     model         => $locale->text('Model')            . ": '$form->{model}'",
282     drawing       => $locale->text('Drawing')          . ": '$form->{drawing}'",
283     microfiche    => $locale->text('Microfiche')       . ": '$form->{microfiche}'",
284     l_soldtotal   => $locale->text('Qty in Selected Records'),
285     ean           => $locale->text('EAN')              . ": '$form->{ean}'",
286     insertdatefrom => $locale->text('Insert Date') . ": " . $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1),
287     insertdateto   => $locale->text('Insert Date') . ": " . $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1),
288   );
289
290   my @itemstatus_keys = qw(active obsolete orphaned onhand short);
291   my @callback_keys   = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
292                            drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto insertdatefrom insertdateto ean shop all);
293
294   # calculate dependencies
295   for (@itemstatus_keys, @callback_keys) {
296     next if ($form->{itemstatus} ne $_ && !$form->{$_});
297     map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
298   }
299
300   # generate callback and optionstrings
301   my @options;
302   for my  $key (@itemstatus_keys, @callback_keys) {
303     next if ($form->{itemstatus} ne $key && !$form->{$key});
304     push @options, $optiontexts{$key};
305   }
306
307   # special case for lastcost
308   if ($form->{ledgerchecks}){
309     # ledgerchecks don't know about sellprice or lastcost. they just return a
310     # price. so rename sellprice to price, and drop lastcost.
311     $column_defs{sellprice}{text} = $locale->text('Price');
312     $form->{l_lastcost} = ""
313   }
314
315   if ($form->{description}) {
316     $description = $form->{description};
317     $description =~ s/\n/<br>/g;
318   }
319
320   if ($form->{l_linetotal}) {
321     $form->{l_qty} = "Y";
322     $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
323     $form->{l_linetotallastcost}  = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if  $form->{l_lastcost};
324     $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
325   }
326   $form->{"l_type_and_classific"} = "Y";
327
328   if ($form->{l_service} && !$form->{l_assembly} && !$form->{l_part}) {
329
330     # remove bin, weight and rop from list
331     map { $form->{"l_$_"} = "" } qw(bin weight rop);
332
333     $form->{l_onhand} = "";
334
335     # qty is irrelevant unless bought or sold
336     if (   $form->{bought}
337         || $form->{sold}
338         || $form->{onorder}
339         || $form->{ordered}
340         || $form->{rfq}
341         || $form->{quoted}) {
342 #      $form->{l_onhand} = "Y";
343     } else {
344       $form->{l_linetotalsellprice} = "";
345       $form->{l_linetotallastcost}  = "";
346     }
347   }
348
349   # soldtotal doesn't make sense with more than one bsooqr option.
350   # so reset it to sold (the most common option), and issue a warning
351   # ...
352   # also it doesn't make sense without bsooqr. disable and issue a warning too
353   my @bsooqr = qw(sold bought onorder ordered rfq quoted);
354   my $bsooqr_mode = grep { $form->{$_} } @bsooqr;
355   if ($form->{l_subtotal} && 1 < $bsooqr_mode) {
356     my $enabled       = first { $form->{$_} } @bsooqr;
357     $form->{$_}       = ''   for @bsooqr;
358     $form->{$enabled} = 'Y';
359
360     push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
361   }
362   if ($form->{l_soldtotal} && !$bsooqr_mode) {
363     delete $form->{l_soldtotal};
364
365     flash('warning', $::locale->text('Soldtotal does not make sense without any bsooqr options'));
366   }
367   if ($form->{l_name} && !$bsooqr_mode) {
368     delete $form->{l_name};
369
370     flash('warning', $::locale->text('Name does not make sense without any bsooqr options'));
371   }
372   IC->all_parts(\%myconfig, \%$form);
373
374   my @columns = qw(
375     partnumber type_and_classific description notes partsgroup bin onhand rop soldtotal unit listprice
376     linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
377     priceupdate weight image drawing microfiche invnumber ordnumber quonumber
378     transdate name serialnumber deliverydate ean projectnumber projectdescription
379     insertdate shop
380   );
381
382   my $pricegroups = SL::DB::Manager::Pricegroup->get_all_sorted;
383   my @pricegroup_columns;
384   my %column_defs_pricegroups;
385   if ($form->{l_pricegroups}) {
386     @pricegroup_columns      = map { "pricegroup_" . $_->id } @{ $pricegroups };
387     %column_defs_pricegroups = map {
388       "pricegroup_" . $_->id => {
389         text    => $::locale->text('Pricegroup') . ' ' . $_->pricegroup,
390         visible => 1,
391       },
392     }  @{ $pricegroups };
393   }
394   push @columns, @pricegroup_columns;
395
396   my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
397   my @searchable_custom_variables  = grep { $_->{searchable} }  @{ $cvar_configs };
398   my %column_defs_cvars            = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
399
400   push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
401
402   %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
403   map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
404   map { $column_defs{$_}->{align}   = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal shop), @pricegroup_columns;
405
406   my @hidden_variables = (
407     qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups insertdatefrom insertdateto),
408     qw(l_type_and_classific classification_id l_part l_service l_assembly l_assortment),
409     @itemstatus_keys,
410     @callback_keys,
411     map({ "cvar_$_->{name}" } @searchable_custom_variables),
412     map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
413     map({ "l_$_" } @columns),
414   );
415
416   my $callback         = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
417
418   my @sort_full        = qw(partnumber description onhand soldtotal deliverydate insertdate shop);
419   my @sort_no_revers   = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
420
421   foreach my $col (@sort_full) {
422     $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
423   }
424   map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
425
426   # add order to callback
427   $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
428
429   my $report = SL::ReportGenerator->new(\%myconfig, $form);
430
431   my %attachment_basenames = (
432     'part'     => $locale->text('part_list'),
433     'service'  => $locale->text('service_list'),
434     'assembly' => $locale->text('assembly_list'),
435     'article'  => $locale->text('article_list'),
436   );
437
438   $report->set_options('raw_top_info_text'     => $form->parse_html_template('ic/generate_report_top', { options => \@options }),
439                        'raw_bottom_info_text'  => $form->parse_html_template('ic/generate_report_bottom' ,
440                                                   { PART_CLASSIFICATIONS => SL::DB::Manager::PartClassification->get_all_sorted }),
441                        'output_format'         => 'HTML',
442                        'title'                 => $form->{title},
443                        'attachment_basename'   => 'article_list' . strftime('_%Y%m%d', localtime time),
444   );
445   $report->set_options_from_form();
446   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
447
448   $report->set_columns(%column_defs);
449   $report->set_column_order(@columns);
450
451   $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
452
453   $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
454
455   CVar->add_custom_variables_to_report('module'         => 'IC',
456                                        'trans_id_field' => 'id',
457                                        'configs'        => $cvar_configs,
458                                        'column_defs'    => \%column_defs,
459                                        'data'           => $form->{parts});
460
461   CVar->add_custom_variables_to_report('module'         => 'IC',
462                                        'sub_module'     => sub { $_[0]->{ioi} },
463                                        'trans_id_field' => 'ioi_id',
464                                        'configs'        => $cvar_configs,
465                                        'column_defs'    => \%column_defs,
466                                        'data'           => $form->{parts});
467
468   my @subtotal_columns = qw(sellprice listprice lastcost);
469   my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
470   my %totals    = map { $_ => 0 } @subtotal_columns;
471   my $idx       = 0;
472   my $same_item = @{ $form->{parts} } ? $form->{parts}[0]{ $form->{sort} } : undef;
473
474   my $defaults  = AM->get_defaults();
475
476   # postprocess parts
477   foreach my $ref (@{ $form->{parts} }) {
478
479     # fresh row, for inserting later
480     my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
481
482     $ref->{exchangerate} ||= 1;
483     $ref->{price_factor} ||= 1;
484     $ref->{sellprice}     *= $ref->{exchangerate} / $ref->{price_factor};
485     $ref->{listprice}     *= $ref->{exchangerate} / $ref->{price_factor};
486     $ref->{lastcost}      *= $ref->{exchangerate} / $ref->{price_factor};
487
488     # use this for assemblies
489     my $soldtotal = $bsooqr_mode ? $ref->{soldtotal} : $ref->{onhand};
490
491     if ($ref->{assemblyitem}) {
492       $row->{partnumber}{align}   = 'right';
493       $row->{soldtotal}{data}     = 0;
494       $soldtotal                  = 0 if ($form->{sold});
495     }
496
497     my $edit_link               = build_std_url('script=controller.pl', 'action=Part/edit', 'part.id=' . E($ref->{id}), 'callback');
498     $row->{partnumber}->{link}  = $edit_link;
499     $row->{description}->{link} = $edit_link;
500
501     foreach (qw(sellprice listprice lastcost)) {
502       $row->{$_}{data}            = $form->format_amount(\%myconfig, $ref->{$_}, 2);
503       $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
504     }
505     foreach ( @pricegroup_columns ) {
506       $row->{$_}{data}            = $form->format_amount(\%myconfig, $ref->{"$_"}, 2);
507     };
508
509
510     map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
511
512     $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
513
514     # 'yes' and 'no' for boolean value shop
515     if ($form->{l_shop}) {
516       $row->{shop}{data} = $row->{shop}{data}? $::locale->text('yes') : $::locale->text('no');
517     }
518
519     if (!$ref->{assemblyitem}) {
520       foreach my $col (@subtotal_columns) {
521         $totals{$col}    += $soldtotal * $ref->{$col};
522         $subtotals{$col} += $soldtotal * $ref->{$col};
523       }
524
525       $subtotals{soldtotal} += $soldtotal;
526     }
527
528     # set module stuff
529     if ($ref->{module} eq 'oe') {
530       # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
531       #
532       # | ist bestellt  | Von Kunden bestellt |  -> edit_oe_ord_link
533       # | Anfrage       | Angebot             |  -> edit_oe_quo_link
534
535       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');
536       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');
537
538       $row->{ordnumber}{link} = $edit_oe_ord_link;
539       $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
540
541     } else {
542       $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback') if ($ref->{invnumber});
543     }
544
545     # set properties of images
546     if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
547       $row->{image}{data}     = '';
548       $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
549     }
550     map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
551
552     $row->{notes}{data} = SL::HTML::Util->strip($ref->{notes});
553     $row->{type_and_classific}{data} = $::request->presenter->type_abbreviation($ref->{part_type}).
554                                        $::request->presenter->classification_abbreviation($ref->{classification_id});
555
556     $report->add_data($row);
557
558     my $next_ref = $form->{parts}[$idx + 1];
559
560     # insert subtotal rows
561     if (($form->{l_subtotal} eq 'Y') &&
562         (!$next_ref ||
563          (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
564       my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
565
566       if ( !$form->{l_assembly} || !$form->{bom}) {
567         $row->{soldtotal}->{data} = $form->format_amount(\%myconfig, $subtotals{soldtotal});
568       }
569
570       map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
571       map { $subtotals{$_} = 0 } ('soldtotal', @subtotal_columns);
572
573       $report->add_data($row);
574
575       $same_item = $next_ref->{ $form->{sort} };
576     }
577
578     $idx++;
579   }
580
581   if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
582     my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
583
584     map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
585
586     $report->add_separator();
587     $report->add_data($row);
588   }
589
590   setup_ic_generate_report_action_bar();
591   $report->generate_with_headers();
592
593   $lxdebug->leave_sub();
594 }    #end generate_report
595
596 sub setup_ic_search_action_bar {
597   my %params = @_;
598
599   for my $bar ($::request->layout->get('actionbar')) {
600     $bar->add(
601       action => [
602         t8('Search'),
603         submit    => [ '#form', { action => 'generate_report' } ],
604         accesskey => 'enter',
605       ],
606
607       action => [
608         t8('TOP100'),
609         submit => [ '#form', { action => 'top100' } ],
610       ],
611     );
612   }
613 }
614
615 sub setup_ic_generate_report_action_bar {
616   my %params = @_;
617
618   for my $bar ($::request->layout->get('actionbar')) {
619     $bar->add(
620       combobox => [
621         action => [
622           t8('Add'),
623         ],
624         action => [
625           t8('Add Part'),
626           submit    => [ '#new_form', { action => 'Part/add_part' } ],
627           accesskey => 'enter',
628         ],
629         action => [
630           t8('Add Service'),
631           submit    => [ '#new_form', { action => 'Part/add_service' } ],
632         ],
633         action => [
634           t8('Add Assembly'),
635           submit    => [ '#new_form', { action => 'Part/add_assembly' } ],
636         ],
637         action => [
638           t8('Add Assortment'),
639           submit    => [ '#new_form', { action => 'Part/add_assortment' } ],
640         ],
641       ], # end of combobox "Add part"
642     );
643   }
644 }