Revision bc8ddcc1
Von Tamino Steinert vor 12 Monaten hinzugefügt
SL/Controller/Part.pm | ||
---|---|---|
93 | 93 |
my ($self, %params) = @_; |
94 | 94 |
|
95 | 95 |
$self->part( SL::DB::Part->new_parent_variant ); |
96 |
$self->part->part_type($::form->{part_type}); |
|
96 | 97 |
$self->add; |
97 | 98 |
} |
98 | 99 |
|
... | ... | |
117 | 118 |
$self->action_add_service if $::form->{part_type} eq 'service'; |
118 | 119 |
$self->action_add_assembly if $::form->{part_type} eq 'assembly'; |
119 | 120 |
$self->action_add_assortment if $::form->{part_type} eq 'assortment'; |
120 |
$self->action_add_parent_variant if $::form->{part_type} eq 'parent_variant'; |
|
121 |
$self->action_add_variant if $::form->{part_type} eq 'variant'; |
|
122 | 121 |
}; |
123 | 122 |
|
124 | 123 |
sub action_save { |
... | ... | |
337 | 336 |
for @variant_property_values_lists; |
338 | 337 |
1; |
339 | 338 |
}) or do { |
340 |
return $self->js->error(t8('Error while creating variants: ' . @_))->render();
|
|
339 |
die t8('Error while creating variants: '), $@;
|
|
341 | 340 |
}; |
342 | 341 |
|
343 | 342 |
$self->redirect_to( |
... | ... | |
447 | 446 |
assembly => t8('Edit Assembly'), |
448 | 447 |
service => t8('Edit Service'), |
449 | 448 |
assortment => t8('Edit Assortment'), |
450 |
parent_variant => t8('Edit Parent Variant'), |
|
451 |
variant => t8('Edit Variant'), |
|
452 | 449 |
); |
450 |
my $title = $title_hash{$self->part->part_type}; |
|
451 |
$title .= |
|
452 |
' (' . SL::Presenter::Part::variant_type_abbreviation($self->part->variant_type) . ')' |
|
453 |
if $self->part->variant_type; |
|
453 | 454 |
|
454 | 455 |
$self->part->prices([]) unless $self->part->prices; |
455 | 456 |
$self->part->translations([]) unless $self->part->translations; |
456 | 457 |
|
457 | 458 |
$self->render( |
458 | 459 |
'part/form', |
459 |
title => $title_hash{$self->part->part_type},
|
|
460 |
title => $title, |
|
460 | 461 |
%assortment_vars, |
461 | 462 |
%assembly_vars, |
462 | 463 |
%parent_variant_vars, |
... | ... | |
1114 | 1115 |
assembly => t8('Add Assembly'), |
1115 | 1116 |
service => t8('Add Service'), |
1116 | 1117 |
assortment => t8('Add Assortment'), |
1117 |
parent_variant => t8('Add Parent Variant'), |
|
1118 |
variant => t8('Add Variant'), |
|
1119 | 1118 |
); |
1119 |
my $title = $title_hash{$self->part->part_type}; |
|
1120 |
$title .= |
|
1121 |
' (' . SL::Presenter::Part::variant_type_abbreviation($self->part->variant_type) . ')' |
|
1122 |
if $self->part->variant_type; |
|
1120 | 1123 |
|
1121 | 1124 |
$self->render( |
1122 | 1125 |
'part/form', |
1123 |
title => $title_hash{$self->part->part_type},
|
|
1126 |
title => $title, |
|
1124 | 1127 |
); |
1125 | 1128 |
} |
1126 | 1129 |
|
... | ... | |
1742 | 1745 |
} |
1743 | 1746 |
|
1744 | 1747 |
sub check_has_valid_part_type { |
1745 |
die "invalid part_type" unless $_[0] =~ /^(part|service|assembly|assortment|parent_variant|variant)$/;
|
|
1748 |
Carp::confess "invalid part_type" unless $_[0] =~ /^(part|service|assembly|assortment)$/;
|
|
1746 | 1749 |
} |
1747 | 1750 |
|
1748 | 1751 |
|
SL/DB/Helper/TransNumberGenerator.pm | ||
---|---|---|
47 | 47 |
service => { number_column => 'partnumber', number_range_column => 'servicenumber', }, |
48 | 48 |
assembly => { number_column => 'partnumber', number_range_column => 'assemblynumber', }, |
49 | 49 |
assortment => { number_column => 'partnumber', number_range_column => 'assortmentnumber', }, |
50 |
parent_variant => { number_column => 'partnumber', number_range_column => 'parent_variant_number', }, |
|
51 |
variant => { number_column => 'partnumber', number_range_column => 'variant_number', }, |
|
52 | 50 |
); |
53 | 51 |
|
54 | 52 |
sub get_next_trans_number { |
SL/DB/MetaSetup/Default.pm | ||
---|---|---|
145 | 145 |
order_warn_no_cusordnumber => { type => 'boolean', default => 'false' }, |
146 | 146 |
order_warn_no_deliverydate => { type => 'boolean', default => 'true' }, |
147 | 147 |
p_reclamation_record_number => { type => 'text', default => '0', not_null => 1 }, |
148 |
parent_variant_number => { type => 'text' }, |
|
149 | 148 |
parts_image_css => { type => 'text', default => 'border:0;float:left;max-width:250px;margin-top:20px:margin-right:10px;margin-left:10px;' }, |
150 | 149 |
parts_listing_image => { type => 'boolean', default => 'true' }, |
151 | 150 |
parts_show_image => { type => 'boolean', default => 'true' }, |
... | ... | |
222 | 221 |
transit_items_chart_id => { type => 'integer' }, |
223 | 222 |
transport_cost_reminder_article_number_id => { type => 'integer' }, |
224 | 223 |
undo_transfer_interval => { type => 'integer', default => 7 }, |
225 |
variant_number => { type => 'text' }, |
|
226 | 224 |
vc_greetings_use_textfield => { type => 'boolean' }, |
227 | 225 |
vendor_ustid_taxnummer_unique => { type => 'boolean', default => 'false' }, |
228 | 226 |
vendornumber => { type => 'text' }, |
SL/DB/MetaSetup/Part.pm | ||
---|---|---|
32 | 32 |
obsolete => { type => 'boolean', default => 'false', not_null => 1 }, |
33 | 33 |
onhand => { type => 'numeric', default => '0', precision => 25, scale => 5 }, |
34 | 34 |
order_qty => { type => 'numeric', default => '0', not_null => 1, precision => 15, scale => 5 }, |
35 |
part_type => { type => 'enum', check_in => [ 'part', 'service', 'assembly', 'assortment', 'parent_variant', 'variant' ], db_type => 'part_type_enum', not_null => 1 },
|
|
35 |
part_type => { type => 'enum', check_in => [ 'part', 'service', 'assembly', 'assortment' ], db_type => 'part_type_enum', not_null => 1 }, |
|
36 | 36 |
partnumber => { type => 'text', not_null => 1 }, |
37 | 37 |
partsgroup_id => { type => 'integer' }, |
38 | 38 |
payment_id => { type => 'integer' }, |
... | ... | |
44 | 44 |
stockable => { type => 'boolean', default => 'false' }, |
45 | 45 |
tariff_code => { type => 'text' }, |
46 | 46 |
unit => { type => 'varchar', length => 20, not_null => 1 }, |
47 |
variant_type => { type => 'enum', check_in => [ 'single', 'parent_variant', 'variant' ], db_type => 'part_variant_type', default => 'single', not_null => 1 }, |
|
47 | 48 |
ve => { type => 'integer' }, |
48 | 49 |
warehouse_id => { type => 'integer' }, |
49 | 50 |
weight => { type => 'float', precision => 4, scale => 4 }, |
SL/DB/MetaSetup/VariantPropertyValue.pm | ||
---|---|---|
13 | 13 |
id => { type => 'serial', not_null => 1 }, |
14 | 14 |
itime => { type => 'timestamp', default => 'now()' }, |
15 | 15 |
mtime => { type => 'timestamp' }, |
16 |
sortkey => { type => 'integer', not_null => 1 },
|
|
16 |
sortkey => { type => 'integer' }, |
|
17 | 17 |
value => { type => 'text', not_null => 1 }, |
18 | 18 |
variant_property_id => { type => 'integer', not_null => 1 }, |
19 | 19 |
); |
SL/DB/Part.pm | ||
---|---|---|
4 | 4 |
|
5 | 5 |
use Carp; |
6 | 6 |
use List::MoreUtils qw(any uniq); |
7 |
use List::Util qw(sum); |
|
7 |
use List::Util qw(sum max);
|
|
8 | 8 |
use Rose::DB::Object::Helpers qw(as_tree); |
9 | 9 |
|
10 | 10 |
use SL::Locale::String qw(t8); |
... | ... | |
215 | 215 |
sub is_assembly { $_[0]->part_type eq 'assembly' } |
216 | 216 |
sub is_service { $_[0]->part_type eq 'service' } |
217 | 217 |
sub is_assortment { $_[0]->part_type eq 'assortment' } |
218 |
sub is_parent_variant { $_[0]->part_type eq 'parent_variant' } |
|
219 |
sub is_variant { $_[0]->part_type eq 'variant' } |
|
218 |
|
|
219 |
sub is_parent_variant { $_[0]->variant_type eq 'parent_variant' } |
|
220 |
sub is_variant { $_[0]->variant_type eq 'variant' } |
|
220 | 221 |
|
221 | 222 |
sub type { return $_[0]->part_type; } |
222 | 223 |
|
... | ... | |
242 | 243 |
|
243 | 244 |
sub new_parent_variant { |
244 | 245 |
my ($class, %params) = @_; |
245 |
$class->new(%params, part_type => 'parent_variant');
|
|
246 |
$class->new(%params, variant_type => 'parent_variant');
|
|
246 | 247 |
} |
247 | 248 |
|
248 | 249 |
sub last_modification { |
... | ... | |
512 | 513 |
die "Given variant_property_values dosn't match the variant_properties of parent_variant part"; |
513 | 514 |
} |
514 | 515 |
|
516 |
my $separator = '.'; # TODO: make configurable |
|
517 |
|
|
518 |
my $last_variant_number = |
|
519 |
max |
|
520 |
map { |
|
521 |
my $partnumber = $_->partnumber; |
|
522 |
$partnumber =~ s/.*\Q$separator\E([0-9]*)/$1/; # escape chars between \Q \E |
|
523 |
$partnumber; |
|
524 |
} |
|
525 |
$self->variants; |
|
526 |
|
|
515 | 527 |
my $new_variant = $self->clone_and_reset; |
516 |
# TODO set partnumber |
|
517 |
$new_variant->part_type('variant'); |
|
518 |
$new_variant->save; |
|
519 |
SL::DB::VariantPropertyValuePart->new( |
|
520 |
part_id => $new_variant->id, |
|
521 |
variant_property_value_id => $_->id, |
|
522 |
)->save for @$variant_property_values; |
|
523 |
SL::DB::PartParentVariantPartVariant->new( |
|
524 |
variant_id => $new_variant->id, |
|
525 |
parent_variant_id => $self->id, |
|
526 |
)->save; |
|
528 |
$new_variant->partnumber($self->partnumber . $separator . ($last_variant_number + 1)); |
|
529 |
$new_variant->variant_type('variant'); |
|
530 |
$new_variant->add_assemblies(map {$_->clone_and_reset} $self->assemblies) if ($self->is_assembly); |
|
531 |
$new_variant->add_variant_property_values(@$variant_property_values); |
|
527 | 532 |
|
533 |
$self->add_variants($new_variant); |
|
534 |
$self->save; |
|
528 | 535 |
return $new_variant; |
529 | 536 |
} |
530 | 537 |
|
... | ... | |
644 | 651 |
|
645 | 652 |
sub variant_value { |
646 | 653 |
my ($self, $variant_property) = @_; |
654 |
die "only callable on parts of type parent_variant" unless $self->is_variant; |
|
655 |
die "only callable with SL::DB::VariantProperty object" unless ref $variant_property eq 'SL::DB::VariantProperty'; |
|
647 | 656 |
|
648 | 657 |
my %property_id_to_values = |
649 | 658 |
map {$_->variant_property_id => $_} |
SL/IC.pm | ||
---|---|---|
319 | 319 |
$form->{l_assembly} = 1 if $form->{searchitems} eq 'assembly' || $form->{searchitems} eq ''; |
320 | 320 |
$form->{l_part} = 1 if $form->{searchitems} eq 'part' || $form->{searchitems} eq ''; |
321 | 321 |
$form->{l_assortment} = 1 if $form->{searchitems} eq 'assortment' || $form->{searchitems} eq ''; |
322 |
$form->{l_parent_variant} = 1 if $form->{searchitems} eq 'parent_variant' || $form->{searchitems} eq ''; |
|
323 |
$form->{l_variant} = 1 if $form->{searchitems} eq 'variant' || $form->{searchitems} eq ''; |
|
324 | 322 |
push @where_tokens, "p.partnumber ILIKE ? OR p.description ILIKE ?"; |
325 | 323 |
push @bind_vars, (like($form->{all})) x 2; |
326 | 324 |
} |
... | ... | |
372 | 370 |
# Oder Bedingungen fuer Ware Dienstleistung Erzeugnis: |
373 | 371 |
if ( $form->{l_part} || $form->{l_assembly} |
374 | 372 |
|| $form->{l_service} || $form->{l_assortment} |
375 |
|| $form->{l_parent_variant} || $form->{l_variant} |
|
376 | 373 |
) { |
377 | 374 |
my @or_tokens = (); |
378 | 375 |
push @or_tokens, "p.part_type = 'service'" if $form->{l_service}; |
379 | 376 |
push @or_tokens, "p.part_type = 'assembly'" if $form->{l_assembly}; |
380 | 377 |
push @or_tokens, "p.part_type = 'part'" if $form->{l_part}; |
381 | 378 |
push @or_tokens, "p.part_type = 'assortment'" if $form->{l_assortment}; |
382 |
push @or_tokens, "p.part_type = 'parent_variant'" if $form->{l_parent_variant}; |
|
383 |
push @or_tokens, "p.part_type = 'variant'" if $form->{l_variant}; |
|
384 | 379 |
push @where_tokens, join ' OR ', map { "($_)" } @or_tokens; |
385 | 380 |
} |
386 | 381 |
else { |
SL/Presenter/Part.pm | ||
---|---|---|
69 | 69 |
assembly => t8('Assembly (typeabbreviation)'), |
70 | 70 |
assortment => t8('Assortment (typeabbreviation)'), |
71 | 71 |
service => t8('Service (typeabbreviation)'), |
72 |
); |
|
73 |
|
|
74 |
return $part_type_abbr{$part_type} || ''; |
|
75 |
} |
|
76 |
|
|
77 |
sub variant_type_abbreviation { |
|
78 |
my ($variant_type) = @_; |
|
79 |
|
|
80 |
my %variant_type_abbr = ( |
|
72 | 81 |
parent_variant => t8('Parent Variant (typeabbreviation)'), |
73 | 82 |
variant => t8('Variant (typeabbreviation)'), |
74 | 83 |
); |
75 | 84 |
|
76 |
return $part_type_abbr{$part_type} || '';
|
|
85 |
return $variant_type_abbr{$variant_type} || '';
|
|
77 | 86 |
} |
78 | 87 |
|
79 | 88 |
# |
SL/TransNumber.pm | ||
---|---|---|
92 | 92 |
$filters{table} = "oe"; |
93 | 93 |
$filters{where} = 'COALESCE(quotation, FALSE) AND (vendor_id IS NOT NULL)'; |
94 | 94 |
|
95 |
} elsif ($type =~ /^(part|service|assembly|assortment|parent_variant|variant)$/) {
|
|
95 |
} elsif ($type =~ /^(part|service|assembly|assortment)$/) { |
|
96 | 96 |
$filters{trans_number} = "partnumber"; |
97 | 97 |
my %numberfield_hash = ( service => 'servicenumber', |
98 | 98 |
assembly => 'assemblynumber', |
99 | 99 |
assortment => 'assortmentnumber', |
100 |
parent_variant => 'parent_variant_number', |
|
101 |
variant => 'variant_number', |
|
102 | 100 |
part => 'articlenumber' |
103 | 101 |
); |
104 | 102 |
$filters{numberfield} = $numberfield_hash{$type}; |
menus/user/00-erp.yaml | ||
---|---|---|
84 | 84 |
name: Add Parent Variant |
85 | 85 |
order: 575 |
86 | 86 |
access: part_service_assembly_edit |
87 |
- parent: master_data_add_parent_variant |
|
88 |
id: master_data_add_parent_variant_part |
|
89 |
name: Add Parent Variant Part |
|
90 |
order: 100 |
|
91 |
params: |
|
92 |
action: Part/add_parent_variant |
|
93 |
part_type: part |
|
94 |
- parent: master_data_add_parent_variant |
|
95 |
id: master_data_add_parent_variant_service |
|
96 |
name: Add Parent Variant Service |
|
97 |
order: 200 |
|
98 |
params: |
|
99 |
action: Part/add_parent_variant |
|
100 |
part_type: service |
|
101 |
- parent: master_data_add_parent_variant |
|
102 |
id: master_data_add_parent_variant_assembly |
|
103 |
name: Add Parent Variant Assembly |
|
104 |
order: 300 |
|
87 | 105 |
params: |
88 | 106 |
action: Part/add_parent_variant |
107 |
part_type: assembly |
|
89 | 108 |
- parent: master_data |
90 | 109 |
id: master_data_add_project |
91 | 110 |
name: Add Project |
sql/Pg-upgrade2/add_variants.sql | ||
---|---|---|
3 | 3 |
-- @depends: release_3_8_0 |
4 | 4 |
-- @ignore: 0 |
5 | 5 |
|
6 |
ALTER TYPE part_type_enum ADD VALUE 'parent_variant'; |
|
7 |
ALTER TYPE part_type_enum ADD VALUE 'variant'; |
|
8 |
|
|
9 |
ALTER TABLE defaults ADD parent_variant_number TEXT; |
|
10 |
ALTER TABLE defaults ADD variant_number TEXT; |
|
6 |
CREATE TYPE part_variant_type AS ENUM ('single', 'parent_variant', 'variant'); |
|
7 |
ALTER TABLE parts ADD COLUMN variant_type part_variant_type DEFAULT 'single' NOT NULL; |
|
11 | 8 |
|
12 | 9 |
CREATE TABLE parts_parent_variant_id_parts_variant_id ( |
13 | 10 |
parent_variant_id INTEGER NOT NULL REFERENCES parts(id), |
... | ... | |
20 | 17 |
name TEXT NOT NULL, |
21 | 18 |
unique_name TEXT NOT NULL UNIQUE, |
22 | 19 |
abbreviation VARCHAR(4) NOT NULL, |
20 |
sortkey INTEGER, |
|
23 | 21 |
itime TIMESTAMP DEFAULT now(), |
24 | 22 |
mtime TIMESTAMP |
25 | 23 |
); |
... | ... | |
50 | 48 |
variant_property_id INTEGER NOT NULL REFERENCES variant_properties(id), |
51 | 49 |
value TEXT NOT NULL, |
52 | 50 |
abbreviation VARCHAR(4) NOT NULL, |
53 |
sortkey INTEGER NOT NULL,
|
|
51 |
sortkey INTEGER, |
|
54 | 52 |
itime TIMESTAMP DEFAULT now(), |
55 | 53 |
mtime TIMESTAMP |
56 | 54 |
); |
sql/Pg-upgrade2/variant_properties_add_column_sortkey.sql | ||
---|---|---|
1 |
-- @tag: variants_add_column_sortkey |
|
2 |
-- @description: Sortierung für Varianteneigenschaften |
|
3 |
-- @depends: add_variants |
|
4 |
-- @ignore: 0 |
|
5 |
|
|
6 |
ALTER TABLE variant_properties ADD COLUMN sortkey INTEGER; |
sql/Pg-upgrade2/variant_property_values_alter_column_sortkey.sql | ||
---|---|---|
1 |
-- @tag: variant_values_alter_column_sortkey |
|
2 |
-- @description: sortkey für Varianteneigenschaftswert darf NULL sein |
|
3 |
-- @depends: add_variants |
|
4 |
-- @ignore: 0 |
|
5 |
|
|
6 |
ALTER TABLE variant_property_values ALTER COLUMN sortkey DROP NOT NULL; |
templates/design40_webpages/client_config/_ranges_of_numbers.html | ||
---|---|---|
104 | 104 |
<td>[% L.input_tag("defaults.assortmentnumber", SELF.defaults.assortmentnumber, size="15", class="wi-normal") %]</td> |
105 | 105 |
[% END %] |
106 | 106 |
</tr> |
107 |
<tr> |
|
108 |
<th>[% LxERP.t8('Parent Variant') %]</th> |
|
109 |
<td>[% L.input_tag("defaults.parent_variant_number", SELF.defaults.parent_variant_number, size="15", class="wi-normal") %]</td> |
|
110 |
</tr> |
|
111 |
<tr> |
|
112 |
<th>[% LxERP.t8('Variant') %]</th> |
|
113 |
<td>[% L.input_tag("defaults.variant_number", SELF.defaults.variant_number, size="15", class="wi-normal") %]</td> |
|
114 |
</tr> |
|
115 | 107 |
</tbody> |
116 | 108 |
</table> |
117 | 109 |
|
templates/design40_webpages/ic/search.html | ||
---|---|---|
36 | 36 |
<input name="l_assortment" id="l_assortment" type="checkbox" value="Y" checked> |
37 | 37 |
<label for="l_assortment"> [% 'Assortment' | $T8 %] </label> |
38 | 38 |
[% END %] |
39 |
<input name="l_parent_variant" id="l_parent_variant" type="checkbox" value="Y" checked> |
|
40 |
<label for="l_parent_variant"> [% 'Parent Variant' | $T8 %] </label> |
|
41 |
<input name="l_variant" id="l_variant" type="checkbox" value="Y" checked> |
|
42 |
<label for="l_variant"> [% 'Variant' | $T8 %] </label> |
|
43 | 39 |
</td> |
44 | 40 |
</tr> |
45 | 41 |
<tr> |
templates/design40_webpages/part/form.html | ||
---|---|---|
10 | 10 |
|
11 | 11 |
[% INCLUDE 'common/flash.html' %] |
12 | 12 |
|
13 |
[% L.hidden_tag('part.part_type' , SELF.part.part_type) %] |
|
14 | 13 |
[% L.hidden_tag('part.id' , SELF.part.id) %] |
14 |
[% L.hidden_tag('part.part_type' , SELF.part.part_type) %] |
|
15 |
[% L.hidden_tag('part.variant_type', SELF.part.variant_type) %] |
|
15 | 16 |
[% L.hidden_tag('last_modification', SELF.part.last_modification) %] |
16 | 17 |
[% L.hidden_tag('callback' , FORM.callback) %] |
17 | 18 |
|
templates/webpages/client_config/_ranges_of_numbers.html | ||
---|---|---|
72 | 72 |
<tr> |
73 | 73 |
<td align="right" nowrap>[% LxERP.t8('Last Purchase Delivery Order Number') %]</td> |
74 | 74 |
<td>[% L.input_tag("defaults.pdonumber", SELF.defaults.pdonumber, size="15") %]</td> |
75 |
<td align="right" nowrap>[% LxERP.t8('Last Parent Variant Number') %]</td> |
|
76 |
<td>[% L.input_tag("defaults.parent_variant_number", SELF.defaults.parent_variant_number, size="15") %]</td> |
|
77 | 75 |
</tr> |
78 | 76 |
|
79 | 77 |
<tr> |
80 | 78 |
<td align="right" nowrap>[% LxERP.t8('Last Supplier Delivery Order Number') %]</td> |
81 | 79 |
<td>[% L.input_tag("defaults.sudonumber", SELF.defaults.sudonumber, size="15") %]</td> |
82 |
<td align="right" nowrap>[% LxERP.t8('Last Variant Number') %]</td> |
|
83 |
<td>[% L.input_tag("defaults.variant_number", SELF.defaults.variant_number, size="15") %]</td> |
|
84 | 80 |
</tr> |
85 | 81 |
|
86 | 82 |
<tr> |
templates/webpages/part/form.html | ||
---|---|---|
9 | 9 |
|
10 | 10 |
<form method="post" id="ic" name="ic" action="controller.pl"> |
11 | 11 |
|
12 |
[% L.hidden_tag('part.part_type' , SELF.part.part_type) %] |
|
13 | 12 |
[% L.hidden_tag('part.id' , SELF.part.id) %] |
13 |
[% L.hidden_tag('part.part_type' , SELF.part.part_type) %] |
|
14 |
[% L.hidden_tag('part.variant_type', SELF.part.variant_type) %] |
|
14 | 15 |
[% L.hidden_tag('last_modification', SELF.part.last_modification) %] |
15 | 16 |
[% L.hidden_tag('callback' , FORM.callback) %] |
16 | 17 |
|
Auch abrufbar als: Unified diff
Varianten: alle Artikeltypen ermöglichen