33 |
33 |
#######################################################################
|
34 |
34 |
|
35 |
35 |
use Carp;
|
36 |
|
use List::Util qw(first);
|
|
36 |
use List::Util qw(any first none);
|
37 |
37 |
use POSIX qw(strftime);
|
38 |
38 |
use Scalar::Util qw(blessed);
|
39 |
39 |
|
... | ... | |
43 |
43 |
use SL::AM;
|
44 |
44 |
use SL::CVar;
|
45 |
45 |
use SL::CT;
|
46 |
|
use SL::Helper::Flash qw(flash_later);
|
|
46 |
use SL::Helper::Flash qw(flash flash_later);
|
47 |
47 |
use SL::IC;
|
48 |
48 |
use SL::WH;
|
49 |
49 |
use SL::OE;
|
50 |
|
use SL::Helper::Inventory qw(produce_assembly);
|
|
50 |
use SL::Helper::Inventory qw(get_stock produce_assembly allocate_for_assembly check_allocations_for_assembly);
|
|
51 |
use SL::Helper::Inventory::Allocation;
|
51 |
52 |
use SL::Locale::String qw(t8);
|
52 |
53 |
use SL::ReportGenerator;
|
53 |
54 |
use SL::Presenter::Tag qw(checkbox_tag);
|
... | ... | |
398 |
399 |
$form->{bestbefore} = '';
|
399 |
400 |
}
|
400 |
401 |
|
|
402 |
# Check if there are more than one chargenumber for one part of an assembly.
|
|
403 |
# In this case let the user select the wanted chargenumbers.
|
|
404 |
my $stocked_wh_id = $::instance_conf->get_produce_assembly_same_warehouse ? $form->{warehouse_id} : undef;
|
|
405 |
my @stocked_by = qw(part warehouse bin chargenumber); # Or 'for_allocate'? That would include 'bestbefore'.
|
|
406 |
my $stocked_parts = get_stock(part => [ map { $_->part } @{$assembly->assemblies} ],
|
|
407 |
warehouse => $stocked_wh_id,
|
|
408 |
by => \@stocked_by,
|
|
409 |
with_objects => [qw(part warehouse bin)]);
|
|
410 |
|
|
411 |
# Remove entries with no stock.
|
|
412 |
$stocked_parts = [ grep { $_->{qty} != 0 } @$stocked_parts];
|
|
413 |
|
|
414 |
my %stocked_by_parts_id = map { my $p_id = $_->{parts_id }; $p_id => [grep { $_->{parts_id} == $p_id } @$stocked_parts] } @$stocked_parts;
|
|
415 |
|
|
416 |
my $is_stock_ambiguous = any { scalar(@{$_ || []}) > 1 } values %stocked_by_parts_id;
|
|
417 |
my $allocation_check_failed;
|
|
418 |
my $stock_check_failed;
|
|
419 |
|
|
420 |
my @allocations;
|
|
421 |
if ($is_stock_ambiguous) {
|
|
422 |
if (!scalar @{$form->{allocations} || []}) {
|
|
423 |
# User has not selected something / first_time.
|
|
424 |
# Pre-fill allocations for selection form.
|
|
425 |
eval {
|
|
426 |
@allocations = allocate_for_assembly(part => $assembly,
|
|
427 |
qty => $form->{qty},
|
|
428 |
warehouse => ($form->{warehouse_id} ? SL::DB::Warehouse->new(id => $form->{warehouse_id})->load : undef),
|
|
429 |
chargenumber => $form->{chargenumber},);
|
|
430 |
1;
|
|
431 |
} or do {
|
|
432 |
my $ex = $@;
|
|
433 |
die $ex unless blessed($ex) && $ex->isa('SL::X::Inventory::Allocation::Multi');
|
|
434 |
|
|
435 |
$form->{title} = $locale->text('Allocate for Assembly');
|
|
436 |
$form->header;
|
|
437 |
print $form->parse_html_template(
|
|
438 |
'wh/produce_assembly_error',
|
|
439 |
{
|
|
440 |
missing_qty_exceptions => [ grep { $_->isa('SL::X::Inventory::Allocation::MissingQty') } @{ $ex->errors } ],
|
|
441 |
other_exceptions => [ grep { !$_->isa('SL::X::Inventory::Allocation::MissingQty') } @{ $ex->errors } ],
|
|
442 |
});
|
|
443 |
|
|
444 |
return $::lxdebug->leave_sub();
|
|
445 |
};
|
|
446 |
|
|
447 |
} else {
|
|
448 |
# User has selected something / selection form was sent.
|
|
449 |
# Check allocations.
|
|
450 |
|
|
451 |
# Create allocation helper objects from form.
|
|
452 |
@allocations = map {
|
|
453 |
SL::Helper::Inventory::Allocation->new(
|
|
454 |
parts_id => $_->{parts_id},
|
|
455 |
qty => $form->parse_amount(\%myconfig, $_->{qty}),
|
|
456 |
bin_id => $_->{bin_id},
|
|
457 |
warehouse_id => $_->{warehouse_id},
|
|
458 |
chargenumber => $_->{chargenumber},
|
|
459 |
bestbefore => $_->{bestbefore},
|
|
460 |
comment => $_->{comment},
|
|
461 |
for_object_id => undef,
|
|
462 |
)
|
|
463 |
} grep { $form->parse_amount(\%myconfig, $_->{qty}) != 0 } @{$form->{allocations}};
|
|
464 |
|
|
465 |
$allocation_check_failed = !check_allocations_for_assembly(part => $assembly,
|
|
466 |
qty => $form->{qty},
|
|
467 |
allocations => \@allocations,
|
|
468 |
check_overfulfilment => 1);
|
|
469 |
|
|
470 |
if (!$allocation_check_failed) {
|
|
471 |
# Check, if all allocations are stocked.
|
|
472 |
foreach my $allocation (@allocations) {
|
|
473 |
$stock_check_failed = none {
|
|
474 |
$_->{parts_id} == $allocation->parts_id &&
|
|
475 |
$_->{warehouse_id} == $allocation->warehouse_id &&
|
|
476 |
$_->{bin_id} == $allocation->bin_id &&
|
|
477 |
$_->{chargenumber} eq $allocation->chargenumber &&
|
|
478 |
$_->{qty} >= $allocation->qty
|
|
479 |
} @$stocked_parts;
|
|
480 |
|
|
481 |
last if $stock_check_failed;
|
|
482 |
}
|
|
483 |
}
|
|
484 |
}
|
|
485 |
|
|
486 |
# Show selection form if requested or check was not ok. Continue to production otherwise.
|
|
487 |
if ($form->{show_allocations} || $allocation_check_failed || $stock_check_failed) {
|
|
488 |
flash('warning', t8("Allocations are not sufficient or overfulfilled")) if ($allocation_check_failed);
|
|
489 |
flash('warning', t8("Not enough on stock for one or more allocations")) if ($stock_check_failed);
|
|
490 |
|
|
491 |
my %allocations_by_parts_id = map { my $p_id = $_->{parts_id}; $p_id => [grep { $_->{parts_id} == $p_id } @allocations] } @allocations;
|
|
492 |
|
|
493 |
my %needed_by_parts_id = map { $_->{parts_id} => $_->qty * $form->{qty} } @{$assembly->assemblies};
|
|
494 |
return create_assembly_chargenumbers($form, \%stocked_by_parts_id, \%needed_by_parts_id, \%allocations_by_parts_id);
|
|
495 |
}
|
|
496 |
|
|
497 |
}
|
|
498 |
|
401 |
499 |
eval {
|
402 |
500 |
produce_assembly(
|
403 |
501 |
part => $assembly, # target assembly
|
404 |
502 |
qty => $form->{qty}, # qty
|
405 |
|
auto_allocate => 1,
|
|
503 |
allocations => ($is_stock_ambiguous ? \@allocations : undef),
|
|
504 |
auto_allocate => !$is_stock_ambiguous,
|
406 |
505 |
bin => $bin, # needed unless a global standard target is configured
|
407 |
506 |
chargenumber => $form->{chargenumber}, # optional
|
408 |
507 |
bestbefore => $form->{bestbefore},
|
... | ... | |
436 |
535 |
$main::lxdebug->leave_sub();
|
437 |
536 |
}
|
438 |
537 |
|
|
538 |
sub create_assembly_chargenumbers {
|
|
539 |
my ($form, $stocked_by_parts_id, $needed_by_parts_id, $allocated_by_parts_id) = @_;
|
|
540 |
|
|
541 |
setup_wh_create_assembly_chargenumbers_action_bar();
|
|
542 |
|
|
543 |
my $hidden_vars = { map { $_ => $form->{$_} } qw(parts_id warehouse_id bin_id chargenumber qty unit comment) };
|
|
544 |
|
|
545 |
$form->{title} = $::locale->text('Select Chargenumbers');
|
|
546 |
$form->header;
|
|
547 |
|
|
548 |
print $form->parse_html_template(
|
|
549 |
'wh/create_assembly_chargenumbers',
|
|
550 |
{
|
|
551 |
hidden_vars => $hidden_vars,
|
|
552 |
stocked_by_parts_id => $stocked_by_parts_id,
|
|
553 |
needed_by_parts_id => $needed_by_parts_id,
|
|
554 |
allocated_by_parts_id => $allocated_by_parts_id,
|
|
555 |
}
|
|
556 |
);
|
|
557 |
}
|
|
558 |
|
439 |
559 |
# --------------------------------------------------------------------
|
440 |
560 |
# Transfer: removal
|
441 |
561 |
# --------------------------------------------------------------------
|
... | ... | |
1200 |
1320 |
submit => [ '#form', { action => 'transfer_assembly_update_part' } ],
|
1201 |
1321 |
accesskey => 'enter',
|
1202 |
1322 |
],
|
|
1323 |
action => [
|
|
1324 |
t8('Produce'),
|
|
1325 |
submit => [ '#form', { action => 'create_assembly', show_allocations => 1 } ],
|
|
1326 |
disabled => $::form->{parts_id} ? undef : $::locale->text('No assembly has been selected yet.'),
|
|
1327 |
],
|
|
1328 |
);
|
|
1329 |
}
|
|
1330 |
}
|
|
1331 |
|
|
1332 |
sub setup_wh_create_assembly_chargenumbers_action_bar {
|
|
1333 |
my ($action) = @_;
|
|
1334 |
|
|
1335 |
for my $bar ($::request->layout->get('actionbar')) {
|
|
1336 |
$bar->add(
|
|
1337 |
action => [
|
|
1338 |
t8('Update'),
|
|
1339 |
submit => [ '#form', { action => 'create_assembly', show_allocations => 1 } ],
|
|
1340 |
accesskey => 'enter',
|
|
1341 |
],
|
1203 |
1342 |
action => [
|
1204 |
1343 |
t8('Produce'),
|
1205 |
1344 |
submit => [ '#form', { action => 'create_assembly' } ],
|
Erzeugnis fertigen: Chargenauswahl