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 my %is_xyz = map { +"is_$_" => ($form->{searchitems} eq $_) } qw(part service assembly assortment);
89 $form->{title} = (ucfirst $form->{searchitems}) . "s";
90 $form->{title} =~ s/ys$/ies/;
91 $form->{title} = $locale->text($form->{title});
92 $form->{title} = $locale->text('Assemblies') if ($is_xyz{is_assembly});
94 $form->{CUSTOM_VARIABLES} = CVar->get_configs('module' => 'IC');
95 ($form->{CUSTOM_VARIABLES_FILTER_CODE},
96 $form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables' => $form->{CUSTOM_VARIABLES},
97 'include_prefix' => 'l_',
98 'include_value' => 'Y');
102 $form->get_lists('partsgroup' => 'ALL_PARTSGROUPS');
103 print $form->parse_html_template('ic/search', { %is_xyz, });
105 $lxdebug->leave_sub();
108 sub search_update_prices {
109 $lxdebug->enter_sub();
111 $auth->assert('part_service_assembly_edit');
113 my $pricegroups = IC->get_pricegroups(\%myconfig, \%$form);
115 $form->{title} = $locale->text('Update Prices');
119 print $form->parse_html_template('ic/search_update_prices', { PRICE_ROWS => $pricegroups });
121 $lxdebug->leave_sub();
124 sub confirm_price_update {
125 $lxdebug->enter_sub();
127 $auth->assert('part_service_assembly_edit');
130 my $value_found = undef;
132 foreach my $idx (qw(sellprice listprice), (1..$form->{price_rows})) {
133 my $name = $idx =~ m/\d/ ? $form->{"pricegroup_${idx}"} : $idx eq 'sellprice' ? $locale->text('Sell Price') : $locale->text('List Price');
134 my $type = $idx =~ m/\d/ ? $form->{"pricegroup_type_${idx}"} : $form->{"${idx}_type"};
135 my $value_idx = $idx =~ m/\d/ ? "price_${idx}" : $idx;
136 my $value = $form->parse_amount(\%myconfig, $form->{$value_idx});
138 if ((0 > $value) && ($type eq 'percent')) {
139 push @errors, $locale->text('You cannot adjust the price for pricegroup "#1" by a negative percentage.', $name);
141 } elsif (!$value && ($form->{$value_idx} ne '')) {
142 push @errors, $locale->text('No valid number entered for pricegroup "#1".', $name);
144 } elsif (0 < $value) {
149 push @errors, $locale->text('No prices will be updated because no prices have been entered.') if (!$value_found);
151 my $num_matches = IC->get_num_matches_for_priceupdate();
156 $form->show_generic_error(join('<br>', @errors), 'back_button' => 1);
159 $form->{nextsub} = "update_prices";
161 map { delete $form->{$_} } qw(action header);
163 print $form->parse_html_template('ic/confirm_price_update', { HIDDENS => [ map { name => $_, value => $form->{$_} }, keys %$form ],
164 num_matches => $num_matches });
166 $lxdebug->leave_sub();
170 $lxdebug->enter_sub();
172 $auth->assert('part_service_assembly_edit');
174 my $num_updated = IC->update_prices(\%myconfig, \%$form);
176 if (-1 != $num_updated) {
177 $form->redirect($locale->text('#1 prices were updated.', $num_updated));
179 $form->error($locale->text('Could not update prices!'));
182 $lxdebug->leave_sub();
186 $::lxdebug->enter_sub();
188 $::auth->assert('part_service_assembly_edit');
190 $::form->{l_soldtotal} = "Y";
191 $::form->{sort} = "soldtotal";
192 $::form->{lastsort} = "soldtotal";
194 $::form->{l_qty} = undef;
195 $::form->{l_linetotal} = undef;
196 $::form->{l_number} = "Y";
197 $::form->{number} = "position";
199 unless ( $::form->{bought}
202 || $::form->{quoted}) {
203 $::form->{bought} = $::form->{sold} = 1;
208 $lxdebug->leave_sub();
213 # Warning, deep magic ahead.
214 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
216 # flags coming from the form:
218 # searchitems=part revers=0 lastsort=''
221 # partnumber ean description partsgroup classification serialnumber make model drawing microfiche
222 # transdatefrom transdateto
225 # itemstatus = active | onhand | short | obsolete | orphaned
226 # action = continue | top100
229 # bought sold onorder ordered rfq quoted
230 # l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
231 # l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
232 # l_partsgroup l_subtotal l_soldtotal l_deliverydate l_pricegroups
235 # nextsub revers lastsort sort ndxs_counter
237 sub generate_report {
238 $lxdebug->enter_sub();
240 $auth->assert('part_service_assembly_details');
242 my ($revers, $lastsort, $description);
244 my $cvar_configs = CVar->get_configs('module' => 'IC');
247 '' => $locale->text('Articles'),
248 part => $locale->text('Parts'),
249 service => $locale->text('Services'),
250 assembly => $locale->text('Assemblies'),
251 assortment => $locale->text('Assortments'),
254 $form->{title} = $titles{$form->{searchitems}};
257 'bin' => { 'text' => $locale->text('Bin'), },
258 'deliverydate' => { 'text' => $locale->text('deliverydate'), },
259 'description' => { 'text' => $locale->text('Part Description'), },
260 'notes' => { 'text' => $locale->text('Notes'), },
261 'drawing' => { 'text' => $locale->text('Drawing'), },
262 'ean' => { 'text' => $locale->text('EAN'), },
263 'image' => { 'text' => $locale->text('Image'), },
264 'insertdate' => { 'text' => $locale->text('Insert Date'), },
265 'invnumber' => { 'text' => $locale->text('Invoice Number'), },
266 'lastcost' => { 'text' => $locale->text('Last Cost'), },
267 'linetotallastcost' => { 'text' => $locale->text('Extended'), },
268 'linetotallistprice' => { 'text' => $locale->text('Extended'), },
269 'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
270 'listprice' => { 'text' => $locale->text('List Price'), },
271 'microfiche' => { 'text' => $locale->text('Microfiche'), },
272 'name' => { 'text' => $locale->text('Name'), },
273 'onhand' => { 'text' => $locale->text('Stocked Qty'), },
274 'ordnumber' => { 'text' => $locale->text('Order Number'), },
275 'partnumber' => { 'text' => $locale->text('Part Number'), },
276 'partsgroup' => { 'text' => $locale->text('Partsgroup'), },
277 'priceupdate' => { 'text' => $locale->text('Updated'), },
278 'quonumber' => { 'text' => $locale->text('Quotation'), },
279 'rop' => { 'text' => $locale->text('ROP'), },
280 'sellprice' => { 'text' => $locale->text('Sell Price'), },
281 'serialnumber' => { 'text' => $locale->text('Serial Number'), },
282 'soldtotal' => { 'text' => $locale->text('Qty in Selected Records'), },
283 'name' => { 'text' => $locale->text('Name in Selected Records'), },
284 'transdate' => { 'text' => $locale->text('Transdate'), },
285 'unit' => { 'text' => $locale->text('Unit'), },
286 'weight' => { 'text' => $locale->text('Weight'), },
287 'shop' => { 'text' => $locale->text('Shop article'), },
288 'type_and_classific' => { 'text' => $locale->text('Type'), },
289 'projectnumber' => { 'text' => $locale->text('Project Number'), },
290 'projectdescription' => { 'text' => $locale->text('Project Description'), },
293 $revers = $form->{revers};
294 $lastsort = $form->{lastsort};
296 # sorting and direction of sorting
297 # ToDO: change this to the simpler field+direction method
298 if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
300 $form->{lastsort} = "partnumber";
301 $form->{sort} = "partnumber";
303 if ($form->{lastsort} eq $form->{sort}) {
304 $form->{revers} = 1 - $form->{revers};
307 $form->{lastsort} = $form->{sort};
311 # special case if we have a serialnumber limit search
312 # serialnumbers are only given in invoices and orders,
313 # so they can only pop up in bought, sold, rfq, and quoted stuff
314 $form->{no_sn_joins} = 'Y' if ( !$form->{bought} && !$form->{sold}
315 && !$form->{rfq} && !$form->{quoted}
316 && ($form->{l_serialnumber} || $form->{serialnumber}));
318 # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
319 # if any of these are ticked the behavior changes slightly for lastcost
320 # since all those are aggregation checks for the legder tables this is an internal switch
321 # refered to as ledgerchecks
322 $form->{ledgerchecks} = 'Y' if ( $form->{bought} || $form->{sold} || $form->{onorder}
323 || $form->{ordered} || $form->{rfq} || $form->{quoted});
325 # if something should be activated if something else is active, enter it here
327 onhand => [ qw(l_onhand) ],
328 short => [ qw(l_onhand) ],
329 onorder => [ qw(l_ordnumber) ],
330 ordered => [ qw(l_ordnumber) ],
331 rfq => [ qw(l_quonumber) ],
332 quoted => [ qw(l_quonumber) ],
333 bought => [ qw(l_invnumber) ],
334 sold => [ qw(l_invnumber) ],
335 ledgerchecks => [ qw(l_name) ],
336 serialnumber => [ qw(l_serialnumber) ],
337 no_sn_joins => [ qw(bought sold) ],
340 # get name of partsgroup if id is given
342 if ($form->{partsgroup_id}) {
343 my $pg = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load;
344 $pg_name = $pg->{'partsgroup'};
347 # these strings get displayed at the top of the results to indicate the user which switches were used
349 active => $locale->text('Active'),
350 obsolete => $locale->text('Obsolete'),
351 orphaned => $locale->text('Orphaned'),
352 onhand => $locale->text('On Hand'),
353 short => $locale->text('Short'),
354 onorder => $locale->text('On Order'),
355 ordered => $locale->text('Ordered'),
356 rfq => $locale->text('RFQ'),
357 quoted => $locale->text('Quoted'),
358 bought => $locale->text('Bought'),
359 sold => $locale->text('Sold'),
360 transdatefrom => $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
361 transdateto => $locale->text('To (time)') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
362 partnumber => $locale->text('Part Number') . ": '$form->{partnumber}'",
363 partsgroup => $locale->text('Partsgroup') . ": '$form->{partsgroup}'",
364 partsgroup_id => $locale->text('Partsgroup') . ": '$pg_name'",
365 serialnumber => $locale->text('Serial Number') . ": '$form->{serialnumber}'",
366 description => $locale->text('Part Description') . ": '$form->{description}'",
367 make => $locale->text('Make') . ": '$form->{make}'",
368 model => $locale->text('Model') . ": '$form->{model}'",
369 drawing => $locale->text('Drawing') . ": '$form->{drawing}'",
370 microfiche => $locale->text('Microfiche') . ": '$form->{microfiche}'",
371 l_soldtotal => $locale->text('Qty in Selected Records'),
372 ean => $locale->text('EAN') . ": '$form->{ean}'",
373 insertdatefrom => $locale->text('Insert Date') . ": " . $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1),
374 insertdateto => $locale->text('Insert Date') . ": " . $locale->text('To (time)') . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1),
377 my @itemstatus_keys = qw(active obsolete orphaned onhand short);
378 my @callback_keys = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
379 drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto insertdatefrom insertdateto ean shop all);
381 # calculate dependencies
382 for (@itemstatus_keys, @callback_keys) {
383 next if ($form->{itemstatus} ne $_ && !$form->{$_});
384 map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
387 # generate callback and optionstrings
389 for my $key (@itemstatus_keys, @callback_keys) {
390 next if ($form->{itemstatus} ne $key && !$form->{$key});
391 push @options, $optiontexts{$key};
394 # special case for lastcost
395 if ($form->{ledgerchecks}){
396 # ledgerchecks don't know about sellprice or lastcost. they just return a
397 # price. so rename sellprice to price, and drop lastcost.
398 $column_defs{sellprice}{text} = $locale->text('Price');
399 $form->{l_lastcost} = ""
402 if ($form->{description}) {
403 $description = $form->{description};
404 $description =~ s/\n/<br>/g;
407 if ($form->{l_linetotal}) {
408 $form->{l_qty} = "Y";
409 $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
410 $form->{l_linetotallastcost} = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if $form->{l_lastcost};
411 $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
413 $form->{"l_type_and_classific"} = "Y";
415 if ($form->{searchitems} eq 'service') {
417 # remove bin, weight and rop from list
418 map { $form->{"l_$_"} = "" } qw(bin weight rop);
420 $form->{l_onhand} = "";
422 # qty is irrelevant unless bought or sold
428 || $form->{quoted}) {
429 # $form->{l_onhand} = "Y";
431 $form->{l_linetotalsellprice} = "";
432 $form->{l_linetotallastcost} = "";
436 # soldtotal doesn't make sense with more than one bsooqr option.
437 # so reset it to sold (the most common option), and issue a warning
439 # also it doesn't make sense without bsooqr. disable and issue a warning too
440 my @bsooqr = qw(sold bought onorder ordered rfq quoted);
441 my $bsooqr_mode = grep { $form->{$_} } @bsooqr;
442 if ($form->{l_subtotal} && 1 < $bsooqr_mode) {
443 my $enabled = first { $form->{$_} } @bsooqr;
444 $form->{$_} = '' for @bsooqr;
445 $form->{$enabled} = 'Y';
447 push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
449 if ($form->{l_soldtotal} && !$bsooqr_mode) {
450 delete $form->{l_soldtotal};
452 flash('warning', $::locale->text('Soldtotal does not make sense without any bsooqr options'));
454 if ($form->{l_name} && !$bsooqr_mode) {
455 delete $form->{l_name};
457 flash('warning', $::locale->text('Name does not make sense without any bsooqr options'));
459 IC->all_parts(\%myconfig, \%$form);
462 partnumber type_and_classific description notes partsgroup bin onhand rop soldtotal unit listprice
463 linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
464 priceupdate weight image drawing microfiche invnumber ordnumber quonumber
465 transdate name serialnumber deliverydate ean projectnumber projectdescription
469 my $pricegroups = SL::DB::Manager::Pricegroup->get_all_sorted;
470 my @pricegroup_columns;
471 my %column_defs_pricegroups;
472 if ($form->{l_pricegroups}) {
473 @pricegroup_columns = map { "pricegroup_" . $_->id } @{ $pricegroups };
474 %column_defs_pricegroups = map {
475 "pricegroup_" . $_->id => {
476 text => $::locale->text('Pricegroup') . ' ' . $_->pricegroup,
481 push @columns, @pricegroup_columns;
483 my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
484 my @searchable_custom_variables = grep { $_->{searchable} } @{ $cvar_configs };
485 my %column_defs_cvars = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
487 push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
489 %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
490 map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
491 map { $column_defs{$_}->{align} = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal shop), @pricegroup_columns;
493 my @hidden_variables = (
494 qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups insertdatefrom insertdateto),
495 qw(l_type_and_classific classification_id),
498 map({ "cvar_$_->{name}" } @searchable_custom_variables),
499 map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
500 map({ "l_$_" } @columns),
503 my $callback = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
505 my @sort_full = qw(partnumber description onhand soldtotal deliverydate insertdate shop);
506 my @sort_no_revers = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
508 foreach my $col (@sort_full) {
509 $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
511 map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
513 # add order to callback
514 $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
516 my $report = SL::ReportGenerator->new(\%myconfig, $form);
518 my %attachment_basenames = (
519 'part' => $locale->text('part_list'),
520 'service' => $locale->text('service_list'),
521 'assembly' => $locale->text('assembly_list'),
522 'article' => $locale->text('article_list'),
525 $report->set_options('raw_top_info_text' => $form->parse_html_template('ic/generate_report_top', { options => \@options }),
526 'raw_bottom_info_text' => $form->parse_html_template('ic/generate_report_bottom' ,
527 { PART_CLASSIFICATIONS => SL::DB::Manager::PartClassification->get_all_sorted }),
528 'output_format' => 'HTML',
529 'title' => $form->{title},
530 'attachment_basename' => $attachment_basenames{$form->{searchitems}} . strftime('_%Y%m%d', localtime time),
532 $report->set_options_from_form();
533 $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
535 $report->set_columns(%column_defs);
536 $report->set_column_order(@columns);
538 $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
540 $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
542 CVar->add_custom_variables_to_report('module' => 'IC',
543 'trans_id_field' => 'id',
544 'configs' => $cvar_configs,
545 'column_defs' => \%column_defs,
546 'data' => $form->{parts});
548 CVar->add_custom_variables_to_report('module' => 'IC',
549 'sub_module' => sub { $_[0]->{ioi} },
550 'trans_id_field' => 'ioi_id',
551 'configs' => $cvar_configs,
552 'column_defs' => \%column_defs,
553 'data' => $form->{parts});
555 my @subtotal_columns = qw(sellprice listprice lastcost);
556 my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
557 my %totals = map { $_ => 0 } @subtotal_columns;
559 my $same_item = @{ $form->{parts} } ? $form->{parts}[0]{ $form->{sort} } : undef;
561 my $defaults = AM->get_defaults();
564 foreach my $ref (@{ $form->{parts} }) {
566 # fresh row, for inserting later
567 my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
569 $ref->{exchangerate} ||= 1;
570 $ref->{price_factor} ||= 1;
571 $ref->{sellprice} *= $ref->{exchangerate} / $ref->{price_factor};
572 $ref->{listprice} *= $ref->{exchangerate} / $ref->{price_factor};
573 $ref->{lastcost} *= $ref->{exchangerate} / $ref->{price_factor};
575 # use this for assemblies
576 my $soldtotal = $bsooqr_mode ? $ref->{soldtotal} : $ref->{onhand};
578 if ($ref->{assemblyitem}) {
579 $row->{partnumber}{align} = 'right';
580 $row->{soldtotal}{data} = 0;
581 $soldtotal = 0 if ($form->{sold});
584 my $edit_link = build_std_url('script=controller.pl', 'action=Part/edit', 'part.id=' . E($ref->{id}), 'callback');
585 $row->{partnumber}->{link} = $edit_link;
586 $row->{description}->{link} = $edit_link;
588 foreach (qw(sellprice listprice lastcost)) {
589 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}, 2);
590 $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
592 foreach ( @pricegroup_columns ) {
593 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{"$_"}, 2);
597 map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
599 $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
601 # 'yes' and 'no' for boolean value shop
602 if ($form->{l_shop}) {
603 $row->{shop}{data} = $row->{shop}{data}? $::locale->text('yes') : $::locale->text('no');
606 if (!$ref->{assemblyitem}) {
607 foreach my $col (@subtotal_columns) {
608 $totals{$col} += $soldtotal * $ref->{$col};
609 $subtotals{$col} += $soldtotal * $ref->{$col};
612 $subtotals{soldtotal} += $soldtotal;
616 if ($ref->{module} eq 'oe') {
617 # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
619 # | ist bestellt | Von Kunden bestellt | -> edit_oe_ord_link
620 # | Anfrage | Angebot | -> edit_oe_quo_link
622 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');
623 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');
625 $row->{ordnumber}{link} = $edit_oe_ord_link;
626 $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
629 $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback') if ($ref->{invnumber});
632 # set properties of images
633 if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
634 $row->{image}{data} = '';
635 $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
637 map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
639 $row->{notes}{data} = SL::HTML::Util->strip($ref->{notes});
640 $row->{type_and_classific}{data} = $::request->presenter->type_abbreviation($ref->{part_type}).
641 $::request->presenter->classification_abbreviation($ref->{classification_id});
643 $report->add_data($row);
645 my $next_ref = $form->{parts}[$idx + 1];
647 # insert subtotal rows
648 if (($form->{l_subtotal} eq 'Y') &&
650 (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
651 my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
653 if (($form->{searchitems} ne 'assembly') || !$form->{bom}) {
654 $row->{soldtotal}->{data} = $form->format_amount(\%myconfig, $subtotals{soldtotal});
657 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
658 map { $subtotals{$_} = 0 } ('soldtotal', @subtotal_columns);
660 $report->add_data($row);
662 $same_item = $next_ref->{ $form->{sort} };
668 if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
669 my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
671 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
673 $report->add_separator();
674 $report->add_data($row);
677 $report->generate_with_headers();
679 $lxdebug->leave_sub();
680 } #end generate_report
682 sub ajax_autocomplete {
683 $main::lxdebug->enter_sub();
685 my $form = $main::form;
686 my %myconfig = %main::myconfig;
688 $form->{column} = 'description' unless $form->{column} =~ /^partnumber|description$/;
689 $form->{$form->{column}} = $form->{q} || '';
690 $form->{limit} = ($form->{limit} * 1) || 10;
691 $form->{searchitems} ||= '';
693 my @results = IC->all_parts(\%myconfig, $form);
695 print $form->ajax_response_header(),
696 $form->parse_html_template('ic/ajax_autocomplete');
698 $main::lxdebug->leave_sub();
705 delete @{$::form}{qw(action action_add action_back_to_record back_sub description item notes partnumber sellprice taxaccount2 unit vc)};
707 $::auth->restore_form_from_session($::form->{previousform}, clobber => 1);
708 $::form->{rowcount}--;
709 $::form->{action} = 'display_form';
710 $::form->{callback} = $::form->{script} . '?' . join('&', map { $::form->escape($_) . '=' . $::form->escape($::form->{$_}) } sort keys %{ $::form });
714 sub continue { call_sub($form->{"nextsub"}); }
717 my $action = first { $::form->{"action_${_}"} } qw(add back_to_record);
718 $::form->error($::locale->text('No action defined.')) unless $action;
720 $::form->{dispatched_action} = $action;