1 #=====================================================================
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
7 #=====================================================================
8 # SQL-Ledger, Accounting
11 # Author: Dieter Simader
12 # Email: dsimader@sql-ledger.org
13 # Web: http://www.sql-ledger.org
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #======================================================================
30 # Inventory Control module
32 #======================================================================
34 use POSIX qw(strftime);
35 use List::Util qw(first max);
36 use List::MoreUtils qw(any);
41 use SL::Helper::Flash qw(flash);
43 use SL::ReportGenerator;
51 our ($form, $locale, %myconfig, $lxdebug, $auth);
53 require "bin/mozilla/io.pl";
54 require "bin/mozilla/common.pl";
55 require "bin/mozilla/reportgenerator.pl";
60 # type=submit $locale->text('Add Part')
61 # type=submit $locale->text('Add Service')
62 # type=submit $locale->text('Add Assembly')
63 # type=submit $locale->text('Edit Part')
64 # type=submit $locale->text('Edit Service')
65 # type=submit $locale->text('Edit Assembly')
66 # $locale->text('Parts')
67 # $locale->text('Services')
68 # $locale->text('Inventory quantity must be zero before you can set this part obsolete!')
69 # $locale->text('Inventory quantity must be zero before you can set this assembly obsolete!')
70 # $locale->text('Part Number missing!')
71 # $locale->text('Service Number missing!')
72 # $locale->text('Assembly Number missing!')
73 # $locale->text('ea');
78 $lxdebug->enter_sub();
80 $auth->assert('part_service_assembly_edit');
82 my $title = 'Add ' . ucfirst $form->{part_type};
83 $form->{title} = $locale->text($title);
84 $form->{callback} = "$form->{script}?action=add&part_type=$form->{part_type}" unless $form->{callback};
85 $form->{unit_changeable} = 1;
87 IC->get_pricegroups(\%myconfig, \%$form);
91 $lxdebug->leave_sub();
95 $lxdebug->enter_sub();
97 $auth->assert('part_service_assembly_details');
99 $form->{revers} = 0; # switch for backward sorting
100 $form->{lastsort} = ""; # memory for which table was sort at last time
101 $form->{ndxs_counter} = 0; # counter for added entries to top100
103 my %is_xyz = map { +"is_$_" => ($form->{searchitems} eq $_) } qw(part service assembly);
105 $form->{title} = (ucfirst $form->{searchitems}) . "s";
106 $form->{title} = $locale->text($form->{title});
107 $form->{title} = $locale->text('Assemblies') if ($is_xyz{is_assembly});
109 $form->{CUSTOM_VARIABLES} = CVar->get_configs('module' => 'IC');
110 ($form->{CUSTOM_VARIABLES_FILTER_CODE},
111 $form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables' => $form->{CUSTOM_VARIABLES},
112 'include_prefix' => 'l_',
113 'include_value' => 'Y');
117 $form->get_lists('partsgroup' => 'ALL_PARTSGROUPS');
118 print $form->parse_html_template('ic/search', { %is_xyz, });
120 $lxdebug->leave_sub();
123 sub search_update_prices {
124 $lxdebug->enter_sub();
126 $auth->assert('part_service_assembly_edit');
128 my $pricegroups = IC->get_pricegroups(\%myconfig, \%$form);
130 $form->{title} = $locale->text('Update Prices');
134 print $form->parse_html_template('ic/search_update_prices', { PRICE_ROWS => $pricegroups });
136 $lxdebug->leave_sub();
139 sub confirm_price_update {
140 $lxdebug->enter_sub();
142 $auth->assert('part_service_assembly_edit');
145 my $value_found = undef;
147 foreach my $idx (qw(sellprice listprice), (1..$form->{price_rows})) {
148 my $name = $idx =~ m/\d/ ? $form->{"pricegroup_${idx}"} : $idx eq 'sellprice' ? $locale->text('Sell Price') : $locale->text('List Price');
149 my $type = $idx =~ m/\d/ ? $form->{"pricegroup_type_${idx}"} : $form->{"${idx}_type"};
150 my $value_idx = $idx =~ m/\d/ ? "price_${idx}" : $idx;
151 my $value = $form->parse_amount(\%myconfig, $form->{$value_idx});
153 if ((0 > $value) && ($type eq 'percent')) {
154 push @errors, $locale->text('You cannot adjust the price for pricegroup "#1" by a negative percentage.', $name);
156 } elsif (!$value && ($form->{$value_idx} ne '')) {
157 push @errors, $locale->text('No valid number entered for pricegroup "#1".', $name);
159 } elsif (0 < $value) {
164 push @errors, $locale->text('No prices will be updated because no prices have been entered.') if (!$value_found);
166 my $num_matches = IC->get_num_matches_for_priceupdate();
171 $form->show_generic_error(join('<br>', @errors), 'back_button' => 1);
174 $form->{nextsub} = "update_prices";
176 map { delete $form->{$_} } qw(action header);
178 print $form->parse_html_template('ic/confirm_price_update', { HIDDENS => [ map { name => $_, value => $form->{$_} }, keys %$form ],
179 num_matches => $num_matches });
181 $lxdebug->leave_sub();
185 $lxdebug->enter_sub();
187 $auth->assert('part_service_assembly_edit');
189 my $num_updated = IC->update_prices(\%myconfig, \%$form);
191 if (-1 != $num_updated) {
192 $form->redirect($locale->text('#1 prices were updated.', $num_updated));
194 $form->error($locale->text('Could not update prices!'));
197 $lxdebug->leave_sub();
201 $::lxdebug->enter_sub();
203 $::auth->assert('part_service_assembly_edit');
205 $::form->{l_soldtotal} = "Y";
206 $::form->{sort} = "soldtotal";
207 $::form->{lastsort} = "soldtotal";
209 $::form->{l_qty} = undef;
210 $::form->{l_linetotal} = undef;
211 $::form->{l_number} = "Y";
212 $::form->{number} = "position";
214 unless ( $::form->{bought}
217 || $::form->{quoted}) {
218 $::form->{bought} = $::form->{sold} = 1;
223 $lxdebug->leave_sub();
228 # Warning, deep magic ahead.
229 # This function parses the requested details, sanity checks them, and converts them into a format thats usable for IC->all_parts
231 # flags coming from the form:
233 # searchitems=part revers=0 lastsort=''
236 # partnumber ean description partsgroup serialnumber make model drawing microfiche
237 # transdatefrom transdateto
240 # itemstatus = active | onhand | short | obsolete | orphaned
241 # action = continue | top100
244 # bought sold onorder ordered rfq quoted
245 # l_partnumber l_description l_serialnumber l_unit l_listprice l_sellprice l_lastcost
246 # l_linetotal l_priceupdate l_bin l_rop l_weight l_image l_drawing l_microfiche
247 # l_partsgroup l_subtotal l_soldtotal l_deliverydate l_pricegroups
250 # nextsub revers lastsort sort ndxs_counter
252 sub generate_report {
253 $lxdebug->enter_sub();
255 $auth->assert('part_service_assembly_details');
257 my ($revers, $lastsort, $description);
259 my $cvar_configs = CVar->get_configs('module' => 'IC');
262 '' => $locale->text('Articles'),
263 part => $locale->text('Parts'),
264 service => $locale->text('Services'),
265 assembly => $locale->text('Assemblies'),
266 assortment => $locale->text('Assortments'),
269 $form->{title} = $titles{$form->{searchitems}};
272 'bin' => { 'text' => $locale->text('Bin'), },
273 'deliverydate' => { 'text' => $locale->text('deliverydate'), },
274 'description' => { 'text' => $locale->text('Part Description'), },
275 'notes' => { 'text' => $locale->text('Notes'), },
276 'drawing' => { 'text' => $locale->text('Drawing'), },
277 'ean' => { 'text' => $locale->text('EAN'), },
278 'image' => { 'text' => $locale->text('Image'), },
279 'insertdate' => { 'text' => $locale->text('Insert Date'), },
280 'invnumber' => { 'text' => $locale->text('Invoice Number'), },
281 'lastcost' => { 'text' => $locale->text('Last Cost'), },
282 'linetotallastcost' => { 'text' => $locale->text('Extended'), },
283 'linetotallistprice' => { 'text' => $locale->text('Extended'), },
284 'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
285 'listprice' => { 'text' => $locale->text('List Price'), },
286 'microfiche' => { 'text' => $locale->text('Microfiche'), },
287 'name' => { 'text' => $locale->text('Name'), },
288 'onhand' => { 'text' => $locale->text('Stocked Qty'), },
289 'ordnumber' => { 'text' => $locale->text('Order Number'), },
290 'partnumber' => { 'text' => $locale->text('Part Number'), },
291 'partsgroup' => { 'text' => $locale->text('Group'), },
292 'priceupdate' => { 'text' => $locale->text('Updated'), },
293 'quonumber' => { 'text' => $locale->text('Quotation'), },
294 'rop' => { 'text' => $locale->text('ROP'), },
295 'sellprice' => { 'text' => $locale->text('Sell Price'), },
296 'serialnumber' => { 'text' => $locale->text('Serial Number'), },
297 'soldtotal' => { 'text' => $locale->text('Qty in Selected Records'), },
298 'name' => { 'text' => $locale->text('Name in Selected Records'), },
299 'transdate' => { 'text' => $locale->text('Transdate'), },
300 'unit' => { 'text' => $locale->text('Unit'), },
301 'weight' => { 'text' => $locale->text('Weight'), },
302 'shop' => { 'text' => $locale->text('Shop article'), },
303 'projectnumber' => { 'text' => $locale->text('Project Number'), },
304 'projectdescription' => { 'text' => $locale->text('Project Description'), },
307 $revers = $form->{revers};
308 $lastsort = $form->{lastsort};
310 # sorting and direction of sorting
311 # ToDO: change this to the simpler field+direction method
312 if (($form->{lastsort} eq "") && ($form->{sort} eq undef)) {
314 $form->{lastsort} = "partnumber";
315 $form->{sort} = "partnumber";
317 if ($form->{lastsort} eq $form->{sort}) {
318 $form->{revers} = 1 - $form->{revers};
321 $form->{lastsort} = $form->{sort};
325 # special case if we have a serialnumber limit search
326 # serialnumbers are only given in invoices and orders,
327 # so they can only pop up in bought, sold, rfq, and quoted stuff
328 $form->{no_sn_joins} = 'Y' if ( !$form->{bought} && !$form->{sold}
329 && !$form->{rfq} && !$form->{quoted}
330 && ($form->{l_serialnumber} || $form->{serialnumber}));
332 # special case for any checkbox of bought | sold | onorder | ordered | rfq | quoted.
333 # if any of these are ticked the behavior changes slightly for lastcost
334 # since all those are aggregation checks for the legder tables this is an internal switch
335 # refered to as ledgerchecks
336 $form->{ledgerchecks} = 'Y' if ( $form->{bought} || $form->{sold} || $form->{onorder}
337 || $form->{ordered} || $form->{rfq} || $form->{quoted});
339 # if something should be activated if something else is active, enter it here
341 onhand => [ qw(l_onhand) ],
342 short => [ qw(l_onhand) ],
343 onorder => [ qw(l_ordnumber) ],
344 ordered => [ qw(l_ordnumber) ],
345 rfq => [ qw(l_quonumber) ],
346 quoted => [ qw(l_quonumber) ],
347 bought => [ qw(l_invnumber) ],
348 sold => [ qw(l_invnumber) ],
349 ledgerchecks => [ qw(l_name) ],
350 serialnumber => [ qw(l_serialnumber) ],
351 no_sn_joins => [ qw(bought sold) ],
354 # get name of partsgroup if id is given
356 if ($form->{partsgroup_id}) {
357 my $pg = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load;
358 $pg_name = $pg->{'partsgroup'};
361 # these strings get displayed at the top of the results to indicate the user which switches were used
363 active => $locale->text('Active'),
364 obsolete => $locale->text('Obsolete'),
365 orphaned => $locale->text('Orphaned'),
366 onhand => $locale->text('On Hand'),
367 short => $locale->text('Short'),
368 onorder => $locale->text('On Order'),
369 ordered => $locale->text('Ordered'),
370 rfq => $locale->text('RFQ'),
371 quoted => $locale->text('Quoted'),
372 bought => $locale->text('Bought'),
373 sold => $locale->text('Sold'),
374 transdatefrom => $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1),
375 transdateto => $locale->text('To (time)') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1),
376 partnumber => $locale->text('Part Number') . ": '$form->{partnumber}'",
377 partsgroup => $locale->text('Group') . ": '$form->{partsgroup}'",
378 partsgroup_id => $locale->text('Group') . ": '$pg_name'",
379 serialnumber => $locale->text('Serial Number') . ": '$form->{serialnumber}'",
380 description => $locale->text('Part Description') . ": '$form->{description}'",
381 make => $locale->text('Make') . ": '$form->{make}'",
382 model => $locale->text('Model') . ": '$form->{model}'",
383 drawing => $locale->text('Drawing') . ": '$form->{drawing}'",
384 microfiche => $locale->text('Microfiche') . ": '$form->{microfiche}'",
385 l_soldtotal => $locale->text('Qty in Selected Records'),
386 ean => $locale->text('EAN') . ": '$form->{ean}'",
387 insertdatefrom => $locale->text('Insert Date') . ": " . $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1),
388 insertdateto => $locale->text('Insert Date') . ": " . $locale->text('To (time)') . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1),
391 my @itemstatus_keys = qw(active obsolete orphaned onhand short);
392 my @callback_keys = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
393 drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto insertdatefrom insertdateto ean shop all);
395 # calculate dependencies
396 for (@itemstatus_keys, @callback_keys) {
397 next if ($form->{itemstatus} ne $_ && !$form->{$_});
398 map { $form->{$_} = 'Y' } @{ $dependencies{$_} } if $dependencies{$_};
401 # generate callback and optionstrings
403 for my $key (@itemstatus_keys, @callback_keys) {
404 next if ($form->{itemstatus} ne $key && !$form->{$key});
405 push @options, $optiontexts{$key};
408 # special case for lastcost
409 if ($form->{ledgerchecks}){
410 # ledgerchecks don't know about sellprice or lastcost. they just return a
411 # price. so rename sellprice to price, and drop lastcost.
412 $column_defs{sellprice}{text} = $locale->text('Price');
413 $form->{l_lastcost} = ""
416 if ($form->{description}) {
417 $description = $form->{description};
418 $description =~ s/\n/<br>/g;
421 if ($form->{l_linetotal}) {
422 $form->{l_qty} = "Y";
423 $form->{l_linetotalsellprice} = "Y" if $form->{l_sellprice};
424 $form->{l_linetotallastcost} = $form->{searchitems} eq 'assembly' && !$form->{bom} ? "" : 'Y' if $form->{l_lastcost};
425 $form->{l_linetotallistprice} = "Y" if $form->{l_listprice};
428 if ($form->{searchitems} eq 'service') {
430 # remove bin, weight and rop from list
431 map { $form->{"l_$_"} = "" } qw(bin weight rop);
433 $form->{l_onhand} = "";
435 # qty is irrelevant unless bought or sold
441 || $form->{quoted}) {
442 # $form->{l_onhand} = "Y";
444 $form->{l_linetotalsellprice} = "";
445 $form->{l_linetotallastcost} = "";
449 # soldtotal doesn't make sense with more than one bsooqr option.
450 # so reset it to sold (the most common option), and issue a warning
452 # also it doesn't make sense without bsooqr. disable and issue a warning too
453 my @bsooqr = qw(sold bought onorder ordered rfq quoted);
454 my $bsooqr_mode = grep { $form->{$_} } @bsooqr;
455 if ($form->{l_subtotal} && 1 < $bsooqr_mode) {
456 my $enabled = first { $form->{$_} } @bsooqr;
457 $form->{$_} = '' for @bsooqr;
458 $form->{$enabled} = 'Y';
460 push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
462 if ($form->{l_soldtotal} && !$bsooqr_mode) {
463 delete $form->{l_soldtotal};
465 flash('warning', $::locale->text('Soldtotal does not make sense without any bsooqr options'));
467 if ($form->{l_name} && !$bsooqr_mode) {
468 delete $form->{l_name};
470 flash('warning', $::locale->text('Name does not make sense without any bsooqr options'));
472 IC->all_parts(\%myconfig, \%$form);
475 partnumber description notes partsgroup bin onhand rop soldtotal unit listprice
476 linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
477 priceupdate weight image drawing microfiche invnumber ordnumber quonumber
478 transdate name serialnumber deliverydate ean projectnumber projectdescription
482 my $pricegroups = SL::DB::Manager::Pricegroup->get_all_sorted;
483 my @pricegroup_columns;
484 my %column_defs_pricegroups;
485 if ($form->{l_pricegroups}) {
486 @pricegroup_columns = map { "pricegroup_" . $_->id } @{ $pricegroups };
487 %column_defs_pricegroups = map {
488 "pricegroup_" . $_->id => {
489 text => $::locale->text('Pricegroup') . ' ' . $_->pricegroup,
494 push @columns, @pricegroup_columns;
496 my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
497 my @searchable_custom_variables = grep { $_->{searchable} } @{ $cvar_configs };
498 my %column_defs_cvars = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables;
500 push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables;
502 %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
503 map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
504 map { $column_defs{$_}->{align} = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal shop), @pricegroup_columns;
506 my @hidden_variables = (
507 qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups insertdatefrom insertdateto),
510 map({ "cvar_$_->{name}" } @searchable_custom_variables),
511 map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
512 map({ "l_$_" } @columns),
515 my $callback = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
517 my @sort_full = qw(partnumber description onhand soldtotal deliverydate insertdate shop);
518 my @sort_no_revers = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
520 foreach my $col (@sort_full) {
521 $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
523 map { $column_defs{$_}->{link} = "${callback}&sort=$_" } @sort_no_revers;
525 # add order to callback
526 $form->{callback} = join '&', ($callback, map { "${_}=" . E($form->{$_}) } qw(sort revers));
528 my $report = SL::ReportGenerator->new(\%myconfig, $form);
530 my %attachment_basenames = (
531 'part' => $locale->text('part_list'),
532 'service' => $locale->text('service_list'),
533 'assembly' => $locale->text('assembly_list'),
536 $report->set_options('raw_top_info_text' => $form->parse_html_template('ic/generate_report_top', { options => \@options }),
537 'raw_bottom_info_text' => $form->parse_html_template('ic/generate_report_bottom'),
538 'output_format' => 'HTML',
539 'title' => $form->{title},
540 'attachment_basename' => $attachment_basenames{$form->{searchitems}} . strftime('_%Y%m%d', localtime time),
542 $report->set_options_from_form();
543 $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
545 $report->set_columns(%column_defs);
546 $report->set_column_order(@columns);
548 $report->set_export_options('generate_report', @hidden_variables, qw(sort revers));
550 $report->set_sort_indicator($form->{sort}, $form->{revers} ? 0 : 1);
552 CVar->add_custom_variables_to_report('module' => 'IC',
553 'trans_id_field' => 'id',
554 'configs' => $cvar_configs,
555 'column_defs' => \%column_defs,
556 'data' => $form->{parts});
558 CVar->add_custom_variables_to_report('module' => 'IC',
559 'sub_module' => sub { $_[0]->{ioi} },
560 'trans_id_field' => 'ioi_id',
561 'configs' => $cvar_configs,
562 'column_defs' => \%column_defs,
563 'data' => $form->{parts});
565 my @subtotal_columns = qw(sellprice listprice lastcost);
566 my %subtotals = map { $_ => 0 } ('onhand', @subtotal_columns);
567 my %totals = map { $_ => 0 } @subtotal_columns;
569 my $same_item = @{ $form->{parts} } ? $form->{parts}[0]{ $form->{sort} } : undef;
571 my $defaults = AM->get_defaults();
574 foreach my $ref (@{ $form->{parts} }) {
576 # fresh row, for inserting later
577 my $row = { map { $_ => { 'data' => $ref->{$_} } } @columns };
579 $ref->{exchangerate} ||= 1;
580 $ref->{price_factor} ||= 1;
581 $ref->{sellprice} *= $ref->{exchangerate} / $ref->{price_factor};
582 $ref->{listprice} *= $ref->{exchangerate} / $ref->{price_factor};
583 $ref->{lastcost} *= $ref->{exchangerate} / $ref->{price_factor};
585 # use this for assemblies
586 my $soldtotal = $bsooqr_mode ? $ref->{soldtotal} : $ref->{onhand};
588 if ($ref->{assemblyitem}) {
589 $row->{partnumber}{align} = 'right';
590 $row->{soldtotal}{data} = 0;
591 $soldtotal = 0 if ($form->{sold});
594 my $edit_link = build_std_url('script=controller.pl', 'action=Part/edit', 'part.id=' . E($ref->{id}), 'callback');
595 $row->{partnumber}->{link} = $edit_link;
596 $row->{description}->{link} = $edit_link;
598 foreach (qw(sellprice listprice lastcost)) {
599 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}, 2);
600 $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
602 foreach ( @pricegroup_columns ) {
603 $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{"$_"}, 2);
607 map { $row->{$_}{data} = $form->format_amount(\%myconfig, $ref->{$_}); } qw(onhand rop weight soldtotal);
609 $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
611 # 'yes' and 'no' for boolean value shop
612 if ($form->{l_shop}) {
613 $row->{shop}{data} = $row->{shop}{data}? $::locale->text('yes') : $::locale->text('no');
616 if (!$ref->{assemblyitem}) {
617 foreach my $col (@subtotal_columns) {
618 $totals{$col} += $soldtotal * $ref->{$col};
619 $subtotals{$col} += $soldtotal * $ref->{$col};
622 $subtotals{soldtotal} += $soldtotal;
626 if ($ref->{module} eq 'oe') {
627 # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
629 # | ist bestellt | Von Kunden bestellt | -> edit_oe_ord_link
630 # | Anfrage | Angebot | -> edit_oe_quo_link
632 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');
633 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');
635 $row->{ordnumber}{link} = $edit_oe_ord_link;
636 $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
639 $row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback') if ($ref->{invnumber});
642 # set properties of images
643 if ($ref->{image} && (lc $report->{options}->{output_format} eq 'html')) {
644 $row->{image}{data} = '';
645 $row->{image}{raw_data} = '<a href="' . H($ref->{image}) . '"><img src="' . H($ref->{image}) . '" height="32" border="0"></a>';
647 map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
649 $row->{notes}{data} = SL::HTML::Util->strip($ref->{notes});
651 $report->add_data($row);
653 my $next_ref = $form->{parts}[$idx + 1];
655 # insert subtotal rows
656 if (($form->{l_subtotal} eq 'Y') &&
658 (!$next_ref->{assemblyitem} && ($same_item ne $next_ref->{ $form->{sort} })))) {
659 my $row = { map { $_ => { 'class' => 'listsubtotal', } } @columns };
661 if (($form->{searchitems} ne 'assembly') || !$form->{bom}) {
662 $row->{soldtotal}->{data} = $form->format_amount(\%myconfig, $subtotals{soldtotal});
665 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $subtotals{$_}, 2) } @subtotal_columns;
666 map { $subtotals{$_} = 0 } ('soldtotal', @subtotal_columns);
668 $report->add_data($row);
670 $same_item = $next_ref->{ $form->{sort} };
676 if ($form->{"l_linetotal"} && !$form->{report_generator_csv_options_for_import}) {
677 my $row = { map { $_ => { 'class' => 'listtotal', } } @columns };
679 map { $row->{"linetotal$_"}->{data} = $form->format_amount(\%myconfig, $totals{$_}, 2) } @subtotal_columns;
681 $report->add_separator();
682 $report->add_data($row);
685 $report->generate_with_headers();
687 $lxdebug->leave_sub();
688 } #end generate_report
691 $lxdebug->enter_sub();
693 $auth->assert('part_service_assembly_edit');
696 my ($column_index, $subtotalonhand, $subtotalsellprice, $subtotallastcost, $subtotallistprice) = @_;
698 map { $column_data{$_} = "<td> </td>" } @{ $column_index };
699 $$subtotalonhand = 0 if ($form->{searchitems} eq 'assembly' && $form->{bom});
701 $column_data{onhand} =
702 "<th class=listsubtotal align=right>"
703 . $form->format_amount(\%myconfig, $$subtotalonhand)
706 $column_data{linetotalsellprice} =
707 "<th class=listsubtotal align=right>"
708 . $form->format_amount(\%myconfig, $$subtotalsellprice, 2)
710 $column_data{linetotallistprice} =
711 "<th class=listsubtotal align=right>"
712 . $form->format_amount(\%myconfig, $$subtotallistprice, 2)
714 $column_data{linetotallastcost} =
715 "<th class=listsubtotal align=right>"
716 . $form->format_amount(\%myconfig, $$subtotallastcost, 2)
719 $$subtotalonhand = 0;
720 $$subtotalsellprice = 0;
721 $$subtotallistprice = 0;
722 $$subtotallastcost = 0;
724 print "<tr class=listsubtotal>";
726 map { print "\n$column_data{$_}" } @{ $column_index };
732 $lxdebug->leave_sub();
736 $lxdebug->enter_sub();
738 $auth->assert('part_service_assembly_details');
740 # show history button
741 $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
742 #/show hhistory button
743 IC->get_part(\%myconfig, \%$form);
745 $form->{"original_partnumber"} = $form->{"partnumber"};
747 my $title = 'Edit ' . ucfirst $form->{part_type};
748 $form->{title} = $locale->text($title);
753 $lxdebug->leave_sub();
757 $lxdebug->enter_sub();
759 $auth->assert('part_service_assembly_details');
761 IC->create_links("IC", \%myconfig, \%$form);
764 map({ $form->{selectcurrency} .= "<option>$_\n" } $::form->get_all_currencies());
766 # parts and assemblies have the same links
767 my $item = $form->{part_type};
768 if ($form->{part_type} eq 'assembly') {
772 # build the popup menus
773 $form->{taxaccounts} = "";
774 foreach my $key (keys %{ $form->{IC_links} }) {
775 foreach my $ref (@{ $form->{IC_links}{$key} }) {
777 # if this is a tax field
778 if ($key =~ /IC_tax/) {
779 if ($key =~ /\Q$item\E/) {
780 $form->{taxaccounts} .= "$ref->{accno} ";
781 $form->{"IC_tax_$ref->{accno}_description"} =
782 "$ref->{accno}--$ref->{description}";
785 if ($form->{amount}{ $ref->{accno} }) {
786 $form->{"IC_tax_$ref->{accno}"} = "checked";
789 $form->{"IC_tax_$ref->{accno}"} = "checked";
794 $form->{"select$key"} .=
795 "<option $ref->{selected}>$ref->{accno}--$ref->{description}\n";
796 if ($form->{amount}{$key} eq $ref->{accno}) {
797 $form->{$key} = "$ref->{accno}--$ref->{description}";
803 chop $form->{taxaccounts};
805 if (($form->{part_type} eq "part") || ($form->{part_type} eq "assembly")) {
806 $form->{selectIC_income} = $form->{selectIC_sale};
807 $form->{selectIC_expense} = $form->{selectIC_cogs};
808 $form->{IC_income} = $form->{IC_sale};
809 $form->{IC_expense} = $form->{IC_cogs};
812 delete $form->{IC_links};
813 delete $form->{amount};
815 $form->get_partsgroup(\%myconfig, { all => 1 });
817 $form->{partsgroup} = "$form->{partsgroup}--$form->{partsgroup_id}";
819 if (@{ $form->{all_partsgroup} }) {
820 $form->{selectpartsgroup} = qq|<option>\n|;
821 map { $form->{selectpartsgroup} .= qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n| } @{ $form->{all_partsgroup} };
824 if ($form->{part_type} eq 'assembly') {
826 foreach my $i (1 .. $form->{assembly_rows}) {
827 if ($form->{"partsgroup_id_$i"}) {
828 $form->{"partsgroup_$i"} =
829 qq|$form->{"partsgroup_$i"}--$form->{"partsgroup_id_$i"}|;
832 $form->get_partsgroup(\%myconfig);
834 if (@{ $form->{all_partsgroup} }) {
835 $form->{selectassemblypartsgroup} = qq|<option>\n|;
838 $form->{selectassemblypartsgroup} .=
839 qq|<option value="$_->{partsgroup}--$_->{id}">$_->{partsgroup}\n|
840 } @{ $form->{all_partsgroup} };
843 $lxdebug->leave_sub();
847 $lxdebug->enter_sub();
849 $auth->assert('part_service_assembly_details');
851 $form->{pg_keys} = sub { "$_[0]->{partsgroup}--$_[0]->{id}" };
852 $form->{description_area} = ($form->{rows} = $form->numtextrows($form->{description}, 40)) > 1;
853 $form->{notes_rows} = max 4, $form->numtextrows($form->{notes}, 40), $form->numtextrows($form->{formel}, 40);
855 map { $form->{"is_$_"} = ($form->{part_type} eq $_) } qw(part service assembly);
856 map { $form->{$_} =~ s/"/"/g; } qw(unit);
858 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
859 'partsgroup' => 'all_partsgroup',
860 'vendors' => 'ALL_VENDORS',
861 'warehouses' => { 'key' => 'WAREHOUSES',
862 'bins' => 'BINS', });
863 # leerer wert für Lager und Lagerplatz korrekt einstellt
864 # ID 0 sollte in Ordnung sein, da der Zähler sowieso höher ist
865 my $no_default_bin_entry = { 'id' => '0', description => '--', 'BINS' => [ { id => '0', description => ''} ] };
866 push @ { $form->{WAREHOUSES} }, $no_default_bin_entry;
867 if (my $max = scalar @{ $form->{WAREHOUSES} }) {
868 my ($default_warehouse_id, $default_bin_id);
869 if ($form->{action} eq 'add') { # default only for new entries
870 $default_warehouse_id = $::instance_conf->get_warehouse_id;
871 $default_bin_id = $::instance_conf->get_bin_id;
873 $form->{warehouse_id} ||= $default_warehouse_id || $form->{WAREHOUSES}->[$max -1]->{id};
874 $form->{bin_id} ||= $default_bin_id || $form->{WAREHOUSES}->[$max -1]->{BINS}->[0]->{id};
877 $form->{LANGUAGES} = SL::DB::Manager::Language->get_all_sorted;
878 $form->{translations_map} = { map { ($_->{language_id} => $_) } @{ $form->{translations} || [] } };
880 IC->retrieve_buchungsgruppen(\%myconfig, $form);
881 @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
883 if (($form->{partnumber} ne '') && !SL::TransNumber->new(number => $form->{partnumber}, type => $form->{part_type}, id => $form->{id})->is_unique) {
884 flash('info', $::locale->text('This partnumber is not unique. You should change it.'));
887 my $units = AM->retrieve_units(\%myconfig, $form);
888 $form->{ALL_UNITS} = [ map +{ name => $_ }, sort { $units->{$a}{sortkey} <=> $units->{$b}{sortkey} } keys %$units ];
890 $form->{defaults} = AM->get_defaults();
892 $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'IC', 'trans_id' => $form->{id});
894 my ($null, $partsgroup_id) = split /--/, $form->{partsgroup};
896 CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}, show_disabled_message => 1, partsgroup_id => $partsgroup_id)
897 if (scalar @{ $form->{CUSTOM_VARIABLES} });
899 $::request->layout->use_javascript("${_}.js") for qw(ckeditor/ckeditor ckeditor/adapters/jquery kivi.PriceRule);
900 $::request->layout->add_javascripts_inline("\$(function(){kivi.PriceRule.load_price_rules_for_part(@{[ $::form->{id} * 1 ]})});") if $::form->{id};
902 #print $form->parse_html_template('ic/form_header', { ALL_PRICE_FACTORS => $form->{ALL_PRICE_FACTORS},
903 # ALL_UNITS => $form->{ALL_UNITS},
904 # BUCHUNGSGRUPPEN => $form->{BUCHUNGSGRUPPEN},
905 # payment_terms => $form->{payment_terms},
906 # all_partsgroup => $form->{all_partsgroup}});
908 $form->{show_edit_buttons} = $main::auth->check_right($::myconfig{login}, 'part_service_assembly_edit');
910 print $form->parse_html_template('ic/form_header');
911 $lxdebug->leave_sub();
915 $lxdebug->enter_sub();
917 $auth->assert('part_service_assembly_details');
919 print $form->parse_html_template('ic/form_footer');
921 $lxdebug->leave_sub();
925 $lxdebug->enter_sub();
928 my @mm_data = grep { any { $_ ne '' } @$_{qw(make model)} } map +{ make => $form->{"make_$_"}, model => $form->{"model_$_"}, lastcost => $form->{"lastcost_$_"}, lastupdate => $form->{"lastupdate_$_"}, sortorder => $form->{"sortorder_$_"} }, 1 .. $numrows;
929 delete @{$form}{grep { m/^make_\d+/ || m/^model_\d+/ } keys %{ $form }};
930 print $form->parse_html_template('ic/makemodel', { MM_DATA => [ @mm_data, {} ], mm_rows => scalar @mm_data + 1 });
932 $lxdebug->leave_sub();
936 $lxdebug->enter_sub();
939 my ($nochange, $callback, $previousform, $linetotal, $line_purchase_price, $href);
941 @column_index = qw(runningnumber qty unit bom partnumber description partsgroup lastcost total);
943 if ($form->{previousform}) {
945 @column_index = qw(qty unit bom partnumber description partsgroup total);
949 $form->{old_callback} = $form->{callback};
950 $callback = $form->{callback};
951 $form->{callback} = "$form->{script}?action=display_form";
954 map { delete $form->{$_} } qw(action header);
956 # save form variables in a previousform variable
957 my %form_to_save = map { ($_ => m/^ (?: listprice | sellprice | lastcost ) $/x ? $form->format_amount(\%myconfig, $form->{$_}) : $form->{$_}) }
959 $previousform = $::auth->save_form_in_session(form => \%form_to_save);
961 $form->{callback} = $callback;
962 $form->{assemblytotal} = 0;
963 $form->{assembly_purchase_price_total} = 0;
968 runningnumber => { text => $locale->text('No.'), nowrap => 1, width => '5%', align => 'left',},
969 qty => { text => $locale->text('Qty'), nowrap => 1, width => '10%', align => 'left',},
970 unit => { text => $locale->text('Unit'), nowrap => 1, width => '5%', align => 'left',},
971 partnumber => { text => $locale->text('Part Number'), nowrap => 1, width => '20%', align => 'left',},
972 description => { text => $locale->text('Part Description'), nowrap => 1, width => '50%', align => 'left',},
973 lastcost => { text => $locale->text('Purchase Prices'), nowrap => 1, width => '50%', align => 'right',},
974 total => { text => $locale->text('Sale Prices'), nowrap => 1, align => 'right',},
975 bom => { text => $locale->text('BOM'), align => 'center',},
976 partsgroup => { text => $locale->text('Group'), align => 'left',},
981 for my $i (1 .. $numrows) {
982 my (%row, @row_hiddens);
984 $form->{"partnumber_$i"} =~ s/\"/"/g;
986 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
987 $line_purchase_price = $form->round_amount($form->{"lastcost_$i"} * $form->{"qty_$i"} / ($form->{"price_factor_$i"} || 1), 4);
988 $form->{assemblytotal} += $linetotal;
989 $form->{assembly_purchase_price_total} += $line_purchase_price;
990 $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
991 $linetotal = $form->format_amount(\%myconfig, $linetotal, 2);
992 $line_purchase_price = $form->format_amount(\%myconfig, $line_purchase_price, 2);
993 $href = build_std_url("action=edit", qq|id=$form->{"id_$i"}|, "rowcount=$numrows", "currow=$i", "previousform=$previousform");
994 map { $row{$_}{data} = "" } qw(qty unit partnumber description bom partsgroup runningnumber);
997 if (($i >= 1) && ($i == $numrows)) {
998 if (!$form->{previousform}) {
999 $row{partnumber}{data} = qq|<input name="partnumber_$i" size=15 value="$form->{"partnumber_$i"}">|;
1000 $row{qty}{data} = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1001 $row{description}{data} = qq|<input name="description_$i" size=40 value="$form->{"description_$i"}">|;
1002 $row{partsgroup}{data} = qq|<input name="partsgroup_$i" size=10 value="$form->{"partsgroup_$i"}">|;
1006 if ($form->{previousform}) {
1007 push @row_hiddens, qw(qty bom);
1008 $row{partnumber}{data} = $form->{"partnumber_$i"};
1009 $row{qty}{data} = $form->{"qty_$i"};
1010 $row{bom}{data} = $form->{"bom_$i"} ? "x" : " ";
1011 $row{qty}{align} = 'right';
1013 $row{partnumber}{data} = qq|$form->{"partnumber_$i"}|;
1014 $row{partnumber}{link} = $href;
1015 $row{qty}{data} = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
1016 $row{runningnumber}{data} = qq|<input name="runningnumber_$i" size=3 value="$i">|;
1017 $row{bom}{data} = sprintf qq|<input name="bom_$i" type=checkbox class=checkbox value=1 %s>|,
1018 $form->{"bom_$i"} ? 'checked' : '';
1020 push @row_hiddens, qw(unit description partnumber partsgroup);
1021 $row{unit}{data} = $form->{"unit_$i"};
1022 #Bei der Artikelbeschreibung und Warengruppe können Sonderzeichen verwendet
1023 #werden, die den HTML Code stören. Daher sollen diese im Template escaped werden
1024 #dies geschieht, wenn die Variable escape gesetzt ist
1025 $row{description}{data} = $form->{"description_$i"};
1026 $row{description}{escape} = 1;
1027 $row{partsgroup}{data} = $form->{"partsgroup_$i"};
1028 $row{partsgroup}{escape} = 1;
1029 $row{bom}{align} = 'center';
1032 $row{lastcost}{data} = $line_purchase_price;
1033 $row{total}{data} = $linetotal;
1034 $row{lastcost}{align} = 'right';
1035 $row{total}{align} = 'right';
1036 $row{deliverydate}{align} = 'right';
1038 push @row_hiddens, qw(id sellprice lastcost weight price_factor_id price_factor);
1039 $row{hiddens} = [ map +{ name => "${_}_$i", value => $form->{"${_}_$i"} }, @row_hiddens ];
1044 print $form->parse_html_template('ic/assembly_row', { COLUMNS => \@column_index, ROWS => \@ROWS, HEADER => \%header });
1046 $lxdebug->leave_sub();
1050 $lxdebug->enter_sub();
1052 $auth->assert('part_service_assembly_edit');
1054 # update checks whether pricegroups, makemodels or assembly items have been changed/added
1055 # new items might have been added (and the original form might have been stored and restored)
1056 # so at the end the ic form is run through check_form in io.pl
1057 # The various combination of events can lead to problems with the order of parse_amount and format_amount
1058 # Currently check_form parses some variables in assembly mode, but not in article or service mode
1059 # This will only ever really be sanely resolved with a rewrite...
1061 # parse pricegroups. and no, don't rely on check_form for this...
1062 map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
1064 unless ($form->{part_type} eq 'assembly') {
1065 # for assemblies check_form will parse sellprice and listprice, but not for parts or services
1066 $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) for qw(sellprice listprice ve gv);
1069 if ($form->{part_type} eq 'part') {
1070 $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) for qw(weight rop);
1073 # same for makemodel lastcosts
1074 # but parse_amount not necessary for assembly component lastcosts
1075 unless ($form->{part_type} eq "assembly") {
1076 map { $form->{"lastcost_$_"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$_"}) } 1 .. $form->{"makemodel_rows"};
1077 $form->{lastcost} = $form->parse_amount(\%myconfig, $form->{lastcost});
1080 if ($form->{part_type} eq "assembly") {
1081 my $i = $form->{assembly_rows};
1083 # if last row is empty check the form otherwise retrieve item
1084 if ( ($form->{"partnumber_$i"} eq "")
1085 && ($form->{"description_$i"} eq "")
1086 && ($form->{"partsgroup_$i"} eq "")) {
1087 # no new assembly item was added
1092 # search db for newly added assemblyitems, via partnumber or description
1093 IC->assembly_item(\%myconfig, \%$form);
1095 # form->{item_list} contains the possible matches, next check whether the
1096 # match is unique or we need to call the page to select the item
1097 my $rows = scalar @{ $form->{item_list} };
1100 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
1103 $form->{makemodel_rows}--;
1104 select_item(mode => 'IC', pre_entered_qty => $form->parse_amount(\%myconfig, $form->{"qty_$i"}));
1105 $::dispatcher->end_request;
1107 map { $form->{item_list}[$i]{$_} =~ s/\"/"/g }
1108 qw(partnumber description unit partsgroup);
1109 map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} }
1110 keys %{ $form->{item_list}[0] };
1111 $form->{"runningnumber_$i"} = $form->{assembly_rows};
1112 $form->{assembly_rows}++;
1120 $form->{rowcount} = $i;
1121 $form->{assembly_rows}++;
1128 } elsif (($form->{part_type} eq 'part') || ($form->{part_type} eq 'service')) {
1132 $lxdebug->leave_sub();
1136 $lxdebug->enter_sub();
1138 $auth->assert('part_service_assembly_edit');
1139 $::form->mtime_ischanged('parts');
1140 my ($parts_id, %newform, $amount, $callback);
1142 # check if there is a part number - commented out, cause there is an automatic allocation of numbers
1143 # $form->isblank("partnumber", $locale->text(ucfirst $form->{part_type}." Part Number missing!"));
1145 # check if there is a description
1146 $form->isblank("description", $locale->text("Part Description missing!"));
1148 $form->error($locale->text("Inventory quantity must be zero before you can set this $form->{part_type} obsolete!"))
1149 if $form->{obsolete} && $form->{onhand} * 1 && $form->{part_type} ne 'service';
1151 if (!$form->{buchungsgruppen_id}) {
1152 $form->error($locale->text("Parts must have an entry type.") . " " .
1153 $locale->text("If you see this message, you most likely just setup your LX-Office and haven't added any entry types. If this is the case, the option is accessible for administrators in the System menu.")
1157 $form->error($locale->text('Description must not be empty!')) unless $form->{description};
1158 $form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
1160 # undef warehouse_id if the empty value is selected
1161 if ( ($form->{warehouse_id} == 0) && ($form->{bin_id} == 0) ) {
1162 undef $form->{warehouse_id};
1163 undef $form->{bin_id};
1166 if (IC->save(\%myconfig, \%$form) == 3) {
1167 $form->error($locale->text('Partnumber not unique!'));
1169 # saving the history
1170 if(!exists $form->{addition}) {
1171 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1172 $form->{what_done} = "part";
1173 $form->{addition} = "SAVED";
1174 $form->save_history;
1176 # /saving the history
1177 $parts_id = $form->{id};
1180 # load previous variables
1181 if ($form->{previousform}) {
1183 # save the new form variables before splitting previousform
1184 map { $newform{$_} = $form->{$_} } keys %$form;
1186 # don't trample on previous variables
1187 map { delete $form->{$_} } keys %newform;
1189 my $ic_cvar_configs = CVar->get_configs(module => 'IC');
1190 my @ic_cvar_fields = map { "cvar_$_->{name}" } @{ $ic_cvar_configs };
1192 # restore original values
1193 $::auth->restore_form_from_session($newform{previousform}, form => $form);
1194 $form->{taxaccounts} = $newform{taxaccount2};
1196 if ($form->{part_type} eq 'assembly') {
1198 # undo number formatting
1199 map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
1200 qw(weight listprice sellprice rop);
1202 $form->{assembly_rows}--;
1203 if ($newform{currow}) {
1204 $i = $newform{currow};
1206 $i = $form->{assembly_rows};
1208 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"} > 0);
1210 $form->{sellprice} -= $form->{"sellprice_$i"} * $form->{"qty_$i"};
1211 $form->{weight} -= $form->{"weight_$i"} * $form->{"qty_$i"};
1213 # change/add values for assembly item
1214 map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit weight listprice sellprice inventory_accno income_accno expense_accno price_factor_id);
1215 map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1217 # das ist __voll__ bekloppt, dass so auszurechnen jb 22.5.09
1218 #$form->{sellprice} += $form->{"sellprice_$i"} * $form->{"qty_$i"};
1219 $form->{weight} += $form->{"weight_$i"} * $form->{"qty_$i"};
1223 # set values for last invoice/order item
1224 $i = $form->{rowcount};
1225 $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"} > 0);
1227 map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit listprice inventory_accno income_accno expense_accno sellprice lastcost price_factor_id);
1228 map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
1230 $form->{"longdescription_$i"} = $newform{notes};
1232 $form->{"sellprice_$i"} = $newform{lastcost} if ($form->{vendor_id});
1234 if ($form->{exchangerate} != 0) {
1235 $form->{"sellprice_$i"} /= $form->{exchangerate};
1238 map { $form->{"taxaccounts_$i"} .= "$_ " } split / /, $newform{taxaccount};
1239 chop $form->{"taxaccounts_$i"};
1240 foreach my $item (qw(description rate taxnumber)) {
1241 my $index = $form->{"taxaccounts_$i"} . "_$item";
1242 $form->{$index} = $newform{$index};
1245 # credit remaining calculation
1246 $amount = $form->{"sellprice_$i"} * (1 - $form->{"discount_$i"} / 100) * $form->{"qty_$i"};
1248 map { $form->{"${_}_base"} += $amount } (split / /, $form->{"taxaccounts_$i"});
1249 map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{"taxaccounts_$i"} if !$form->{taxincluded};
1251 $form->{creditremaining} -= $amount;
1253 # redo number formatting, because invoice parse them!
1254 map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}) } qw(weight listprice sellprice lastcost rop);
1257 $form->{"id_$i"} = $parts_id;
1259 # Get the actual price factor (not just the ID) for the marge calculation.
1260 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
1261 foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
1262 next if ($pfac->{id} != $newform{price_factor_id});
1263 $form->{"marge_price_factor_$i"} = $pfac->{factor};
1266 delete $form->{ALL_PRICE_FACTORS};
1268 delete $form->{action};
1270 # restore original callback
1271 $callback = $form->unescape($form->{callback});
1272 $form->{callback} = $form->unescape($form->{old_callback});
1273 delete $form->{old_callback};
1275 $form->{makemodel_rows}--;
1277 # put callback together
1278 foreach my $key (keys %$form) {
1280 # do single escape for Apache 2.0
1281 my $value = $form->escape($form->{$key}, 1);
1282 $callback .= qq|&$key=$value|;
1284 $form->{callback} = $callback;
1290 $lxdebug->leave_sub();
1294 $lxdebug->enter_sub();
1296 $auth->assert('part_service_assembly_edit');
1298 # saving the history
1299 if(!exists $form->{addition}) {
1300 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1301 $form->{addition} = "SAVED AS NEW";
1302 $form->{what_done} = "part";
1303 $form->save_history;
1305 # /saving the history
1307 # deleting addition to get the history saved for the new part, too.
1308 delete $form->{addition};
1311 if ($form->{"original_partnumber"} &&
1312 ($form->{"partnumber"} eq $form->{"original_partnumber"})) {
1313 $form->{partnumber} = "";
1316 $lxdebug->leave_sub();
1320 $lxdebug->enter_sub();
1322 $auth->assert('part_service_assembly_edit');
1324 # saving the history
1325 if(!exists $form->{addition}) {
1326 $form->{snumbers} = qq|partnumber_| . $form->{partnumber};
1327 $form->{addition} = "DELETED";
1328 $form->{what_done} = "part";
1329 $form->save_history;
1331 # /saving the history
1332 my $rc = IC->delete(\%myconfig, \%$form);
1335 $form->redirect($locale->text('Item deleted!')) if ($rc > 0);
1336 $form->error($locale->text('Cannot delete item!'));
1338 $lxdebug->leave_sub();
1342 $lxdebug->enter_sub();
1344 $auth->assert('part_service_assembly_details');
1349 pricegroup => $form->{"pricegroup_$_"},
1350 pricegroup_id => $form->{"pricegroup_id_$_"},
1351 price => $form->{"price_$_"},
1354 print $form->parse_html_template('ic/price_row', { PRICES => \@PRICES });
1356 $lxdebug->leave_sub();
1359 sub ajax_autocomplete {
1360 $main::lxdebug->enter_sub();
1362 my $form = $main::form;
1363 my %myconfig = %main::myconfig;
1365 $form->{column} = 'description' unless $form->{column} =~ /^partnumber|description$/;
1366 $form->{$form->{column}} = $form->{q} || '';
1367 $form->{limit} = ($form->{limit} * 1) || 10;
1368 $form->{searchitems} ||= '';
1370 my @results = IC->all_parts(\%myconfig, $form);
1372 print $form->ajax_response_header(),
1373 $form->parse_html_template('ic/ajax_autocomplete');
1375 $main::lxdebug->leave_sub();
1379 $::lxdebug->enter_sub;
1381 $auth->assert('part_service_assembly_edit');
1385 $::form->language_payment(\%::myconfig);
1387 Common::webdav_folder($::form);
1390 price_row($::form->{price_rows});
1391 makemodel_row(++$::form->{makemodel_rows}) if $::form->{part_type} =~ /^(part|service)$/;
1392 assembly_row(++$::form->{assembly_rows}) if $::form->{part_type} eq 'assembly';
1396 $::lxdebug->leave_sub;
1399 sub back_to_record {
1403 delete @{$::form}{qw(action action_add action_back_to_record back_sub description item notes partnumber sellprice taxaccount2 unit vc)};
1405 $::auth->restore_form_from_session($::form->{previousform}, clobber => 1);
1406 $::form->{rowcount}--;
1407 $::form->{action} = 'display_form';
1408 $::form->{callback} = $::form->{script} . '?' . join('&', map { $::form->escape($_) . '=' . $::form->escape($::form->{$_}) } sort keys %{ $::form });
1412 sub continue { call_sub($form->{"nextsub"}); }
1415 my $action = first { $::form->{"action_${_}"} } qw(add back_to_record);
1416 $::form->error($::locale->text('No action defined.')) unless $action;
1418 $::form->{dispatched_action} = $action;