Revision 98404f3e
Von Tamino Steinert vor 8 Monaten hinzugefügt
SL/Controller/Part.pm | ||
---|---|---|
33 | 33 |
use SL::JSON; |
34 | 34 |
use SL::Locale::String qw(t8); |
35 | 35 |
use SL::MoreCommon qw(save_form); |
36 |
use SL::Presenter; |
|
36 | 37 |
use SL::Presenter::EscapedText qw(escape is_escaped); |
37 | 38 |
use SL::Presenter::Part; |
38 | 39 |
use SL::Presenter::Tag qw(select_tag); |
... | ... | |
732 | 733 |
} |
733 | 734 |
} |
734 | 735 |
|
736 |
sub action_show_multi_variants_dialog { |
|
737 |
my ($self) = @_; |
|
738 |
|
|
739 |
$self->render('part/_multi_variants_dialog', { layout => 0 }); |
|
740 |
} |
|
741 |
|
|
742 |
sub action_multi_variants_update_result { |
|
743 |
my ($self) = @_; |
|
744 |
my $max_count = $::form->{limit}; |
|
745 |
|
|
746 |
my $parent_variant_id = $::form->{multi_items}->{filter}->{parent_variant_id}; |
|
747 |
my $parent_variant; |
|
748 |
$parent_variant = SL::DB::Manager::Part->find_by( |
|
749 |
id => $parent_variant_id |
|
750 |
) if $parent_variant_id ne ''; |
|
751 |
return $self->js->flash('error', t8('No parent variant selected.'))->render |
|
752 |
unless $parent_variant; |
|
753 |
|
|
754 |
|
|
755 |
if ($::form->{old_parent_variant_id} ne $parent_variant_id) { |
|
756 |
# update parent_variant properties |
|
757 |
my $properties_table = SL::Presenter->get->render( |
|
758 |
'part/_multi_variants_parent_properties_table', |
|
759 |
PROPERTIES => \@{$parent_variant->variant_properties} |
|
760 |
); |
|
761 |
$::form->{multi_items}->{filter}->{'has_variant_property_value_id'} = []; |
|
762 |
$self->js->html('#multi_variants_parent_variant_properties', $properties_table); |
|
763 |
$self->js->val('#old_parent_variant_id', $parent_variant_id); |
|
764 |
} |
|
765 |
|
|
766 |
my $count = $self->multi_items_models->count; |
|
767 |
|
|
768 |
my $result; |
|
769 |
if ($count == 0) { |
|
770 |
my $text = escape($::locale->text('No results.')); |
|
771 |
$result = $text; |
|
772 |
} elsif ($max_count && $count > $max_count) { |
|
773 |
my $text = escape($::locale->text('Too many results (#1 from #2).', $count, $max_count)); |
|
774 |
$result = $text; |
|
775 |
} else { |
|
776 |
my $multi_variants = SL::DB::Manager::Part->sort_variants( |
|
777 |
$self->multi_items_models->get |
|
778 |
); |
|
779 |
$result = SL::Presenter->get->render('part/_multi_variants_result', multi_variants => $multi_variants); |
|
780 |
} |
|
781 |
|
|
782 |
$self->js->html('#multi_items_result', $result)->render; |
|
783 |
return; |
|
784 |
} |
|
785 |
|
|
735 | 786 |
sub action_add_makemodel_row { |
736 | 787 |
my ($self) = @_; |
737 | 788 |
|
... | ... | |
1732 | 1783 |
SL::Controller::Helper::GetModels->new( |
1733 | 1784 |
controller => $_[0], |
1734 | 1785 |
model => 'Part', |
1735 |
with_objects => [ qw(unit_obj partsgroup classification) ], |
|
1786 |
with_objects => [ qw(unit_obj partsgroup classification parent_variants variant_property_values variant_property_values.variant_property) ],
|
|
1736 | 1787 |
disable_plugin => 'paginated', |
1737 | 1788 |
source => $::form->{multi_items}, |
1738 | 1789 |
sorted => { |
SL/DB/Manager/Part.pm | ||
---|---|---|
34 | 34 |
return or => [ map { $prefix . $_ => $value } qw(partnumber description ean customerprices.customer_partnumber) ], |
35 | 35 |
$prefix . 'customerprices'; |
36 | 36 |
}, |
37 |
has_variant_property_value_id => sub { |
|
38 |
my ($key, $value, $prefix) = @_; |
|
39 |
|
|
40 |
my @values = grep {$_} |
|
41 |
ref $value eq 'ARRAY' ? @{$value} : split(/\s+/, $value); |
|
42 |
return unless scalar @values; |
|
43 |
|
|
44 |
my $where = join(' or ', ("val.id = ?") x @values) || '1=1'; |
|
45 |
my $query = <<SQL; |
|
46 |
SELECT part_id from ( |
|
47 |
SELECT |
|
48 |
t1.part_id, COUNT(*) as count_hits |
|
49 |
FROM |
|
50 |
variant_property_values val |
|
51 |
JOIN variant_property_values_parts t1 ON (t1.variant_property_value_id = val.id) |
|
52 |
WHERE |
|
53 |
$where |
|
54 |
GROUP BY t1.part_id |
|
55 |
) as tmp |
|
56 |
WHERE count_hits >= ?; |
|
57 |
SQL |
|
58 |
|
|
59 |
push @values, scalar @values; # count_hits |
|
60 |
|
|
61 |
my @part_ids = |
|
62 |
map {$_->{part_id}} |
|
63 |
selectall_hashref_query($::form, $::form->get_standard_dbh, $query, @values); |
|
64 |
|
|
65 |
return id => scalar @part_ids ? \@part_ids : (-1); # empty list not allowed |
|
66 |
}, |
|
37 | 67 |
# all_with_variants => sub { |
38 | 68 |
all => sub { |
39 | 69 |
my ($key, $value, $prefix) = @_; |
... | ... | |
221 | 251 |
return $open_qty |
222 | 252 |
} |
223 | 253 |
|
254 |
sub sort_variants { |
|
255 |
my ($self, $variants) = @_; |
|
256 |
|
|
257 |
my @sorted_variants = |
|
258 |
map { $_->{variant} } |
|
259 |
sort { $a->{sortkey} cmp $b->{sortkey} } |
|
260 |
map { { |
|
261 |
variant => $_, |
|
262 |
sortkey => join('', map { |
|
263 |
(10000 + $_->variant_property->sortkey ) . (10000 + $_->sortkey) |
|
264 |
} @{$_->variant_property_values} |
|
265 |
), |
|
266 |
|
|
267 |
} } |
|
268 |
@$variants; |
|
269 |
return \@sorted_variants; |
|
270 |
} |
|
271 |
|
|
224 | 272 |
sub _sort_spec { |
225 | 273 |
( |
226 | 274 |
default => [ 'partnumber', 1 ], |
js/kivi.Part.js | ||
---|---|---|
453 | 453 |
this.last_dummy = this.$dummy.val(); |
454 | 454 |
this.timer = undefined; |
455 | 455 |
this.dialog = undefined; |
456 |
// for different popups on same page |
|
456 | 457 |
this.multiple_default = this.o.multiple; |
458 |
this.variants_list_default = this.o.variants_list; |
|
457 | 459 |
|
458 | 460 |
this.init(); |
459 | 461 |
}; |
... | ... | |
625 | 627 |
} |
626 | 628 |
}, |
627 | 629 |
open_dialog: function() { |
628 |
if (this.o.multiple) { |
|
630 |
if (this.o.variants_list) { |
|
631 |
this.o.variants_list = this.variants_list_default; |
|
632 |
this.dialog = new ns.PickerMultiVariantPopup(this); |
|
633 |
} else if (this.o.multiple) { |
|
629 | 634 |
this.o.multiple = this.multiple_default; |
630 | 635 |
this.dialog = new ns.PickerMultiPopup(this); |
631 | 636 |
} else { |
... | ... | |
906 | 911 |
} |
907 | 912 |
}; |
908 | 913 |
|
914 |
ns.PickerMultiVariantPopup = function(pp) { |
|
915 |
this.pp = pp; |
|
916 |
this.open_dialog(); |
|
917 |
}; |
|
918 |
|
|
919 |
ns.PickerMultiVariantPopup.prototype = { |
|
920 |
open_dialog: function() { |
|
921 |
var self = this; |
|
922 |
$('#row_table_id thead a img').remove(); |
|
923 |
|
|
924 |
kivi.popup_dialog({ |
|
925 |
url: 'controller.pl?action=Part/show_multi_variants_dialog', |
|
926 |
data: $.extend({ |
|
927 |
real_id: self.pp.real_id, |
|
928 |
show_pos_input: self.pp.o.multiple_pos_input, |
|
929 |
}, self.pp.ajax_data(this.pp.$dummy.val())), |
|
930 |
id: 'jq_multi_variants_dialog', |
|
931 |
dialog: { |
|
932 |
title: kivi.t8('Add multiple variants'), |
|
933 |
width: 800, |
|
934 |
height: 800 |
|
935 |
}, |
|
936 |
load: function() { |
|
937 |
self.init_search(); |
|
938 |
} |
|
939 |
}); |
|
940 |
return true; |
|
941 |
}, |
|
942 |
init_search: function() { |
|
943 |
|
|
944 |
var self = this; |
|
945 |
$('#multi_items_filter_table select').keydown(function(event) { |
|
946 |
if(event.which == KEY.ENTER) { |
|
947 |
event.preventDefault(); |
|
948 |
self.update_results(); |
|
949 |
return false; |
|
950 |
} |
|
951 |
}); |
|
952 |
|
|
953 |
// reset picker for parent_variant |
|
954 |
kivi.run_once_for('input.part_autocomplete', 'part_picker', function(elt) { |
|
955 |
if (!$(elt).data('part_picker')) |
|
956 |
$(elt).data('part_picker', new kivi.Part.Picker($(elt))); |
|
957 |
}); |
|
958 |
$('#multi_items_filter_parent_variant_id_name').focus(); |
|
959 |
$('#multi_items_filter_button').click(function(){ self.update_results(); }); |
|
960 |
$('#multi_items_filter_reset').click(function(){ |
|
961 |
$("#multi_items_form").resetForm(); |
|
962 |
$("#multi_variants_parent_variant_properties").html(''); |
|
963 |
$("#old_parent_variant_id").val(''); |
|
964 |
$("#multi_items_result").html(''); |
|
965 |
}); |
|
966 |
$('#continue_button').click(function(){ self.add_multi_items(); }); |
|
967 |
}, |
|
968 |
update_results: function() { |
|
969 |
var self = this; |
|
970 |
var data = $('#multi_items_form').serializeArray(); |
|
971 |
data.push({ name: 'action', value: 'Part/multi_variants_update_result' }); |
|
972 |
data.push({ name: 'type', value: self.pp.type }); |
|
973 |
data.push({ name: 'limit', value: self.pp.o.multiple_limit }); |
|
974 |
var ppdata = self.pp.ajax_data(function(){ |
|
975 |
var val = $('#multi_items_filter').val(); |
|
976 |
return val === undefined ? '' : val; |
|
977 |
}); |
|
978 |
$.each(Object.keys(ppdata), function() {data.push({ name: 'multi_items.' + this, value: ppdata[this]});}); |
|
979 |
|
|
980 |
$.post( |
|
981 |
"controller.pl", |
|
982 |
data, |
|
983 |
function(data){ |
|
984 |
kivi.eval_json_result(data); |
|
985 |
self.init_results(); |
|
986 |
self.enable_continue(); |
|
987 |
} |
|
988 |
); |
|
989 |
}, |
|
990 |
set_qty_to_one: function(clicked) { |
|
991 |
if ($(clicked).val() === '') { |
|
992 |
$(clicked).val(kivi.format_amount(1.00, -2)); |
|
993 |
} |
|
994 |
$(clicked).select(); |
|
995 |
}, |
|
996 |
init_results: function() { |
|
997 |
var self = this; |
|
998 |
$('#multi_items_all_qty').change(function(event){ |
|
999 |
$('.multi_items_qty').val($(event.target).val()); |
|
1000 |
}); |
|
1001 |
$('.multi_items_qty').focus(function(){ self.set_qty_to_one(this); }); |
|
1002 |
}, |
|
1003 |
result_timer: function() { |
|
1004 |
}, |
|
1005 |
close_dialog: function() { |
|
1006 |
$('#jq_multi_variants_dialog').dialog('close'); |
|
1007 |
}, |
|
1008 |
disable_continue: function() { |
|
1009 |
$('#multi_items_result input, #multi_items_position').off("keydown"); |
|
1010 |
$('#continue_button').prop('disabled', true); |
|
1011 |
}, |
|
1012 |
enable_continue: function() { |
|
1013 |
var self = this; |
|
1014 |
$('#multi_items_result input, #multi_items_position').keydown(function(event) { |
|
1015 |
if(event.keyCode == KEY.ENTER) { |
|
1016 |
event.preventDefault(); |
|
1017 |
self.add_multi_items(); |
|
1018 |
return false; |
|
1019 |
} |
|
1020 |
}); |
|
1021 |
$('#continue_button').prop('disabled', false); |
|
1022 |
}, |
|
1023 |
add_multi_items: function() { |
|
1024 |
// rows at all |
|
1025 |
var n_rows = $('.multi_items_qty').length; |
|
1026 |
if ( n_rows === 0) { return; } |
|
1027 |
|
|
1028 |
// filled rows |
|
1029 |
n_rows = $('.multi_items_qty').filter(function() { |
|
1030 |
return $(this).val().length > 0; |
|
1031 |
}).length; |
|
1032 |
if (n_rows === 0) { return; } |
|
1033 |
|
|
1034 |
this.disable_continue(); |
|
1035 |
|
|
1036 |
var data = $('#multi_items_form').serializeArray(); |
|
1037 |
this.pp.set_multi_items(data); |
|
1038 |
} |
|
1039 |
}; |
|
1040 |
|
|
909 | 1041 |
ns.reinit_widgets = function() { |
910 | 1042 |
kivi.run_once_for('input.part_autocomplete', 'part_picker', function(elt) { |
911 | 1043 |
if (!$(elt).data('part_picker')) |
js/locale/de.js | ||
---|---|---|
4 | 4 |
"Add function block":"Funktionsblock hinzufügen", |
5 | 5 |
"Add linked record":"Verknüpften Beleg hinzufügen", |
6 | 6 |
"Add multiple items":"Mehrere Artikel hinzufügen", |
7 |
"Add multiple variants":"Mehrere Varianten hinzufügen", |
|
7 | 8 |
"Add note":"Notiz erfassen", |
8 | 9 |
"Add picture":"Bild hinzufügen", |
9 | 10 |
"Add picture to text block":"Bild dem Textblock hinzufügen", |
js/locale/en.js | ||
---|---|---|
4 | 4 |
"Add function block":"", |
5 | 5 |
"Add linked record":"", |
6 | 6 |
"Add multiple items":"", |
7 |
"Add multiple variants":"", |
|
7 | 8 |
"Add note":"", |
8 | 9 |
"Add picture":"", |
9 | 10 |
"Add picture to text block":"", |
locale/de/all | ||
---|---|---|
269 | 269 |
'Add linked record' => 'Verknüpften Beleg hinzufügen', |
270 | 270 |
'Add links' => 'Verknüpfungen hinzufügen', |
271 | 271 |
'Add multiple items' => 'Mehrere Artikel hinzufügen', |
272 |
'Add multiple variants' => 'Mehrere Varianten hinzufügen', |
|
272 | 273 |
'Add new currency' => 'Neue Währung hinzufügen', |
273 | 274 |
'Add new custom variable' => 'Neue benutzerdefinierte Variable erfassen', |
274 | 275 |
'Add new price rule item' => 'Neue Bedingung hinzufügen', |
... | ... | |
2565 | 2566 |
'No internal phone extensions have been configured yet.' => 'Es wurden noch keine internen Durchwahlen konfiguriert.', |
2566 | 2567 |
'No invoice email found.' => 'Keine Rechnungsmailadresse gefunden.', |
2567 | 2568 |
'No invoices have been selected.' => 'Es wurden keine Rechnungen ausgewählt.', |
2569 |
'No parent variant selected.' => 'Kein Stammartikel ausgewählt.', |
|
2568 | 2570 |
'No part was selected.' => 'Es wurde kein Artikel ausgewählt.', |
2569 | 2571 |
'No parts selected.' => 'Es wurden keine Artikel ausgewählt.', |
2570 | 2572 |
'No partsgroup selected.' => 'Es wurde keine Warengruppen ausgewählt.', |
locale/en/all | ||
---|---|---|
269 | 269 |
'Add linked record' => '', |
270 | 270 |
'Add links' => '', |
271 | 271 |
'Add multiple items' => '', |
272 |
'Add multiple variants' => '', |
|
272 | 273 |
'Add new currency' => '', |
273 | 274 |
'Add new custom variable' => '', |
274 | 275 |
'Add new price rule item' => '', |
... | ... | |
2564 | 2565 |
'No internal phone extensions have been configured yet.' => '', |
2565 | 2566 |
'No invoice email found.' => '', |
2566 | 2567 |
'No invoices have been selected.' => '', |
2568 |
'No parent variant selected.' => '', |
|
2567 | 2569 |
'No part was selected.' => '', |
2568 | 2570 |
'No parts selected.' => '', |
2569 | 2571 |
'No partsgroup selected.' => '', |
templates/design40_webpages/part/_multi_variants_dialog.html | ||
---|---|---|
1 |
[% USE T8 %] |
|
2 |
[% USE HTML %] |
|
3 |
[% USE L %] |
|
4 |
[% USE P %] |
|
5 |
[% USE LxERP %] |
|
6 |
|
|
7 |
<form method="post" id="multi_items_form" method="POST"> |
|
8 |
|
|
9 |
<div class="buttons"> |
|
10 |
[% L.button_tag('', LxERP.t8('Filter'), id='multi_items_filter_button') %] |
|
11 |
[% L.button_tag('', LxERP.t8('Reset'), id='multi_items_filter_reset') %] |
|
12 |
</div> |
|
13 |
|
|
14 |
|
|
15 |
<div class="select-item control-panel"> |
|
16 |
<table id="multi_items_filter_table" class="tbl-plain"> |
|
17 |
<colgroup> |
|
18 |
<col class="wi-wide"> |
|
19 |
<col class="wi-wide"> |
|
20 |
</colgroup> |
|
21 |
<tr> |
|
22 |
<th> |
|
23 |
[% LxERP.t8("Description") %]/[% LxERP.t8("Partnumber") %] |
|
24 |
</th> |
|
25 |
<td> |
|
26 |
[% L.hidden_tag("old_parent_variant_id", '' ) %] |
|
27 |
[% P.part.picker( |
|
28 |
'multi_items.filter.parent_variant_id', '', |
|
29 |
variant_type="parent_variant", |
|
30 |
onchange='$("#multi_items_filter_button").click();', |
|
31 |
class="wi-wide" |
|
32 |
) %] |
|
33 |
</td> |
|
34 |
</tr> |
|
35 |
</table> |
|
36 |
|
|
37 |
<div id="multi_variants_parent_variant_properties"/> |
|
38 |
</div> |
|
39 |
|
|
40 |
|
|
41 |
<div id="multi_items_result"></div> |
|
42 |
|
|
43 |
[%- IF FORM.show_pos_input -%] |
|
44 |
[% 'At position' | $T8 %] |
|
45 |
[% L.input_tag('multi_items.position', '', id='multi_items_position', size=5, class="numeric") %] |
|
46 |
[%- END -%] |
|
47 |
<div class="buttons"> |
|
48 |
[% L.button_tag('', LxERP.t8('Continue'), id='continue_button') %] |
|
49 |
</div> |
|
50 |
|
|
51 |
</form> |
templates/design40_webpages/part/_multi_variants_parent_properties_table.html | ||
---|---|---|
1 |
[% USE T8 %] |
|
2 |
[% USE HTML %] |
|
3 |
[% USE L %] |
|
4 |
[% USE LxERP %] |
|
5 |
[% USE P %] |
|
6 |
|
|
7 |
<table id="variant_parent_properties"> |
|
8 |
<colgroup> |
|
9 |
<col class="wi-wide"> |
|
10 |
<col class="wi-wide"> |
|
11 |
</colgroup> |
|
12 |
<tbody> |
|
13 |
[% FOREACH property = PROPERTIES %] |
|
14 |
<tr> |
|
15 |
<th>[% property.displayable_name | html %]</th> |
|
16 |
<td>[% L.select_tag("multi_items.filter.has_variant_property_value_id[+]", |
|
17 |
property.property_values, |
|
18 |
value_key="id", title_key="displayable_name", |
|
19 |
with_empty=1, default='', |
|
20 |
onchange='$("#multi_items_filter_button").click();', |
|
21 |
class='wi-wide', |
|
22 |
) %]</td> |
|
23 |
</tr> |
|
24 |
[% END %] |
|
25 |
</tbody> |
|
26 |
</table> |
templates/design40_webpages/part/_multi_variants_result.html | ||
---|---|---|
1 |
[% USE T8 %] |
|
2 |
[% USE HTML %] |
|
3 |
[% USE L %] |
|
4 |
[% USE LxERP %] |
|
5 |
[% USE P %] |
|
6 |
|
|
7 |
<table id="multi_items" class="tbl-list"> |
|
8 |
<thead> |
|
9 |
<tr> |
|
10 |
<th>[% 'for all' | $T8 %]</th> |
|
11 |
<th>[% L.input_tag("multi_items.all_qty", '', size=5, class='numeric wi-verysmall') %]</th> |
|
12 |
<th colspan="4"></th> |
|
13 |
</tr> |
|
14 |
<tr> |
|
15 |
<th></th> |
|
16 |
<th>[% 'Qty' | $T8 %]</th> |
|
17 |
<th>[% 'Unit' | $T8 %]</th> |
|
18 |
<th>[% 'Article' | $T8 %]</th> |
|
19 |
<th>[% 'Sellprice' | $T8 %]</th> |
|
20 |
<th>[% 'Partsgroup' | $T8 %]</th> |
|
21 |
</tr> |
|
22 |
</thead> |
|
23 |
<tbody> |
|
24 |
[% FOREACH item = multi_variants %] |
|
25 |
<tr> |
|
26 |
<td></td> |
|
27 |
<td> |
|
28 |
[% L.hidden_tag("add_items[+].parts_id", item.id) %] |
|
29 |
[% L.input_tag("add_items[].qty_as_number", '', size=5, class = 'multi_items_qty numeric wi-verysmall') %] |
|
30 |
</td> |
|
31 |
<td>[% HTML.escape(item.unit) %]</td> |
|
32 |
<td>[% item.presenter.part(tabindex="-1") %] [% HTML.escape(item.description) %] [% HTML.escape(item.variant_values) %]</td> |
|
33 |
<td class="numeric">[% HTML.escape(item.sellprice_as_number) %]</td> |
|
34 |
<td class="numeric">[% HTML.escape(item.partsgroup.partsgroup) %]</td> |
|
35 |
</tr> |
|
36 |
[% END %] |
|
37 |
</tbody> |
|
38 |
</table> |
Auch abrufbar als: Unified diff
Varianten: PartPicker: mehrere Varianten eines Stammartikel hinzufügen