Preisupdate in eigenen controller verlagert
[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   $form->header;
98
99   $form->get_lists('partsgroup'    => 'ALL_PARTSGROUPS');
100   print $form->parse_html_template('ic/search');
101
102   $lxdebug->leave_sub();
103 }    #end search()
104
105 sub top100 {
106   $::lxdebug->enter_sub();
107
108   $::auth->assert('part_service_assembly_edit');
109
110   $::form->{l_soldtotal} = "Y";
111   $::form->{sort}        = "soldtotal";
112   $::form->{lastsort}    = "soldtotal";
113
114   $::form->{l_qty}       = undef;
115   $::form->{l_linetotal} = undef;
116   $::form->{l_number}    = "Y";
117   $::form->{number}      = "position";
118
119   unless (   $::form->{bought}
120           || $::form->{sold}
121           || $::form->{rfq}
122           || $::form->{quoted}) {
123     $::form->{bought} = $::form->{sold} = 1;
124   }
125
126   generate_report();
127
128   $lxdebug->leave_sub();
129 }
130
131 #
132 # Report for Wares.
133 # Warning, deep magic ahead.
134 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
135 #
136 # flags coming from the form:
137 # hardcoded:
138 #  searchitems=part revers=0 lastsort=''
139 #
140 # filter:
141 # partnumber ean description partsgroup classification serialnumber make model drawing microfiche
142 # transdatefrom transdateto
143 #
144 # radio:
145 #  itemstatus = active | onhand | short | obsolete | orphaned
146 #  action     = continue | top100
147 #
148 # checkboxes:
149 #  bought sold onorder ordered rfq quoted
150 #  l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
151 #  l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
152 #  l_partsgroup l_subtotal l_soldtotal l_deliverydate l_pricegroups
153 #
154 # hiddens:
155 #  nextsub revers lastsort sort ndxs_counter
156 #
157 sub generate_report {
158   $lxdebug->enter_sub();
159
160   $auth->assert('part_service_assembly_details');
161
162   my ($revers, $lastsort, $description);
163
164   my $cvar_configs = CVar->get_configs('module' => 'IC');
165
166   $form->{title} = $locale->text('Articles');
167
168   my %column_defs = (
169     'bin'                => { 'text' => $locale->text('Bin'), },
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'), },
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   );
204
205   $revers     = $form->{revers};
206   $lastsort   = $form->{lastsort};
207
208   # sorting and direction of sorting
209   # ToDO: change this to the simpler field+direction method
210   if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
211     $form->{revers}   = 0;
212     $form->{lastsort} = "partnumber";
213     $form->{sort}     = "partnumber";
214   } else {
215     if ($form->{lastsort} eq $form->{sort}) {
216       $form->{revers} = 1 - $form->{revers};
217     } else {
218       $form->{revers} = 0;
219       $form->{lastsort} = $form->{sort};
220     }    #fi
221   }    #fi
222
223   # special case if we have a serialnumber limit search
224   # serialnumbers are only given in invoices and orders,
225   # so they can only pop up in bought, sold, rfq, and quoted stuff
226   $form->{no_sn_joins} = 'Y' if (   !$form->{bought} && !$form->{sold}
227                                  && !$form->{rfq}    && !$form->{quoted}
228                                  && ($form->{l_serialnumber} || $form->{serialnumber}));
229
230   # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
231   # if any of these are ticked the behavior changes slightly for lastcost
232   # since all those are aggregation checks for the legder tables this is an internal switch
233   # refered to as ledgerchecks
234   $form->{ledgerchecks} = 'Y' if (   $form->{bought} || $form->{sold} || $form->{onorder}
235                                   || $form->{ordered} || $form->{rfq} || $form->{quoted});
236
237   # if something should be activated if something else is active, enter it here
238   my %dependencies = (
239     onhand       => [ qw(l_onhand) ],
240     short        => [ qw(l_onhand) ],
241     onorder      => [ qw(l_ordnumber) ],
242     ordered      => [ qw(l_ordnumber) ],
243     rfq          => [ qw(l_quonumber) ],
244     quoted       => [ qw(l_quonumber) ],
245     bought       => [ qw(l_invnumber) ],
246     sold         => [ qw(l_invnumber) ],
247     ledgerchecks => [ qw(l_name) ],
248     serialnumber => [ qw(l_serialnumber) ],
249     no_sn_joins  => [ qw(bought sold) ],
250   );
251
252   # get name of partsgroup if id is given
253   my $pg_name;
254   if ($form->{partsgroup_id}) {
255     my $pg = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load;
256     $pg_name = $pg->{'partsgroup'};
257   }
258
259   # these strings get displayed at the top of the results to indicate the user which switches were used
260   my %optiontexts = (
261     active        => $locale->text('Active'),
262     obsolete      => $locale->text('Obsolete'),
263     orphaned      => $locale->text('Orphaned'),
264     onhand        => $locale->text('On Hand'),
265     short         => $locale->text('Short'),
266     onorder       => $locale->text('On Order'),
267     ordered       => $locale->text('Ordered'),
268     rfq           => $locale->text('RFQ'),
269     quoted        => $locale->text('Quoted'),
270     bought        => $locale->text('Bought'),
271     sold          => $locale->text('Sold'),
272     transdatefrom => $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
273     transdateto   => $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
274     partnumber    => $locale->text('Part Number')      . ": '$form->{partnumber}'",
275     partsgroup    => $locale->text('Partsgroup')       . ": '$form->{partsgroup}'",
276     partsgroup_id => $locale->text('Partsgroup')       . ": '$pg_name'",
277     serialnumber  => $locale->text('Serial Number')    . ": '$form->{serialnumber}'",
278     description   => $locale->text('Part Description') . ": '$form->{description}'",
279     make          => $locale->text('Make')             . ": '$form->{make}'",
280     model         => $locale->text('Model')            . ": '$form->{model}'",
281     drawing       => $locale->text('Drawing')          . ": '$form->{drawing}'",
282     microfiche    => $locale->text('Microfiche')       . ": '$form->{microfiche}'",
283     l_soldtotal   => $locale->text('Qty in Selected Records'),
284     ean           => $locale->text('EAN')              . ": '$form->{ean}'",
285     insertdatefrom => $locale->text('Insert Date') . ": " . $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1),
286     insertdateto   => $locale->text('Insert Date') . ": " . $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1),
287   );
288
289   my @itemstatus_keys = qw(active obsolete orphaned onhand short);
290   my @callback_keys   = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
291                            drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto insertdatefrom insertdateto ean shop all);
292
293   # calculate dependencies
294   for (@itemstatus_keys, @callback_keys) {
295     next if ($form->{itemstatus} ne $_ && !$form->{$_});
296     map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
297   }
298
299   # generate callback and optionstrings
300   my @options;
301   for my  $key (@itemstatus_keys, @callback_keys) {
302     next if ($form->{itemstatus} ne $key && !$form->{$key});
303     push @options, $optiontexts{$key};
304   }
305
306   # special case for lastcost
307   if ($form->{ledgerchecks}){
308     # ledgerchecks don't know about sellprice or lastcost. they just return a
309     # price. so rename sellprice to price, and drop lastcost.
310     $column_defs{sellprice}{text} = $locale->text('Price');
311     $form->{l_lastcost} = ""
312   }
313
314   if ($form->{description}) {
315     $description = $form->{description};
316     $description =~ s/\n/<br>/g;
317   }
318
319   if ($form->{l_linetotal}) {
320     $form->{l_qty} = "Y";
321     $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
322     $form->{l_linetotallastcost}  = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if  $form->{l_lastcost};
323     $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
324   }
325   $form->{"l_type_and_classific"} = "Y";
326
327   if ($form->{l_service} && !$form->{l_assembly} && !$form->{l_part}) {
328
329     # remove bin, weight and rop from list
330     map { $form->{"l_$_"} = "" } qw(bin weight rop);
331
332     $form->{l_onhand} = "";
333
334     # qty is irrelevant unless bought or sold
335     if (   $form->{bought}
336         || $form->{sold}
337         || $form->{onorder}
338         || $form->{ordered}
339         || $form->{rfq}
340         || $form->{quoted}) {
341 #      $form->{l_onhand} = "Y";
342     } else {
343       $form->{l_linetotalsellprice} = "";
344       $form->{l_linetotallastcost}  = "";
345     }
346   }
347
348   # soldtotal doesn't make sense with more than one bsooqr option.
349   # so reset it to sold (the most common option), and issue a warning
350   # ...
351   # also it doesn't make sense without bsooqr. disable and issue a warning too
352   my @bsooqr = qw(sold bought onorder ordered rfq quoted);
353   my $bsooqr_mode = grep { $form->{$_} } @bsooqr;
354   if ($form->{l_subtotal} && 1 < $bsooqr_mode) {
355     my $enabled       = first { $form->{$_} } @bsooqr;
356     $form->{$_}       = ''   for @bsooqr;
357     $form->{$enabled} = 'Y';
358
359     push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
360   }
361   if ($form->{l_soldtotal} && !$bsooqr_mode) {
362     delete $form->{l_soldtotal};
363
364     flash('warning', $::locale->text('Soldtotal does not make sense without any bsooqr options'));
365   }
366   if ($form->{l_name} && !$bsooqr_mode) {
367     delete $form->{l_name};
368
369     flash('warning', $::locale->text('Name does not make sense without any bsooqr options'));
370   }
371   IC->all_parts(\%myconfig, \%$form);
372
373   my @columns = qw(
374     partnumber type_and_classific description notes partsgroup bin onhand rop soldtotal unit listprice
375     linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
376     priceupdate weight image drawing microfiche invnumber ordnumber quonumber
377     transdate name serialnumber deliverydate ean projectnumber projectdescription
378     insertdate shop
379   );
380
381   my $pricegroups = SL::DB::Manager::Pricegroup->get_all_sorted;
382   my @pricegroup_columns;
383   my %column_defs_pricegroups;
384   if ($form->{l_pricegroups}) {
385     @pricegroup_columns      = map { "pricegroup_" . $_->id } @{ $pricegroups };
386     %column_defs_pricegroups = map {
387       "pricegroup_" . $_->id => {
388         text    => $::locale->text('Pricegroup') . ' ' . $_->pricegroup,
389         visible => 1,
390       },
391     }  @{ $pricegroups };
392   }
393   push @columns, @pricegroup_columns;
394
395   my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
396   my @searchable_custom_variables  = grep { $_->{searchable} }  @{ $cvar_configs };
397   my %column_defs_cvars            = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
398
399   push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
400
401   %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
402   map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
403   map { $column_defs{$_}->{align}   = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal shop), @pricegroup_columns;
404
405   my @hidden_variables = (
406     qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups insertdatefrom insertdateto),
407     qw(l_type_and_classific classification_id),
408     @itemstatus_keys,
409     @callback_keys,
410     map({ "cvar_$_->{name}" } @searchable_custom_variables),
411     map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
412     map({ "l_$_" } @columns),
413   );
414
415   my $callback         = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
416
417   my @sort_full        = qw(partnumber description onhand soldtotal deliverydate insertdate shop);
418   my @sort_no_revers   = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
419
420   foreach my $col (@sort_full) {
421     $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
422   }
423   map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
424
425   # add order to callback
426   $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
427
428   my $report = SL::ReportGenerator->new(\%myconfig, $form);
429
430   my %attachment_basenames = (
431     'part'     => $locale->text('part_list'),
432     'service'  => $locale->text('service_list'),
433     'assembly' => $locale->text('assembly_list'),
434     'article'  => $locale->text('article_list'),
435   );
436
437   $report->set_options('raw_top_info_text'     => $form->parse_html_template('ic/generate_report_top', { options => \@options }),
438                        'raw_bottom_info_text'  => $form->parse_html_template('ic/generate_report_bottom' ,
439                                                   { PART_CLASSIFICATIONS => SL::DB::Manager::PartClassification->get_all_sorted }),
440                        'output_format'         => 'HTML',
441                        'title'                 => $form->{title},
442                        'attachment_basename'   => 'article_list' . strftime('_%Y%m%d', localtime time),
443   );
444   $report->set_options_from_form();
445   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
446
447   $report->set_columns(%column_defs);
448   $report->set_column_order(@columns);
449
450   $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
451
452   $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
453
454   CVar->add_custom_variables_to_report('module'         => 'IC',
455                                        'trans_id_field' => 'id',
456                                        'configs'        => $cvar_configs,
457                                        'column_defs'    => \%column_defs,
458                                        'data'           => $form->{parts});
459
460   CVar->add_custom_variables_to_report('module'         => 'IC',
461                                        'sub_module'     => sub { $_[0]->{ioi} },
462                                        'trans_id_field' => 'ioi_id',
463                                        'configs'        => $cvar_configs,
464                                        'column_defs'    => \%column_defs,
465                                        'data'           => $form->{parts});
466
467   my @subtotal_columns = qw(sellprice listprice lastcost);
468   my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
469   my %totals    = map { $_ => 0 } @subtotal_columns;
470   my $idx       = 0;
471   my $same_item = @{ $form->{parts} } ? $form->{parts}[0]{ $form->{sort} } : undef;
472
473   my $defaults  = AM->get_defaults();
474
475   # postprocess parts
476   foreach my $ref (@{ $form->{parts} }) {
477
478     # fresh row, for inserting later
479     my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
480
481     $ref->{exchangerate} ||= 1;
482     $ref->{price_factor} ||= 1;
483     $ref->{sellprice}     *= $ref->{exchangerate} / $ref->{price_factor};
484     $ref->{listprice}     *= $ref->{exchangerate} / $ref->{price_factor};
485     $ref->{lastcost}      *= $ref->{exchangerate} / $ref->{price_factor};
486
487     # use this for assemblies
488     my $soldtotal = $bsooqr_mode ? $ref->{soldtotal} : $ref->{onhand};
489
490     if ($ref->{assemblyitem}) {
491       $row->{partnumber}{align}   = 'right';
492       $row->{soldtotal}{data}     = 0;
493       $soldtotal                  = 0 if ($form->{sold});
494     }
495
496     my $edit_link               = build_std_url('script=controller.pl', 'action=Part/edit', 'part.id=' . E($ref->{id}), 'callback');
497     $row->{partnumber}->{link}  = $edit_link;
498     $row->{description}->{link} = $edit_link;
499
500     foreach (qw(sellprice listprice lastcost)) {
501       $row->{$_}{data}            = $form->format_amount(\%myconfig, $ref->{$_}, 2);
502       $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
503     }
504     foreach ( @pricegroup_columns ) {
505       $row->{$_}{data}            = $form->format_amount(\%myconfig, $ref->{"$_"}, 2);
506     };
507
508
509     map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
510
511     $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
512
513     # 'yes' and 'no' for boolean value shop
514     if ($form->{l_shop}) {
515       $row->{shop}{data} = $row->{shop}{data}? $::locale->text('yes') : $::locale->text('no');
516     }
517
518     if (!$ref->{assemblyitem}) {
519       foreach my $col (@subtotal_columns) {
520         $totals{$col}    += $soldtotal * $ref->{$col};
521         $subtotals{$col} += $soldtotal * $ref->{$col};
522       }
523
524       $subtotals{soldtotal} += $soldtotal;
525     }
526
527     # set module stuff
528     if ($ref->{module} eq 'oe') {
529       # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
530       #
531       # | ist bestellt  | Von Kunden bestellt |  -> edit_oe_ord_link
532       # | Anfrage       | Angebot             |  -> edit_oe_quo_link
533
534       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');
535       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');
536
537       $row->{ordnumber}{link} = $edit_oe_ord_link;
538       $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
539
540     } else {
541       $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback') if ($ref->{invnumber});
542     }
543
544     # set properties of images
545     if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
546       $row->{image}{data}     = '';
547       $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
548     }
549     map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
550
551     $row->{notes}{data} = SL::HTML::Util->strip($ref->{notes});
552     $row->{type_and_classific}{data} = $::request->presenter->type_abbreviation($ref->{part_type}).
553                                        $::request->presenter->classification_abbreviation($ref->{classification_id});
554
555     $report->add_data($row);
556
557     my $next_ref = $form->{parts}[$idx + 1];
558
559     # insert subtotal rows
560     if (($form->{l_subtotal} eq 'Y') &&
561         (!$next_ref ||
562          (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
563       my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
564
565       if ( !$form->{l_assembly} || !$form->{bom}) {
566         $row->{soldtotal}->{data} = $form->format_amount(\%myconfig, $subtotals{soldtotal});
567       }
568
569       map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
570       map { $subtotals{$_} = 0 } ('soldtotal', @subtotal_columns);
571
572       $report->add_data($row);
573
574       $same_item = $next_ref->{ $form->{sort} };
575     }
576
577     $idx++;
578   }
579
580   if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
581     my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
582
583     map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
584
585     $report->add_separator();
586     $report->add_data($row);
587   }
588
589   $report->generate_with_headers();
590
591   $lxdebug->leave_sub();
592 }    #end generate_report
593
594 sub continue { call_sub($form->{"nextsub"}); }