Revision 706e28ed
Von Tamino Steinert vor 10 Monaten hinzugefügt
SL/Controller/Part.pm | ||
---|---|---|
10 | 10 |
use File::Temp; |
11 | 11 |
use List::Util qw(sum); |
12 | 12 |
use List::UtilsBy qw(extract_by); |
13 |
use List::MoreUtils qw(any pairwise); |
|
13 | 14 |
use POSIX qw(strftime); |
14 | 15 |
use Text::CSV_XS; |
15 | 16 |
|
... | ... | |
294 | 295 |
|
295 | 296 |
my $part = $self->part; |
296 | 297 |
my $variant_properties = $part->variant_properties(); |
297 |
my @needed_variant_property_ids = sort map {$_->id} @$variant_properties; |
|
298 | 298 |
|
299 |
if (@variant_property_ids != @needed_variant_property_ids) { |
|
299 |
if (any {$_} grep { |
|
300 |
!defined $::form->{variant_properties}->{$_->id}->{selected_property_value_ids} |
|
301 |
} @$variant_properties |
|
302 |
) { |
|
300 | 303 |
return $self->js->error( |
301 | 304 |
t8('No property value selected for variant properties: #1.', |
302 | 305 |
join(", ", |
303 | 306 |
map {$_->displayable_name} |
304 |
grep {!defined $::form->{variant_properties}->{$_->id}->{selected_property_values}} |
|
307 |
grep {!defined $::form->{variant_properties}->{$_->id}->{selected_property_value_ids}}
|
|
305 | 308 |
@$variant_properties |
306 | 309 |
) |
307 | 310 |
) |
... | ... | |
311 | 314 |
my @grouped_variant_property_values = (); |
312 | 315 |
push @grouped_variant_property_values, |
313 | 316 |
SL::DB::Manager::VariantPropertyValue->get_all( |
314 |
where => [ id => $::form->{variant_properties}->{$_}->{selected_property_values} ] |
|
317 |
where => [ id => $::form->{variant_properties}->{$_}->{selected_property_value_ids} ]
|
|
315 | 318 |
) |
316 | 319 |
for @variant_property_ids; |
317 | 320 |
|
318 | 321 |
|
319 |
my @variant_property_values_lists = ();
|
|
322 |
my @variant_property_value_lists = (); |
|
320 | 323 |
foreach my $variant_property_values (@grouped_variant_property_values) { |
321 | 324 |
my @new_lists = (); |
322 | 325 |
foreach my $variant_property_value (@$variant_property_values) { |
323 |
unless (scalar @variant_property_values_lists) {
|
|
326 |
unless (scalar @variant_property_value_lists) { |
|
324 | 327 |
push @new_lists, [$variant_property_value]; |
325 | 328 |
} |
326 |
foreach my $variant_property_values_list (@variant_property_values_lists) {
|
|
329 |
foreach my $variant_property_values_list (@variant_property_value_lists) { |
|
327 | 330 |
push @new_lists, [@$variant_property_values_list, $variant_property_value]; |
328 | 331 |
} |
329 | 332 |
} |
330 |
@variant_property_values_lists = @new_lists;
|
|
333 |
@variant_property_value_lists = @new_lists; |
|
331 | 334 |
} |
332 | 335 |
|
333 |
my @new_variants = ();
|
|
336 |
_check_variant_property_values_not_taken($part, \@variant_property_value_lists);
|
|
334 | 337 |
SL::DB->client->with_transaction(sub { |
335 |
push @new_variants, $part->create_new_variant($_) |
|
336 |
for @variant_property_values_lists; |
|
338 |
$part->create_new_variant($_) for @variant_property_value_lists; |
|
337 | 339 |
1; |
338 | 340 |
}) or do { |
339 |
die t8('Error while creating variants: '), $@; |
|
341 |
die t8('Error while creating variants: '), SL::DB->client->error; |
|
342 |
}; |
|
343 |
|
|
344 |
$self->redirect_to( |
|
345 |
controller => 'Part', |
|
346 |
action => 'edit', |
|
347 |
'part.id' => $self->part->id |
|
348 |
); |
|
349 |
} |
|
350 |
|
|
351 |
sub action_convert_part_to_variant { |
|
352 |
my ($self) = @_; |
|
353 |
|
|
354 |
my $convert_part_id = $::form->{convert_part}->{id}; |
|
355 |
die t8("Please select a part to convert.") unless $convert_part_id; |
|
356 |
|
|
357 |
my @variant_property_ids = sort keys %{$::form->{convert_part}->{variant_properties}}; |
|
358 |
|
|
359 |
my $part = $self->part; |
|
360 |
my $variant_properties = $part->variant_properties(); |
|
361 |
my @needed_variant_property_ids = sort map {$_->id} @$variant_properties; |
|
362 |
|
|
363 |
if (@variant_property_ids != @needed_variant_property_ids) { |
|
364 |
return $self->js->error( |
|
365 |
t8('No property value selected for variant properties: #1.', |
|
366 |
join(", ", |
|
367 |
map {$_->displayable_name} |
|
368 |
grep {!defined $::form->{convert_part}->{variant_properties}->{$_->id}->{selected_property_value_id}} |
|
369 |
@$variant_properties |
|
370 |
) |
|
371 |
) |
|
372 |
)->render(); |
|
373 |
} |
|
374 |
|
|
375 |
my @variant_property_values = map { |
|
376 |
SL::DB::VariantPropertyValue->new( |
|
377 |
id => $::form->{convert_part}->{variant_properties}->{$_}->{selected_property_value_id} |
|
378 |
)->load() |
|
379 |
} @variant_property_ids; |
|
380 |
|
|
381 |
_check_variant_property_values_not_taken($part, [\@variant_property_values]); |
|
382 |
SL::DB->client->with_transaction(sub { |
|
383 |
my $part_to_convert = SL::DB::Part->new(id => $convert_part_id)->load; |
|
384 |
$part_to_convert->variant_type('variant'); |
|
385 |
$part_to_convert->variant_property_values(\@variant_property_values); |
|
386 |
$part_to_convert->parent_variant($part); |
|
387 |
$part_to_convert->save; |
|
388 |
1; |
|
389 |
}) or do { |
|
390 |
die t8('Error while converting part to variant: '), SL::DB->Client->error; |
|
340 | 391 |
}; |
341 | 392 |
|
342 | 393 |
$self->redirect_to( |
... | ... | |
1748 | 1799 |
Carp::confess "invalid part_type" unless $_[0] =~ /^(part|service|assembly|assortment)$/; |
1749 | 1800 |
} |
1750 | 1801 |
|
1802 |
sub _check_variant_property_values_not_taken { |
|
1803 |
my ($parent_variant, $variant_property_value_lists) = @_; |
|
1804 |
|
|
1805 |
my @double_lists; |
|
1806 |
my $variants = $parent_variant->variants; |
|
1807 |
foreach my $variant (@$variants) { |
|
1808 |
my @property_value_ids = sort map {$_->id} $variant->variant_property_values; |
|
1809 |
foreach my $test_property_values (@$variant_property_value_lists) { |
|
1810 |
my @test_property_value_ids = sort map {$_->id} @$test_property_values; |
|
1811 |
if (@test_property_value_ids == @property_value_ids |
|
1812 |
&& not any {$_} pairwise {$a != $b} @test_property_value_ids, @property_value_ids |
|
1813 |
) { |
|
1814 |
push @double_lists, join(', ', map {$_->displayable_name} @$test_property_values); |
|
1815 |
} |
|
1816 |
} |
|
1817 |
} |
|
1818 |
|
|
1819 |
if (@double_lists) { |
|
1820 |
die t8("There is already a variant with the property values: #1.", join("; ", @double_lists)); |
|
1821 |
} |
|
1822 |
} |
|
1751 | 1823 |
|
1752 | 1824 |
sub normalize_text_blocks { |
1753 | 1825 |
my ($self) = @_; |
SL/DB/Part.pm | ||
---|---|---|
3 | 3 |
use strict; |
4 | 4 |
|
5 | 5 |
use Carp; |
6 |
use List::MoreUtils qw(any uniq); |
|
6 |
use List::MoreUtils qw(any uniq pairwise);
|
|
7 | 7 |
use List::Util qw(sum max); |
8 | 8 |
use Rose::DB::Object::Helpers qw(as_tree); |
9 | 9 |
|
... | ... | |
133 | 133 |
|
134 | 134 |
__PACKAGE__->before_save('_before_save_set_partnumber'); |
135 | 135 |
__PACKAGE__->before_save('_before_save_set_assembly_weight'); |
136 |
__PACKAGE__->before_save('_before_check_variant_property_values'); |
|
136 | 137 |
|
137 | 138 |
sub _before_save_set_partnumber { |
138 | 139 |
my ($self) = @_; |
... | ... | |
151 | 152 |
return 1; |
152 | 153 |
} |
153 | 154 |
|
155 |
sub _before_check_variant_property_values { |
|
156 |
my ($self) = @_; |
|
157 |
if ($self->is_variant) { |
|
158 |
my @property_value_ids = sort map {$_->id} $self->variant_property_values; |
|
159 |
my ($parent_variant) = $self->parent_variant; |
|
160 |
my $other_variants = $parent_variant->variants; |
|
161 |
foreach my $variant (@$other_variants) { |
|
162 |
next if $variant->id == $self->id; |
|
163 |
my @other_property_value_ids = sort map {$_->id} $variant->variant_property_values; |
|
164 |
if (@other_property_value_ids == @property_value_ids |
|
165 |
&& not any {$_} pairwise {$a != $b} @other_property_value_ids, @property_value_ids |
|
166 |
) { |
|
167 |
die t8("There is already a variant with the property values: "), |
|
168 |
join(' ,', map {$_->displayable_name} $self->variant_property_values); |
|
169 |
} |
|
170 |
} |
|
171 |
} |
|
172 |
return 1; |
|
173 |
} |
|
174 |
|
|
154 | 175 |
sub items { |
155 | 176 |
my ($self) = @_; |
156 | 177 |
|
... | ... | |
514 | 535 |
die "Given variant_property_values dosn't match the variant_properties of parent_variant part"; |
515 | 536 |
} |
516 | 537 |
|
517 |
my $separator = '.'; # TODO: make configurable |
|
538 |
my $new_variant = $self->clone_and_reset; |
|
539 |
$new_variant->partnumber($self->_next_variant_partnumber); |
|
540 |
$new_variant->variant_type('variant'); |
|
541 |
$new_variant->add_assemblies(map {$_->clone_and_reset} $self->assemblies) if ($self->is_assembly); |
|
542 |
$new_variant->add_variant_property_values(@$variant_property_values); |
|
543 |
$new_variant->parent_variant($self); |
|
544 |
$new_variant->save; |
|
518 | 545 |
|
546 |
$self->add_variants($new_variant); |
|
547 |
return $new_variant; |
|
548 |
} |
|
549 |
|
|
550 |
sub _next_variant_partnumber { |
|
551 |
my ($self) = @_; |
|
552 |
die "only callable on parts of type parent_variant" unless $self->is_parent_variant; |
|
553 |
|
|
554 |
my $separator = '.'; # TODO: make configurable |
|
519 | 555 |
my $last_variant_number = |
520 | 556 |
max |
521 | 557 |
map { |
... | ... | |
525 | 561 |
} |
526 | 562 |
$self->variants; |
527 | 563 |
|
528 |
my $new_variant = $self->clone_and_reset; |
|
529 |
$new_variant->partnumber($self->partnumber . $separator . ($last_variant_number + 1)); |
|
530 |
$new_variant->variant_type('variant'); |
|
531 |
$new_variant->add_assemblies(map {$_->clone_and_reset} $self->assemblies) if ($self->is_assembly); |
|
532 |
$new_variant->add_variant_property_values(@$variant_property_values); |
|
533 |
|
|
534 |
$self->add_variants($new_variant); |
|
535 |
$self->save; |
|
536 |
return $new_variant; |
|
564 |
return $self->partnumber . $separator . ($last_variant_number + 1); |
|
537 | 565 |
} |
538 | 566 |
|
539 | 567 |
sub clone_and_reset_deep { |
SL/Presenter/Part.pm | ||
---|---|---|
294 | 294 |
for autocompletion. You may comma separate multiple types as in |
295 | 295 |
C<part,assembly>. |
296 | 296 |
|
297 |
If C<%params> contains C<variant_type> only parts of this variant type will be |
|
298 |
used for autocompletion. You may comma separate multiple variant types as in |
|
299 |
C<single,variant>. |
|
300 |
|
|
297 | 301 |
If C<%params> contains C<status> only parts of this status will be used |
298 | 302 |
for autocompletion. C<status> can be one of the following strings: |
299 | 303 |
C<active>, C<obsolete> or C<all>. C<active> is the default if C<status> is |
js/kivi.Part.js | ||
---|---|---|
337 | 337 |
$.post("controller.pl", data, kivi.eval_json_result); |
338 | 338 |
}; |
339 | 339 |
|
340 |
ns.convert_part_to_variant = function() { |
|
341 |
var data = $('#ic').serializeArray(); |
|
342 |
data.push({ name: 'action', value: 'Part/convert_part_to_variant' }); |
|
343 |
$.post("controller.pl", data, kivi.eval_json_result); |
|
344 |
}; |
|
345 |
|
|
340 | 346 |
ns.update_variant_property_value_options = function() { |
341 | 347 |
var data = $('#ic').serializeArray(); |
342 | 348 |
data.push({ name: 'action', value: 'Part/update_variant_property_value_options' }); |
... | ... | |
403 | 409 |
if (this.o.part_type) |
404 | 410 |
data['filter.part_type'] = this.o.part_type.split(','); |
405 | 411 |
|
412 |
if (this.o.variant_type) |
|
413 |
data['filter.variant_type'] = this.o.variant_type.split(','); |
|
414 |
|
|
406 | 415 |
if (this.o.status) { |
407 | 416 |
if (this.o.status == 'active') data['filter.obsolete'] = 0; |
408 | 417 |
if (this.o.status == 'obsolete') data['filter.obsolete'] = 1; |
templates/design40_webpages/part/_parent_variant.html | ||
---|---|---|
53 | 53 |
<div class="wrapper"> |
54 | 54 |
[% FOREACH variant_property = SELF.part.variant_properties %] |
55 | 55 |
<div class="col input-panel" style="min-width:fit-content;"> |
56 |
<h4>[% variant_property.name_translated | html %]</h4>
|
|
57 |
[% L.select_tag("variant_properties." _ variant_property.id _ ".selected_property_values[]", |
|
56 |
<h4>[% variant_property.displayable_name | html %]</h4>
|
|
57 |
[% L.select_tag("variant_properties." _ variant_property.id _ ".selected_property_value_ids[]",
|
|
58 | 58 |
variant_property.property_values, |
59 | 59 |
title_key='displayable_name', value_key='id', |
60 |
id="selected_property_values_" _ variant_property.id, |
|
60 |
id="selected_property_value_ids_" _ variant_property.id,
|
|
61 | 61 |
multiple=1, |
62 | 62 |
) %] |
63 | 63 |
[% L.multiselect2side( |
64 |
"selected_property_values_" _ variant_property.id, |
|
64 |
"selected_property_value_ids_" _ variant_property.id,
|
|
65 | 65 |
labelsx=LxERP.t8("All Property Values"), |
66 | 66 |
labeldx=LxERP.t8("Selected Property Values") |
67 | 67 |
) %] |
... | ... | |
70 | 70 |
</div> |
71 | 71 |
[% L.button_tag('kivi.Part.create_variants();', LxERP.t8("Create Variants with selected Values")) %] |
72 | 72 |
</div> |
73 |
|
|
74 |
<div class="wrapper input-panel"> |
|
75 |
<h3> [% LxERP.t8("Convert part to Variant") %] </h3> |
|
76 |
<table class="tbl-list"> |
|
77 |
<caption> |
|
78 |
[% 'Variant Properties' | $T8 %] |
|
79 |
</caption> |
|
80 |
<thead> |
|
81 |
<tr> |
|
82 |
<th>[% 'Name' | $T8 %]</th> |
|
83 |
<th>[% 'Value' | $T8 %]</th> |
|
84 |
</tr> |
|
85 |
</thead> |
|
86 |
<tbody> |
|
87 |
[% FOREACH variant_property = SELF.part.variant_properties %] |
|
88 |
<tr> |
|
89 |
<td>[% variant_property.displayable_name | html %]</td> |
|
90 |
<td> |
|
91 |
[% L.select_tag("convert_part.variant_properties." _ variant_property.id _ ".selected_property_value_id", |
|
92 |
variant_property.property_values, |
|
93 |
title_key='displayable_name', value_key='id', |
|
94 |
size=variant_property.property_values.size, |
|
95 |
) %] |
|
96 |
</tr> |
|
97 |
[% END %] |
|
98 |
</tbody> |
|
99 |
</table> |
|
100 |
<label for="convert_part.id">[% 'Part to convert:' | $T8 %]</label> |
|
101 |
[% P.part.picker('convert_part.id', undef, |
|
102 |
part_type=SELF.part.part_type, variant_type='single', |
|
103 |
placeholder=(LxERP.t8('Type:') _ ' ' _ LxERP.t8(SELF.part.part_type)), |
|
104 |
class="wi-wide" |
|
105 |
) %] |
|
106 |
<br> |
|
107 |
<br> |
|
108 |
[% L.button_tag('kivi.Part.convert_part_to_variant();', LxERP.t8("Convert")) %] |
|
109 |
</div> |
|
73 | 110 |
</div> |
74 | 111 |
|
75 | 112 |
|
Auch abrufbar als: Unified diff
Varianten: einfachen Artikel in Variante umwandeln